User script: drag & drop from </> to [quote]...
-
As we all know, highlighting text to
<blockquote>
it works... kind of. As long as you don't care about any formatting, images, ... anything except the plain text and built-in emojis (which they actually got to quote correctly... shocking, I know). Quoting the whole post does work, but then you have to trim it down to the part you wanted to quote. Overall, this kind of sucks. Why can't "quote reply" just work?So, I started wondering if I could fix the "quote reply", but of course it's all Ember and who the hell knows how it does anything. Trying to set a breakpoint on clicking that just stopped somewhere in the middle of a huge-ass 32k-character-long line in the jQuery script, which Prettify Source, naturally, didn't help.
Then it occurred to me that if you could highlight text from the raw and quote that into the reply box, then it ought to work correctly. But the "quote reply" doesn't show up when you highlight text in the raw. Then I thought highlighting the raw text and drag-and-drop to the reply box would work, but Discourse manages to make it impossible to drag-and-drop anything, anywhere, anyway. So... I wrote a script that lets you do it.
Without further ado:
document.body.addEventListener("mousedown", function (e) { function dragStart(ev) { e.target.removeEventListener("dragstart", dragStart); for (var n = e.target.parentElement; !n.hasAttribute("data-post-id"); n = n.parentElement); // make sure the reply box is open var r = document.getElementById("reply-control") if (!r || r.classList.contains("closed")) { n.querySelector(".fa-reply").parentElement.click(); } document.getElementById("reply-control").addEventListener("drop", drop); // add BBcode [quote] tags to the quoted text, and set it as the data for the drag & drop event ev.dataTransfer.setData("text/bbcode", "[quote=\"" + [n.querySelector(".username a").getAttribute("data-user-card"), n.id.replace("_", ":"), "topic:" + document.getElementById("topic").getAttribute("data-topic-id")].join(", ") + (t.textContent == s.toString() ? ", full:true" : "") + "\"]\n" + s.toString() + "\n</blockquote>\n\n"); ev.dataTransfer.setData("text/plain", s.toString()); } function drop(ev) { if (ev.dataTransfer.types.contains("text/bbcode")) { var i = ev.target.selectionStart, t = ev.dataTransfer.getData("text/BBcode"); ev.target.value = ev.target.value.slice(0, i) + t + ev.target.value.slice(i); ev.target.selectionStart = ev.target.selectionEnd = i + t.length; ev.stopImmediatePropagation(); // stops this handler from running more than once ev.preventDefault(); // stops the default handler from inserting the event's text/plain data } // there's apparently a glitch where the textarea has focus, but is hiding it - blur and refocus fixes this ev.target.blur(); // focus() won't do anything unless the drop event's allowed to finish first, so use setTimeout to schedule it next setTimeout(() => ev.target.focus(), 0); } function dragEnd(ev) { ev.target.removeEventListener("dragend", dragEnd); document.getElementById("reply-control").removeEventListener("drop", drop); // if the dragged text wasn't dropped into the reply box (and the reply box contains nothing else), cancel the reply var r = document.getElementById("reply-control"); if (r && !(r.querySelector("textarea") || {}).value && r.querySelector(".cancel")) r.querySelector(".cancel").click(); } // these 3 if statements check that the selection contains one non-empty range which is contained entirely within a raw block var s = window.getSelection(), t = e.target; if (!e.ctrlKey && s.rangeCount == 1) { s = s.getRangeAt(0); // just in case the original target element wasn't the raw block itself, but an element within that block // Discourse wouldn't do this, but other userscripts *might* interfere... while (t.matches(".tdwtf-raw-area *")) t = t.parentElement; // if the source of the mouseDown event was a raw area which completely contains some highlighted text if (t.matches(".tdwtf-raw-area") && t.contains(s.startContainer) && t.contains(s.endContainer)) { if (s.startContainer != s.endContainer || s.startOffset != s.endOffset) { // don't let higher-up event handlers run - apparently something up there prevents default so you can't start dragging e.stopPropagation(); e.target.addEventListener("dragstart", dragStart); e.target.addEventListener("dragend", dragEnd); } } } });
To use:
- click the </> button for the post to which you'll reply
- highlight as much of the raw text as you wish to quote (some or all)
- start dragging it (if you don't have the reply box open, it'll open)
- drop it into the reply box
The correct BBcode
[quote]
tags will be added to format it as a reply. It will not be escaped, so it should Just Work, with the exception of -format stupidity where certain things get formatted totally differently because they're inside of a[quote]
.
-
The correct BBcode
<blockquote>
tags will be added t
Noice![quote="anotherusername, post:1, topic:54580"]
entListener("dragend", dragEnd);
@anotherusername said:To use:
- click the </> button for the post to which you'll reply
- highlight as much of the raw text as you wish to quote (some or all)
- start dragging it (if you don't have the reply box open, it'll open)
- drop it into the reply box
-
Okay so I realized that dragging text might not ultimately end up in the reply box, so I changed the data type to
text/bbcode
and changed thedrop
handler to insert that instead of thetext/plain
data. The event still transferstext/plain
data, so for example if you highlighted a URL to drag up to the browser's interface, it won't have the BBcode tags attached to it when you drop it there.That revealed a minor bug I had where the
drop
event handler wasn't being attached to the reply box in some cases, and another bug where thedrop
event executed multiple times (and thus inserted the[quote]
multiple times). So both of those had to be fixed, too, but it's working.
-
I made an alternative method as a bookmarklet:
javascript:$(document).on('mouseover', '.tdwtf-raw-area:not(.cooked)', function(e) { $(e.target).addClass('cooked'); });
To use: activate the bookmarklet once per Discourse session, then use highlight-quoting inside the raw text as you would in cooked.
[quote="anotherusername, post:1, topic:54580"]
As we all know, highlighting text to<blockquote>
it works... kind of.This could potentially break anything that relies on
tdwtf-raw-area
not beingcooked
but otherwise seems to work ok. It could also be done server-side by adding thecooked
class to raw (@PJH) which would make the bookmarklet redundant.: It's not quite perfect as it escapes <, >, etc.
-
Close, but not quite...
results in...
click the <tt></></tt> button
instead of...
click the </> button
I originally considered whether it'd be possible to just munge the selection in the cooked post to add all teh codez before Discourse grabbed the selection, but I ran into that same problem there...
<
and>
get escaped, so there's no way to convince it to quote HTML tags correctly.
-
Yeah, I noticed that after posting it. It's because the contents of the raw block are escaped and Discourse's native select-quoting copies it as-is. Although, curiously enough, & appears to be un-escaped.
escapes <, >, etc.
Of course none of this would be necessary if the original Discofeature did
munge the selection in the cooked post to add all teh codez
so we wouldn't need to mess with the raw at all.
-
Of course none of this would be necessary if the original Discofeature
...just worked correctly. Yes...