1import { Controller } from "@hotwired/stimulus" 2 3export default class extends Controller { 4 static values = { cooldown: Number } 5 static targets = ["submit"] 6 7 connect() { 8 if (this.hasSubmitTarget) { 9 this.submitTarget.disabled = true 10 this.cooldownTimer = setTimeout(() => { 11 this.submitTarget.disabled = false 12 }, this.cooldownValue || 1500) 13 } 14 this.boundEsc = this.onEsc.bind(this) 15 this.boundOutsideClick = this.onOutsideClick.bind(this) 16 this.boundSubmit = this.onSubmit.bind(this) 17 document.addEventListener("keydown", this.boundEsc) 18 this.element.addEventListener("click", this.boundOutsideClick) 19 this.element.querySelectorAll("form").forEach(f => f.addEventListener("submit", this.boundSubmit)) 20 } 21 22 async onSubmit(event) { 23 const form = event.target 24 if (!form.action.match(/send_now/)) return // only intercept the send form 25 event.preventDefault() 26 const formData = new FormData(form) 27 try { 28 const res = await fetch(form.action, { 29 method: form.method.toUpperCase(), 30 body: formData, 31 headers: { Accept: "text/html" }, 32 redirect: "follow" 33 }) 34 if (res.ok || res.redirected) { 35 // Force a full reload. fetch already followed the redirect to the 36 // topic page; setting href to the same path (only the hash changed) 37 // would otherwise just update the hash and leave the composer DOM in 38 // place. After reload the destroyed draft no longer renders and the 39 // new pending message appears in the timeline. 40 history.replaceState({}, "", res.url) 41 window.location.reload() 42 } else { 43 const body = await res.text() 44 alert(`Cannot send: ${body || res.statusText}`) 45 } 46 } catch (e) { 47 alert(`Cannot send: ${e.message}`) 48 } 49 } 50 51 disconnect() { 52 clearTimeout(this.cooldownTimer) 53 document.removeEventListener("keydown", this.boundEsc) 54 this.element.removeEventListener("click", this.boundOutsideClick) 55 this.element.querySelectorAll("form").forEach(f => f.removeEventListener("submit", this.boundSubmit)) 56 } 57 58 onEsc(event) { 59 if (event.key === "Escape") this.cancel(event) 60 } 61 62 onOutsideClick(event) { 63 if (event.target === this.element) this.cancel(event) 64 } 65 66 cancel(event) { 67 if (event) event.preventDefault() 68 const frame = this.element.closest("turbo-frame") 69 if (frame) frame.innerHTML = "" 70 } 71}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.