From c4dbe0a619af5fde63e5be3ade9b909abdf9e1d2 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 25 Jun 2018 19:44:34 -0700 Subject: [PATCH] Add DOM-based linkify to goonchat (no more URLs in IC) (#38693) * Add DOM-based linkify to goonchat (no more URLs in IC) * Add linkify spans to places where OOC messages appear This includes: - OOC - Deadchat - Admin chat - Admin logs - Admin PMs - Prayers * Limit fallback behavior to linkify spans --- code/__HELPERS/mobs.dm | 1 + code/modules/admin/admin.dm | 4 +- code/modules/admin/verbs/adminhelp.dm | 4 +- code/modules/admin/verbs/adminpm.dm | 14 ++-- code/modules/admin/verbs/adminsay.dm | 4 +- code/modules/admin/verbs/pray.dm | 2 +- code/modules/client/verbs/ooc.dm | 9 +-- .../browserassets/js/browserOutput.js | 66 +++++++++++++++++-- 8 files changed, 80 insertions(+), 24 deletions(-) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 97bff98a9ad..24b6233c598 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -429,6 +429,7 @@ Proc for attack log creation, because really why not step(X, pick(NORTH, SOUTH, EAST, WEST)) /proc/deadchat_broadcast(message, mob/follow_target=null, turf/turf_target=null, speaker_key=null, message_type=DEADCHAT_REGULAR) + message = "[message]" for(var/mob/M in GLOB.player_list) var/datum/preferences/prefs if(M.client && M.client.prefs) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 334528a1c25..56662a2157e 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -1,11 +1,11 @@ //////////////////////////////// /proc/message_admins(msg) - msg = "ADMIN LOG: [msg]" + msg = "ADMIN LOG: [msg]" to_chat(GLOB.admins, msg) /proc/relay_msg_admins(msg) - msg = "RELAY: [msg]" + msg = "RELAY: [msg]" to_chat(GLOB.admins, msg) diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 319a79a59da..d49cb921f66 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -255,7 +255,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) /datum/admin_help/proc/MessageNoRecipient(msg) var/ref_src = "[REF(src)]" //Message to be sent to all admins - var/admin_msg = "Ticket [TicketHref("#[id]", ref_src)]: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]: [keywords_lookup(msg)]" + var/admin_msg = "Ticket [TicketHref("#[id]", ref_src)]: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]: [keywords_lookup(msg)]" AddInteraction("[LinkedReplyName(ref_src)]: [msg]") @@ -267,7 +267,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) to_chat(X, admin_msg) //show it to the person adminhelping too - to_chat(initiator, "PM to-Admins: [msg]") + to_chat(initiator, "PM to-Admins: [msg]") //Reopen a closed ticket /datum/admin_help/proc/Reopen() diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 179595866ff..1848252d337 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -145,15 +145,15 @@ var/keywordparsedmsg = keywords_lookup(msg) if(irc) - to_chat(src, "PM to-Admins: [rawmsg]") + to_chat(src, "PM to-Admins: [rawmsg]") var/datum/admin_help/AH = admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to IRC: [keywordparsedmsg]") ircreplyamount-- send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg) else if(recipient.holder) if(holder) //both are admins - to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]") - to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]") + to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]") + to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]") //omg this is dumb, just fill in both their tickets var/interaction_message = "PM from-[key_name(src, recipient, 1)] to-[key_name(recipient, src, 1)]: [keywordparsedmsg]" @@ -162,10 +162,10 @@ admin_ticket_log(recipient, interaction_message) else //recipient is an admin but sender is not - var/replymsg = "Reply PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]" + var/replymsg = "Reply PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]" admin_ticket_log(src, replymsg) to_chat(recipient, replymsg) - to_chat(src, "PM to-Admins: [msg]") + to_chat(src, "PM to-Admins: [msg]") //play the recieving admin the adminhelp sound (if they have them enabled) if(recipient.prefs.toggles & SOUND_ADMINHELP) @@ -177,9 +177,9 @@ new /datum/admin_help(msg, recipient, TRUE) to_chat(recipient, "-- Administrator private message --") - to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]") + to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]") to_chat(recipient, "Click on the administrator's name to reply.") - to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]") + to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]") admin_ticket_log(recipient, "PM From [key_name_admin(src)]: [keywordparsedmsg]") diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 82bbc27abcf..bf392885115 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -12,10 +12,10 @@ log_talk(mob,"[key_name(src)] : [msg]",LOGASAY) msg = keywords_lookup(msg) if(check_rights(R_ADMIN,0)) - msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" + msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" to_chat(GLOB.admins, msg) else - msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" + msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" to_chat(GLOB.admins, msg) SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index aaed0f22942..e690d0a0ad3 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -39,7 +39,7 @@ font_color = "blue" prayer_type = "SPIRITUAL PRAYER" - msg = "[icon2html(cross, GLOB.admins)][prayer_type][deity ? " (to [deity])" : ""]: [ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]: [msg]" + msg = "[icon2html(cross, GLOB.admins)][prayer_type][deity ? " (to [deity])" : ""]: [ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]: [msg]" for(var/client/C in GLOB.admins) if(C.prefs.chat_toggles & CHAT_PRAYER) diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 9d708f6228d..2f4922fddf0 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -58,18 +58,19 @@ if(prefs.unlock_content) if(prefs.toggles & MEMBER_PUBLIC) keyname = "[icon2html('icons/member_content.dmi', world, "blag")][keyname]" + //The linkify span classes and linkify=TRUE below make ooc text get clickable chat href links if you pass in something resembling a url for(var/client/C in GLOB.clients) if(C.prefs.chat_toggles & CHAT_OOC) if(holder) if(!holder.fakekey || C.holder) if(check_rights_for(src, R_ADMIN)) - to_chat(C, "[CONFIG_GET(flag/allow_admin_ooccolor) && prefs.ooccolor ? "" :"" ]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]") + to_chat(C, "[CONFIG_GET(flag/allow_admin_ooccolor) && prefs.ooccolor ? "" :"" ]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]") else - to_chat(C, "OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]") + to_chat(C, "OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]") else - to_chat(C, "OOC: [holder.fakekey ? holder.fakekey : key]: [msg]") + to_chat(C, "OOC: [holder.fakekey ? holder.fakekey : key]: [msg]") else if(!(key in C.prefs.ignoring)) - to_chat(C, "OOC: [keyname]: [msg]") + to_chat(C, "OOC: [keyname]: [msg]") /proc/toggle_ooc(toggle = null) if(toggle != null) //if we're specifically en/disabling ooc diff --git a/code/modules/goonchat/browserassets/js/browserOutput.js b/code/modules/goonchat/browserassets/js/browserOutput.js index 045177c92d7..15265b2a93d 100644 --- a/code/modules/goonchat/browserassets/js/browserOutput.js +++ b/code/modules/goonchat/browserassets/js/browserOutput.js @@ -95,8 +95,53 @@ if (typeof String.prototype.trim !== 'function') { }; } +// Linkify the contents of a node, within its parent. +function linkify(parent, insertBefore, text) { + var start = 0; + var match; + var regex = /(?:(?:https?:\/\/)|(?:www\.))(?:[^ ]*?\.[^ ]*?)+[-A-Za-z0-9+&@#\/%?=~_|$!:,.;()]+/ig; + while ((match = regex.exec(text)) !== null) { + // add the unmatched text + parent.insertBefore(document.createTextNode(text.substring(start, match.index)), insertBefore); + + var href = match[0]; + if (!/^https?:\/\//i.test(match[0])) { + href = "http://" + match[0]; + } + + // add the link + var link = document.createElement("a"); + link.href = href; + link.textContent = match[0]; + parent.insertBefore(link, insertBefore); + + start = regex.lastIndex; + } + if (start !== 0) { + // add the remaining text and remove the original text node + parent.insertBefore(document.createTextNode(text.substring(start)), insertBefore); + parent.removeChild(insertBefore); + } +} + +// Recursively linkify the children of a given node. +function linkify_node(node) { + var children = node.childNodes; + // work backwards to avoid the risk of looping forever on our own output + for (var i = children.length - 1; i >= 0; --i) { + var child = children[i]; + if (child.nodeType == Node.TEXT_NODE) { + // text is to be linkified + linkify(node, child, child.textContent); + } else if (child.nodeName != "A" && child.nodeName != "a") { + // do not linkify existing links + linkify_node(child); + } + } +} + //Shit fucking piece of crap that doesn't work god fuckin damn it -function linkify(text) { +function linkify_fallback(text) { var rex = /((?: 0) {