Emojis, Markdown and better file uploading
This commit is contained in:
		| @@ -110,7 +110,7 @@ | |||||||
|                         </button> |                         </button> | ||||||
|                     </div> |                     </div> | ||||||
|                     <div class="message-input-wrapper"> |                     <div class="message-input-wrapper"> | ||||||
|                         <input type="text" id="messageInput" placeholder="Send a message..." autofocus> |                         <textarea style="font-family: sans-serif" id="messageInput" placeholder="Send a message..." autofocus></textarea> | ||||||
|                         <button class="send-button" onclick="sendMessage()"> |                         <button class="send-button" onclick="sendMessage()"> | ||||||
|                             <i class="fa-solid fa-paper-plane"></i> |                             <i class="fa-solid fa-paper-plane"></i> | ||||||
|                         </button> |                         </button> | ||||||
|   | |||||||
| @@ -24,10 +24,17 @@ document.addEventListener('DOMContentLoaded', function() { | |||||||
|             chatHeader.insertBefore(menuToggle, chatHeader.firstChild); |             chatHeader.insertBefore(menuToggle, chatHeader.firstChild); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     document.getElementById('messageInput').addEventListener('input', function () { | ||||||
|  |         this.style.height = 'auto'; | ||||||
|  |         this.style.height = (this.scrollHeight) + 'px'; | ||||||
|  |     }); | ||||||
|  |  | ||||||
|      |      | ||||||
|     // Add event listeners |     // Add event listeners | ||||||
|     document.getElementById('messageInput').addEventListener('keypress', (event) => { |     document.getElementById('messageInput').addEventListener('keydown', function(event) { | ||||||
|         if (event.key === 'Enter') { |         if (event.key === 'Enter' && !event.shiftKey) { | ||||||
|  |             event.preventDefault(); | ||||||
|             sendMessage(); |             sendMessage(); | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| @@ -264,6 +271,218 @@ function resetLoginButton() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function markdownToHtmlDiv(markdown) { | ||||||
|  |   // Emoji mapping | ||||||
|  |   const emojiMap = { | ||||||
|  |       // 😀 Faces | ||||||
|  |       smile: '😄', | ||||||
|  |       happy: '😊', | ||||||
|  |       grin: '😁', | ||||||
|  |       laugh: '😂', | ||||||
|  |       joy: '🤣', | ||||||
|  |       wink: '😉', | ||||||
|  |       blush: '☺️', | ||||||
|  |       cool: '😎', | ||||||
|  |       smirk: '😏', | ||||||
|  |       thinking: '🤔', | ||||||
|  |       neutral: '😐', | ||||||
|  |       expressionless: '😑', | ||||||
|  |       sleepy: '😴', | ||||||
|  |       dizzy: '😵', | ||||||
|  |       surprised: '😲', | ||||||
|  |       scream: '😱', | ||||||
|  |       cry: '😢', | ||||||
|  |       sob: '😭', | ||||||
|  |       angry: '😠', | ||||||
|  |       rage: '😡', | ||||||
|  |       yum: '😋', | ||||||
|  |       relieved: '😌', | ||||||
|  |       confused: '😕', | ||||||
|  |       nerd: '🤓', | ||||||
|  |       zany: '🤪', | ||||||
|  |       shush: '🤫', | ||||||
|  |       hush: '🤐', | ||||||
|  |  | ||||||
|  |       // ❤️ Emotions | ||||||
|  |       heart: '❤️', | ||||||
|  |       heartbroken: '💔', | ||||||
|  |       love: '😍', | ||||||
|  |       kiss: '😘', | ||||||
|  |       hug: '🤗', | ||||||
|  |       clap: '👏', | ||||||
|  |       pray: '🙏', | ||||||
|  |       ok: '👌', | ||||||
|  |       fingerscrossed: '🤞', | ||||||
|  |       thumbsup: '👍', | ||||||
|  |       thumbsdown: '👎', | ||||||
|  |       eyes: '👀', | ||||||
|  |  | ||||||
|  |       // 🔥 Reactions | ||||||
|  |       fire: '🔥', | ||||||
|  |       100: '💯', | ||||||
|  |       poop: '💩', | ||||||
|  |       poo: '💩', | ||||||
|  |       skull: '💀', | ||||||
|  |       explosion: '💥', | ||||||
|  |       mindblown: '🤯', | ||||||
|  |       party: '🥳', | ||||||
|  |       tada: '🎉', | ||||||
|  |       balloon: '🎈', | ||||||
|  |  | ||||||
|  |       // 🐶 Animals | ||||||
|  |       chicken: '🐔', | ||||||
|  |       dog: '🐶', | ||||||
|  |       cat: '🐱', | ||||||
|  |       fox: '🦊', | ||||||
|  |       panda: '🐼', | ||||||
|  |       pig: '🐷', | ||||||
|  |       cow: '🐮', | ||||||
|  |       rabbit: '🐰', | ||||||
|  |       bear: '🐻', | ||||||
|  |       unicorn: '🦄', | ||||||
|  |       monkey: '🐒', | ||||||
|  |       dragon: '🐉', | ||||||
|  |       snake: '🐍', | ||||||
|  |  | ||||||
|  |       // 🍔 Food & Drink | ||||||
|  |       pizza: '🍕', | ||||||
|  |       burger: '🍔', | ||||||
|  |       fries: '🍟', | ||||||
|  |       hotdog: '🌭', | ||||||
|  |       taco: '🌮', | ||||||
|  |       ramen: '🍜', | ||||||
|  |       sushi: '🍣', | ||||||
|  |       icecream: '🍨', | ||||||
|  |       cake: '🎂', | ||||||
|  |       coffee: '☕', | ||||||
|  |       tea: '🍵', | ||||||
|  |       beer: '🍺', | ||||||
|  |  | ||||||
|  |       // 🌍 Nature & Weather | ||||||
|  |       sun: '☀️', | ||||||
|  |       moon: '🌙', | ||||||
|  |       star: '⭐', | ||||||
|  |       cloud: '☁️', | ||||||
|  |       rain: '🌧️', | ||||||
|  |       snow: '❄️', | ||||||
|  |       lightning: '⚡', | ||||||
|  |       rainbow: '🌈', | ||||||
|  |       tree: '🌳', | ||||||
|  |       flower: '🌸', | ||||||
|  |  | ||||||
|  |       // ⚽ Activities | ||||||
|  |       soccer: '⚽', | ||||||
|  |       basketball: '🏀', | ||||||
|  |       football: '🏈', | ||||||
|  |       baseball: '⚾', | ||||||
|  |       tennis: '🎾', | ||||||
|  |       bowling: '🎳', | ||||||
|  |       video_game: '🎮', | ||||||
|  |       chess: '♟️', | ||||||
|  |       music: '🎵', | ||||||
|  |       guitar: '🎸', | ||||||
|  |       mic: '🎤', | ||||||
|  |  | ||||||
|  |       // 💡 Objects & Symbols | ||||||
|  |       lightbulb: '💡', | ||||||
|  |       phone: '📱', | ||||||
|  |       laptop: '💻', | ||||||
|  |       bomb: '💣', | ||||||
|  |       money: '💰', | ||||||
|  |       star2: '🌟', | ||||||
|  |       warning: '⚠️', | ||||||
|  |       check: '✅', | ||||||
|  |       x: '❌', | ||||||
|  |       question: '❓', | ||||||
|  |       exclamation: '❗', | ||||||
|  |       infinity: '♾️', | ||||||
|  |       hourglass: '⏳', | ||||||
|  |       clock: '🕒', | ||||||
|  |  | ||||||
|  |       pepe: '🐸', | ||||||
|  |       troll: '😈', | ||||||
|  |       sus: '🧐', | ||||||
|  |       amongus: '🟥', | ||||||
|  |       monke: '🐵', | ||||||
|  |       chad: '🦍', | ||||||
|  |       gigachad: '💪', | ||||||
|  |       wojak: '😔', | ||||||
|  |       feelsbadman: '😞', | ||||||
|  |       feelsgoodman: '😌', | ||||||
|  |       yikes: '😬', | ||||||
|  |       clown: '🤡', | ||||||
|  |       clownworld: '🌎🤡', | ||||||
|  |       triggered: '😡', | ||||||
|  |       pog: '😲', | ||||||
|  |       kek: '🤣', | ||||||
|  |       based: '😎', | ||||||
|  |       cringe: '😖', | ||||||
|  |       dab: '🕺', | ||||||
|  |       sigma: '🧠', | ||||||
|  |       npc: '🤖', | ||||||
|  |       doomer: '🌑', | ||||||
|  |       zoomer: '⚡', | ||||||
|  |       boomer: '👴', | ||||||
|  |       sheesh: '😤', | ||||||
|  |       rickroll: '🕺🎶', | ||||||
|  |       trolled: '🧌', | ||||||
|  |       rekt: '💀', | ||||||
|  |       skillissue: '📉', | ||||||
|  |       bro: '🙄', | ||||||
|  |       cope: '😢', | ||||||
|  |       ratio: '➗', | ||||||
|  |       amogus: '👁️👄👁️', | ||||||
|  |  | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |   // Escape HTML first | ||||||
|  |   let html = markdown | ||||||
|  |     .replace(/&/g, '&') | ||||||
|  |     .replace(/</g, '<') | ||||||
|  |     .replace(/>/g, '>'); | ||||||
|  |  | ||||||
|  |   // Code blocks (```...```) | ||||||
|  |   html = html.replace(/```([^```]*)```/gs, (match, p1) => | ||||||
|  |     `<pre><code>${p1.trim()}</code></pre>` | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   // Inline code (`code`) | ||||||
|  |   html = html.replace(/`([^`\n]+)`/g, (match, p1) => `<code>${p1}</code>`); | ||||||
|  |  | ||||||
|  |   // Emojis :emoji_name: | ||||||
|  |   html = html.replace(/:([a-z0-9_]+):/gi, (match, p1) => emojiMap[p1] || match); | ||||||
|  |  | ||||||
|  |   // Process overlapping formatting in order: Bold+Underline+Italic | ||||||
|  |   html = parseFormatting(html); | ||||||
|  |  | ||||||
|  |   // Replace line breaks | ||||||
|  |   html = html.replace(/\n/g, '<br>'); | ||||||
|  |  | ||||||
|  |   return html; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Parse overlapping formatting using tokens | ||||||
|  | function parseFormatting(text) { | ||||||
|  |   // Define replacement tokens and their HTML | ||||||
|  |   const formattingRules = [ | ||||||
|  |     { regex: /\*\*\*(.*?)\*\*\*/g, html: '<strong><em>$1</em></strong>' }, // ***bold italic*** | ||||||
|  |     { regex: /___(.*?)___/g, html: '<u><em>$1</em></u>' },                 // ___underline italic___ | ||||||
|  |     { regex: /\*\*(.*?)\*\*/g, html: '<strong>$1</strong>' },             // **bold** | ||||||
|  |     { regex: /__(.*?)__/g, html: '<u>$1</u>' },                           // __underline__ | ||||||
|  |     { regex: /\*(.*?)\*/g, html: '<em>$1</em>' },                         // *italic* | ||||||
|  |     { regex: /_(.*?)_/g, html: '<em>$1</em>' },                           // _italic_ | ||||||
|  |     { regex: /~~(.*?)~~/g, html: '<s>$1</s>' }                            // ~~strike~~ | ||||||
|  |   ]; | ||||||
|  |  | ||||||
|  |   // Apply formatting in order | ||||||
|  |   for (const rule of formattingRules) { | ||||||
|  |     text = text.replace(rule.regex, rule.html); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return text; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| // Handle incoming messages | // Handle incoming messages | ||||||
| function handleMessage(event) { | function handleMessage(event) { | ||||||
|     if (event.data === "ping") { |     if (event.data === "ping") { | ||||||
| @@ -618,7 +837,7 @@ function addChatMessage(author, content, isHistory = false) { | |||||||
|     // Add message text |     // Add message text | ||||||
|     const textDiv = document.createElement('div'); |     const textDiv = document.createElement('div'); | ||||||
|     textDiv.className = 'message-text'; |     textDiv.className = 'message-text'; | ||||||
|     textDiv.textContent = content; |     textDiv.innerHTML = markdownToHtmlDiv(content); | ||||||
|     contentDiv.appendChild(textDiv); |     contentDiv.appendChild(textDiv); | ||||||
|      |      | ||||||
|     messageWrapper.appendChild(avatar); |     messageWrapper.appendChild(avatar); | ||||||
| @@ -797,6 +1016,8 @@ async function uploadFile() { | |||||||
|     const formData = new FormData(); |     const formData = new FormData(); | ||||||
|     formData.append("file", fileInput.files[0]); |     formData.append("file", fileInput.files[0]); | ||||||
|     formData.append("room", currentRoom);  |     formData.append("room", currentRoom);  | ||||||
|  |     formData.append("username", username); | ||||||
|  |     formData.append("token", md5(password)); | ||||||
|      |      | ||||||
|     try { |     try { | ||||||
|         const response = await fetch(getUploadUrl(), { |         const response = await fetch(getUploadUrl(), { | ||||||
| @@ -807,17 +1028,6 @@ async function uploadFile() { | |||||||
|          |          | ||||||
|         if (response.ok) { |         if (response.ok) { | ||||||
|             hideFileUpload(); |             hideFileUpload(); | ||||||
|              |  | ||||||
|             const processedMessage = { |  | ||||||
|                 "type": "message", |  | ||||||
|                 "username": username, |  | ||||||
|                 "token": md5(password), |  | ||||||
|                 "room": currentRoom, |  | ||||||
|                 "content": `Sent a file` |  | ||||||
|             } |  | ||||||
|             if (ws && ws.readyState === WebSocket.OPEN) { |  | ||||||
|                 ws.send(JSON.stringify(processedMessage)); |  | ||||||
|             } |  | ||||||
|         } else { |         } else { | ||||||
|             alert("Failed to upload file. Please try again."); |             alert("Failed to upload file. Please try again."); | ||||||
|         } |         } | ||||||
| @@ -1143,4 +1353,4 @@ if (uploadField) { | |||||||
|             this.value = ""; |             this.value = ""; | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -798,6 +798,20 @@ fun main(args: Array<String>) { | |||||||
|             Files.copy(uploadedFile.content(), filePath) |             Files.copy(uploadedFile.content(), filePath) | ||||||
|              |              | ||||||
|             val room = if (ctx.formParam("room") != null) ctx.formParam("room") else "general" |             val room = if (ctx.formParam("room") != null) ctx.formParam("room") else "general" | ||||||
|  |             var username: String = "" | ||||||
|  |             var token: String = "" | ||||||
|  |  | ||||||
|  |             if (ctx.formParam("username") != null) { | ||||||
|  |                 username = ctx.formParam("username").toString() | ||||||
|  |             } else { | ||||||
|  |                 // do error | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (ctx.formParam("token") != null) { | ||||||
|  |                 token = ctx.formParam("token").toString() | ||||||
|  |             } else { | ||||||
|  |                 // do error | ||||||
|  |             } | ||||||
|              |              | ||||||
|             val processedData = JSONObject().apply { |             val processedData = JSONObject().apply { | ||||||
|                 put("type", "fileStatus") |                 put("type", "fileStatus") | ||||||
| @@ -808,7 +822,7 @@ fun main(args: Array<String>) { | |||||||
|              |              | ||||||
|             val processedData2 = JSONObject().apply { |             val processedData2 = JSONObject().apply { | ||||||
|                 put("type", "file") |                 put("type", "file") | ||||||
|                 put("username", "system") |                 put("username", username) | ||||||
|                 put("room", room) |                 put("room", room) | ||||||
|                 put("content", "https://maxwellj.xyz/chookchat/uploads/$newFilename") |                 put("content", "https://maxwellj.xyz/chookchat/uploads/$newFilename") | ||||||
|             } |             } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user