From 45a3c47608a088d5c942beb84a5b6517a88eea65 Mon Sep 17 00:00:00 2001
From: Letter N <24603524+LetterN@users.noreply.github.com>
Date: Wed, 8 Jul 2020 15:10:49 +0800
Subject: [PATCH] removes fun
---
code/controllers/subsystem/chat.dm | 9 +-
code/modules/goonchat/browserOutput.dm | 144 ++++--
.../browserassets/html/browserOutput.html | 8 +-
.../browserassets/js/browserOutput.js | 485 ++++++++++--------
4 files changed, 381 insertions(+), 265 deletions(-)
diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm
index 8d4de0c091..bbeb0683f0 100644
--- a/code/controllers/subsystem/chat.dm
+++ b/code/controllers/subsystem/chat.dm
@@ -4,6 +4,7 @@ SUBSYSTEM_DEF(chat)
wait = 1
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT
+
var/list/payload = list()
@@ -17,7 +18,7 @@ SUBSYSTEM_DEF(chat)
return
-/datum/controller/subsystem/chat/proc/queue(target, message, handle_whitespace = TRUE)
+/datum/controller/subsystem/chat/proc/queue(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = TRUE)
if(!target || !message)
return
@@ -35,8 +36,8 @@ SUBSYSTEM_DEF(chat)
if(handle_whitespace)
message = replacetext(message, "\n", "
")
message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]")
- message += "
"
-
+ if (trailing_newline)
+ message += "
"
//url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
//Do the double-encoding here to save nanoseconds
@@ -47,7 +48,7 @@ SUBSYSTEM_DEF(chat)
var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
if(!C)
- return
+ continue
//Send it to the old style output window.
SEND_TEXT(C, original_message)
diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm
index 6d9e141309..cd50408941 100644
--- a/code/modules/goonchat/browserOutput.dm
+++ b/code/modules/goonchat/browserOutput.dm
@@ -2,18 +2,30 @@
For the main html chat area
*********************************/
-//Precaching a bunch of shit
+/// Should match the value set in the browser js
+#define MAX_COOKIE_LENGTH 5
+
+//Precaching a bunch of shit. Someone ship this out of here
GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of icons for the browser output
-//On client, created on login
+//lazy renaming to chat_output, instead renamed to old chatOutput
+/**
+ * The chatOutput datum exists to handle the goonchat browser.
+ * On client, created on Client/New()
+ */
/datum/chatOutput
- var/client/owner //client ref
+ /// The client that owns us.
+ var/client/owner
+ /// How many times client data has been checked
var/total_checks = 0
- var/last_check = 0
- var/loaded = FALSE // Has the client loaded the browser output area?
- var/list/messageQueue //If they haven't loaded chat, this is where messages will go until they do
- var/cookieSent = FALSE // Has the client sent a cookie for analysis
- var/broken = FALSE
+ /// When to next clear the client data checks counter
+ var/next_time_to_clear = 0
+ /// Has the client loaded the browser output area?
+ var/loaded = FALSE
+ /// If they haven't loaded chat, this is where messages will go until they do
+ var/list/messageQueue
+ var/cookieSent = FALSE // Has the client sent a cookie for analysis
+ var/broken = FALSE
var/list/connectionHistory //Contains the connection history passed from chat cookie
var/adminMusicVolume = 25 //This is for the Play Global Sound verb
@@ -22,13 +34,18 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
messageQueue = list()
connectionHistory = list()
+/**
+ * start: Tries to load the chat browser
+ * Aborts if a problem is encountered.
+ * Async because this is called from Client/New.
+ */
/datum/chatOutput/proc/start()
+ set waitfor = FALSE
//Check for existing chat
if(!owner)
return FALSE
if(!winexists(owner, "browseroutput")) // Oh goddamnit.
- set waitfor = FALSE
broken = TRUE
message_admins("Couldn't start chat for [key_name_admin(owner)]!")
. = FALSE
@@ -43,6 +60,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
return TRUE
+/// Loads goonchat and sends assets.
/datum/chatOutput/proc/load()
set waitfor = FALSE
if(!owner)
@@ -53,6 +71,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
owner << browse(file('code/modules/goonchat/browserassets/html/browserOutput.html'), "window=browseroutput")
+/// Interprets input from the client. Will send data back if required.
/datum/chatOutput/Topic(href, list/href_list)
if(usr.client != owner)
return TRUE
@@ -83,20 +102,22 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
if("setMusicVolume")
data = setMusicVolume(arglist(params))
-
if("colorPresetPost") //User just swapped color presets in their goonchat preferences. Do we do anything else?
switch(href_list["preset"])
if("light")
owner.force_white_theme()
if("dark" || "normal")
owner.force_dark_theme()
-
+ // if("swaptodarkmode")
+ // swaptodarkmode()
+ // if("swaptolightmode")
+ // swaptolightmode()
if(data)
ehjax_send(data = data)
-//Called on chat output done-loading by JS.
+/// Called on chat output done-loading by JS.
/datum/chatOutput/proc/doneLoading()
if(loaded)
return
@@ -113,34 +134,75 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
messageQueue = null
sendClientData()
+ syncRegex()
+
//do not convert to to_chat()
SEND_TEXT(owner, "Failed to load fancy chat, reverting to old chat. Certain features won't work.")
+/// Hides the standard output and makes the browser visible.
/datum/chatOutput/proc/showChat()
winset(owner, "output", "is-visible=false")
winset(owner, "browseroutput", "is-disabled=false;is-visible=true")
+/// Calls syncRegex on all currently owned chatOutput datums
+/proc/syncChatRegexes()
+ for (var/user in GLOB.clients)
+ var/client/C = user
+ var/datum/chatOutput/Cchat = C.chatOutput
+ if (Cchat && !Cchat.broken && Cchat.loaded)
+ Cchat.syncRegex()
+
+/// Used to dynamically add regexes to the browser output. Currently only used by the IC filter.
+/datum/chatOutput/proc/syncRegex()
+ var/list/regexes = list()
+
+ if (config.ic_filter_regex)
+ regexes["show_filtered_ic_chat"] = list(
+ config.ic_filter_regex.name,
+ "ig",
+ "$1"
+ )
+
+ if (regexes.len)
+ ehjax_send(data = list("syncRegex" = regexes))
+
+/// Sends json encoded data to the browser.
/datum/chatOutput/proc/ehjax_send(client/C = owner, window = "browseroutput", data)
if(islist(data))
data = json_encode(data)
C << output("[data]", "[window]:ehjaxCallback")
-/datum/chatOutput/proc/sendMusic(music, pitch)
+/**
+ * Sends music data to the browser. If enabled by the browser, it will start playing.
+ * Arguments:
+ * music must be a https adress.
+ * extra_data is a list. The keys "pitch", "start" and "end" are used.
+ ** "pitch" determines the playback rate
+ ** "start" determines the start time of the sound
+ ** "end" determines when the musics stops playing
+ */
+/datum/chatOutput/proc/sendMusic(music, pitch, list/extra_data) //someone remove pitch
if(!findtext(music, GLOB.is_http_protocol))
return
var/list/music_data = list("adminMusic" = url_encode(url_encode(music)))
- if(pitch)
- music_data["musicRate"] = pitch
+
+ if(extra_data?.len)
+ music_data["musicRate"] = extra_data["pitch"] || pitch
+ music_data["musicSeek"] = extra_data["start"]
+ music_data["musicHalt"] = extra_data["end"]
+
ehjax_send(data = music_data)
+/// Stops music playing throw the browser.
/datum/chatOutput/proc/stopMusic()
ehjax_send(data = "stopMusic")
+/// Setter for adminMusicVolume. Sanitizes the value to between 0 and 100.
/datum/chatOutput/proc/setMusicVolume(volume = "")
if(volume)
adminMusicVolume = clamp(text2num(volume), 0, 100)
-//Sends client connection details to the chat to handle and save
+/// Sends client connection details to the chat to handle and save
/datum/chatOutput/proc/sendClientData()
//Get dem deets
var/list/deets = list("clientData" = list())
@@ -150,11 +212,11 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
var/data = json_encode(deets)
ehjax_send(data = data)
-//Called by client, sent data to investigate (cookie history so far)
+/// Called by client, sent data to investigate (cookie history so far)
/datum/chatOutput/proc/analyzeClientData(cookie = "")
//Spam check
- if(world.time > last_check + (3 SECONDS))
- last_check = world.time
+ if(world.time > next_time_to_clear)
+ next_time_to_clear = world.time + (3 SECONDS)
total_checks = 0
total_checks += 1
@@ -172,12 +234,13 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
if (connData && islist(connData) && connData.len > 0 && connData["connData"])
connectionHistory = connData["connData"] //lol fuck
var/list/found = new()
- if(connectionHistory.len > 5)
+
+ if(connectionHistory.len > MAX_COOKIE_LENGTH)
message_admins("[key_name(src.owner)] was kicked for an invalid ban cookie)")
qdel(owner)
return
- for(var/i in min(connectionHistory.len, 5) to 1 step -1)
+ for(var/i in connectionHistory.len to 1 step -1)
if(QDELETED(owner))
//he got cleaned up before we were done
return
@@ -191,45 +254,33 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
//Uh oh this fucker has a history of playing on a banned account!!
if (found.len > 0)
- //TODO: add a new evasion ban for the CURRENT client details, using the matched row details
message_admins("[key_name(src.owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])")
log_admin_private("[key_name(owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])")
cookieSent = TRUE
-//Called by js client every 60 seconds
+/// Called by js client every 60 seconds
/datum/chatOutput/proc/ping()
return "pong"
-//Called by js client on js error
+/// Called by js client on js error
/datum/chatOutput/proc/debug(error)
log_world("\[[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]\] Client: [(src.owner.key ? src.owner.key : src.owner)] triggered JS error: [error]")
-//Global chat procs
-/proc/to_chat_immediate(target, message, handle_whitespace=TRUE)
+/// Global chat proc. to_chat_immediate will circumvent SSchat and send data as soon as possible.
+/proc/to_chat_immediate(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE)
if(!target || !message)
return
- //Ok so I did my best but I accept that some calls to this will be for shit like sound and images
- //It stands that we PROBABLY don't want to output those to the browser output so just handle them here
- if (istype(target, /savefile))
- CRASH("Invalid message! [message]")
-
- if(!istext(message))
- if (istype(message, /image) || istype(message, /sound))
- CRASH("Invalid message! [message]")
- return
-
if(target == world)
target = GLOB.clients
var/original_message = message
- //Some macros remain in the string even after parsing and fuck up the eventual output
- message = replacetext(message, "\improper", "")
- message = replacetext(message, "\proper", "")
if(handle_whitespace)
message = replacetext(message, "\n", "
")
- message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]")
+ message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]") //EIGHT SPACES IN TOTAL!!
+ if(trailing_newline)
+ message += "
"
if(islist(target))
// Do the double-encoding outside the loop to save nanoseconds
@@ -272,14 +323,19 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
// url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
C << output(url_encode(url_encode(message)), "browseroutput:output")
-/proc/to_chat(target, message, handle_whitespace = TRUE)
+/// Sends a text message to the target.
+/proc/to_chat(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE)
if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized)
- to_chat_immediate(target, message, handle_whitespace)
+ to_chat_immediate(target, message, handle_whitespace, trailing_newline, confidential)
return
- SSchat.queue(target, message, handle_whitespace)
+ SSchat.queue(target, message, handle_whitespace, trailing_newline, confidential)
-/datum/chatOutput/proc/swaptolightmode() //Dark mode light mode stuff. Yell at KMC if this breaks! (See darkmode.dm for documentation)
+/// Dark mode light mode stuff. Yell at KMC if this breaks! (See darkmode.dm for documentation)
+/datum/chatOutput/proc/swaptolightmode()
owner.force_white_theme()
+/// Light mode stuff. (See darkmode.dm for documentation)
/datum/chatOutput/proc/swaptodarkmode()
owner.force_dark_theme()
+
+#undef MAX_COOKIE_LENGTH
diff --git a/code/modules/goonchat/browserassets/html/browserOutput.html b/code/modules/goonchat/browserassets/html/browserOutput.html
index 0acb127517..ce51cd8de8 100644
--- a/code/modules/goonchat/browserassets/html/browserOutput.html
+++ b/code/modules/goonchat/browserassets/html/browserOutput.html
@@ -38,10 +38,10 @@
-
Decrease font size -
-
Increase font size +
-
Decrease line height -
-
Increase line height +
+
Decrease font size
+
Increase font size
+
Decrease line height
+
Increase line height
Toggle ping display
Highlight string
Save chat log
diff --git a/code/modules/goonchat/browserassets/js/browserOutput.js b/code/modules/goonchat/browserassets/js/browserOutput.js
index 0d53b44ba8..ea8996bdfa 100644
--- a/code/modules/goonchat/browserassets/js/browserOutput.js
+++ b/code/modules/goonchat/browserassets/js/browserOutput.js
@@ -29,12 +29,13 @@ var opts = {
'scrollSnapTolerance': 10, //If within x pixels of bottom
'clickTolerance': 10, //Keep focus if outside x pixels of mousedown position on mouseup
'imageRetryDelay': 50, //how long between attempts to reload images (in ms)
- 'imageRetryLimit': 50, //how many attempts should we make?
+ 'imageRetryLimit': 50, //how many attempts should we make?
'popups': 0, //Amount of popups opened ever
'wasd': false, //Is the user in wasd mode?
'priorChatHeight': 0, //Thing for height-resizing detection
'restarting': false, //Is the round restarting?
'colorPreset': 0, // index in the color presets list.
+ //'darkmode':false, //Are we using darkmode? If not WHY ARE YOU LIVING IN 2009??? <- /tg/ take on darktheme
//Options menu
'selectedSubLoop': null, //Contains the interval loop for closing the selected sub menu
@@ -65,14 +66,17 @@ var opts = {
'volumeUpdateDelay': 5000, //Time from when the volume updates to data being sent to the server
'volumeUpdating': false, //True if volume update function set to fire
'updatedVolume': 0, //The volume level that is sent to the server
-
+ 'musicStartAt': 0, //The position the music starts playing
+ 'musicEndAt': 0, //The position the music... stops playing... if null, doesn't apply (so the music runs through)
+
'defaultMusicVolume': 25,
'messageCombining': true,
};
+var replaceRegexes = {};
-// Array of names for chat display color presets.
+// Array of names for chat display color presets. CIT SPECIFIC.
// If not set to normal, a CSS file `browserOutput_${name}.css` will be added to the head.
var colorPresets = [
'normal',
@@ -84,12 +88,6 @@ function clamp(val, min, max) {
return Math.max(min, Math.min(val, max))
}
-function outerHTML(el) {
- var wrap = document.createElement('div');
- wrap.appendChild(el.cloneNode(true));
- return wrap.innerHTML;
-}
-
//Polyfill for fucking date now because of course IE8 and below don't support it
if (!Date.now) {
Date.now = function now() {
@@ -103,6 +101,7 @@ if (typeof String.prototype.trim !== 'function') {
};
}
+// CIT SPECIFIC.
function updateColorPreset() {
var el = $("#colorPresetLink")[0];
el.href = "browserOutput_"+colorPresets[opts.colorPreset]+".css";
@@ -172,7 +171,7 @@ function byondDecode(message) {
// The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b.
// Marvelous.
message = message.replace(/\+/g, "%20");
- try {
+ try {
// This is a workaround for the above not always working when BYOND's shitty url encoding breaks. (byond bug id:2399401)
if (decodeURIComponent) {
message = decodeURIComponent(message);
@@ -185,57 +184,71 @@ function byondDecode(message) {
return message;
}
-//Actually turns the highlight term match into appropriate html
-function addHighlightMarkup(match) {
- var extra = '';
- if (opts.highlightColor) {
- extra += ' style="background-color: '+opts.highlightColor+'"';
+function replaceRegex() {
+ var selectedRegex = replaceRegexes[$(this).attr('replaceRegex')];
+ if (selectedRegex) {
+ var replacedText = $(this).html().replace(selectedRegex[0], selectedRegex[1]);
+ $(this).html(replacedText);
}
- return '
'+match+'';
+ $(this).removeAttr('replaceRegex');
}
-//Highlights words based on user settings
+// Get a highlight markup span
+function createHighlightMarkup() {
+ var extra = '';
+ if (opts.highlightColor) {
+ extra += ' style="background-color: ' + opts.highlightColor + '"';
+ }
+ return '
';
+}
+
+// Get all child text nodes that match a regex pattern
+function getTextNodes(elem, pattern) {
+ var result = $([]);
+ $(elem).contents().each(function(idx, child) {
+ if (child.nodeType === 3 && /\S/.test(child.nodeValue) && pattern.test(child.nodeValue)) {
+ result = result.add(child);
+ }
+ else {
+ result = result.add(getTextNodes(child, pattern));
+ }
+ });
+ return result;
+}
+
+// Highlight all text terms matching the registered regex patterns
function highlightTerms(el) {
- if (el.children.length > 0) {
- for(var h = 0; h < el.children.length; h++){
- highlightTerms(el.children[h]);
- }
- }
+ var pattern = new RegExp("(" + opts.highlightTerms.join('|') + ")", 'gi');
+ var nodes = getTextNodes(el, pattern);
- var hasTextNode = false;
- for (var node = 0; node < el.childNodes.length; node++)
- {
- if (el.childNodes[node].nodeType === 3)
- {
- hasTextNode = true;
- break;
- }
- }
-
- if (hasTextNode) { //If element actually has text
- var newText = '';
- for (var c = 0; c < el.childNodes.length; c++) { //Each child element
- if (el.childNodes[c].nodeType === 3) { //Is it text only?
- var words = el.childNodes[c].data.split(' ');
- for (var w = 0; w < words.length; w++) { //Each word in the text
- var newWord = null;
- for (var i = 0; i < opts.highlightTerms.length; i++) { //Each highlight term
- if (opts.highlightTerms[i] && words[w].toLowerCase().indexOf(opts.highlightTerms[i].toLowerCase()) > -1) { //If a match is found
- newWord = words[w].replace("<", "<").replace(new RegExp(opts.highlightTerms[i], 'gi'), addHighlightMarkup);
- break;
- }
- if (window.console)
- console.log(newWord)
- }
- newText += newWord || words[w].replace("<", "<");
- newText += w >= words.length - 1 ? '' : ' ';
- }
- } else { //Every other type of element
- newText += outerHTML(el.childNodes[c]);
+ nodes.each(function (idx, node) {
+ var content = $(node).text();
+ var parent = $(node).parent();
+ var pre = $(node.previousSibling);
+ $(node).remove();
+ content.split(pattern).forEach(function (chunk) {
+ // Get our highlighted span/text node
+ var toInsert = null;
+ if (pattern.test(chunk)) {
+ var tmpElem = $(createHighlightMarkup());
+ tmpElem.text(chunk);
+ toInsert = tmpElem;
}
- }
- el.innerHTML = newText;
- }
+ else {
+ toInsert = document.createTextNode(chunk);
+ }
+
+ // Insert back into our element
+ if (pre.length == 0) {
+ var result = parent.prepend(toInsert);
+ pre = $(result[0].firstChild);
+ }
+ else {
+ pre.after(toInsert);
+ pre = $(pre[0].nextSibling);
+ }
+ });
+ });
}
function iconError(E) {
@@ -268,41 +281,96 @@ function output(message, flag) {
message = byondDecode(message).trim();
- //Stuff we do along with appending a message
- var atBottom = false;
- var bodyHeight = $('body').height();
- var messagesHeight = $messages.outerHeight();
- var scrollPos = $('body,html').scrollTop();
-
- //Should we snap the output to the bottom?
- if (bodyHeight + scrollPos >= messagesHeight - opts.scrollSnapTolerance) {
- atBottom = true;
- if ($('#newMessages').length) {
- $('#newMessages').remove();
- }
- //If not, put the new messages box in
- } else {
- if ($('#newMessages').length) {
- var messages = $('#newMessages .number').text();
- messages = parseInt(messages);
- messages++;
- $('#newMessages .number').text(messages);
- if (messages == 2) {
- $('#newMessages .messageWord').append('s');
+ //The behemoth of filter-code (for Admin message filters)
+ //Note: This is proooobably hella inefficient
+ var filteredOut = false;
+ if (opts.hasOwnProperty('showMessagesFilters') && !opts.showMessagesFilters['All'].show) {
+ //Get this filter type (defined by class on message)
+ var messageHtml = $.parseHTML(message),
+ messageClasses;
+ if (opts.hasOwnProperty('filterHideAll') && opts.filterHideAll) {
+ var internal = false;
+ messageClasses = (!!$(messageHtml).attr('class') ? $(messageHtml).attr('class').split(/\s+/) : false);
+ if (messageClasses) {
+ for (var i = 0; i < messageClasses.length; i++) { //Every class
+ if (messageClasses[i] == 'internal') {
+ internal = true;
+ break;
+ }
+ }
+ }
+ if (!internal) {
+ filteredOut = 'All';
}
} else {
- $messages.after('
1 new message ');
+ //If the element or it's child have any classes
+ if (!!$(messageHtml).attr('class') || !!$(messageHtml).children().attr('class')) {
+ messageClasses = $(messageHtml).attr('class').split(/\s+/);
+ if (!!$(messageHtml).children().attr('class')) {
+ messageClasses = messageClasses.concat($(messageHtml).children().attr('class').split(/\s+/));
+ }
+ var tempCount = 0;
+ for (var i = 0; i < messageClasses.length; i++) { //Every class
+ var thisClass = messageClasses[i];
+ $.each(opts.showMessagesFilters, function(key, val) { //Every filter
+ if (key !== 'All' && val.show === false && typeof val.match != 'undefined') {
+ for (var i = 0; i < val.match.length; i++) {
+ var matchClass = val.match[i];
+ if (matchClass == thisClass) {
+ filteredOut = key;
+ break;
+ }
+ }
+ }
+ if (filteredOut) return false;
+ });
+ if (filteredOut) break;
+ tempCount++;
+ }
+ } else {
+ if (!opts.showMessagesFilters['Misc'].show) {
+ filteredOut = 'Misc';
+ }
+ }
}
}
+ //Stuff we do along with appending a message
+ var atBottom = false;
+ if (!filteredOut) {
+ var bodyHeight = $('body').height();
+ var messagesHeight = $messages.outerHeight();
+ var scrollPos = $('body,html').scrollTop();
+
+ //Should we snap the output to the bottom?
+ if (bodyHeight + scrollPos >= messagesHeight - opts.scrollSnapTolerance) {
+ atBottom = true;
+ if ($('#newMessages').length) {
+ $('#newMessages').remove();
+ }
+ //If not, put the new messages box in
+ } else {
+ if ($('#newMessages').length) {
+ var messages = $('#newMessages .number').text();
+ messages = parseInt(messages);
+ messages++;
+ $('#newMessages .number').text(messages);
+ if (messages == 2) {
+ $('#newMessages .messageWord').append('s');
+ }
+ } else {
+ $messages.after('
1 new message ');
+ }
+ }
+ }
opts.messageCount++;
//Pop the top message off if history limit reached
- //if (opts.messageCount >= opts.messageLimit) {
- //$messages.children('div.entry:first-child').remove();
- //opts.messageCount--; //I guess the count should only ever equal the limit
- //}
+ if (opts.messageCount >= opts.messageLimit) {
+ $messages.children('div.entry:first-child').remove();
+ opts.messageCount--; //I guess the count should only ever equal the limit
+ }
// Create the element - if combining is off, we use it, and if it's on, we
// might discard it bug need to check its text content. Some messages vary
@@ -323,6 +391,7 @@ function output(message, flag) {
badge = $('
', {'class': 'r', 'text': 2});
}
lastmessages.html(message);
+ lastmessages.find('[replaceRegex]').each(replaceRegex);
lastmessages.append(badge);
badge.animate({
"font-size": "0.9em"
@@ -340,6 +409,13 @@ function output(message, flag) {
//Actually append the message
entry.className = 'entry';
+ if (filteredOut) {
+ entry.className += ' hidden';
+ entry.setAttribute('data-filter', filteredOut);
+ }
+
+ $(entry).find('[replaceRegex]').each(replaceRegex);
+
$last_message = trimmed_message;
$messages[0].appendChild(entry);
$(entry).find("img.icon").error(iconError);
@@ -360,11 +436,11 @@ function output(message, flag) {
//Actually do the snap
//Stuff we can do after the message shows can go here, in the interests of responsiveness
if (opts.highlightTerms && opts.highlightTerms.length > 0) {
- highlightTerms(entry);
+ highlightTerms($(entry));
}
}
- if (atBottom) {
+ if (!filteredOut && atBottom) {
$('body,html').scrollTop($messages.outerHeight());
}
}
@@ -408,6 +484,20 @@ function toHex(n) {
return "0123456789ABCDEF".charAt((n-n%16)/16) + "0123456789ABCDEF".charAt(n%16);
}
+/*
+function swap() { //Swap to darkmode
+ if (opts.darkmode){
+ document.getElementById("sheetofstyles").href = "browserOutput_white.css";
+ opts.darkmode = false;
+ runByond('?_src_=chat&proc=swaptolightmode');
+ } else {
+ document.getElementById("sheetofstyles").href = "browserOutput.css";
+ opts.darkmode = true;
+ runByond('?_src_=chat&proc=swaptodarkmode');
+ }
+ setCookie('darkmode', (opts.darkmode ? 'true' : 'false'), 365);
+}
+*/
function handleClientData(ckey, ip, compid) {
//byond sends player info to here
var currentData = {'ckey': ckey, 'ip': ip, 'compid': compid};
@@ -488,6 +578,7 @@ function ehjaxCallback(data) {
} else if (data.adminMusic) {
if (typeof data.adminMusic === 'string') {
var adminMusic = byondDecode(data.adminMusic);
+ var bindLoadedData = false;
adminMusic = adminMusic.match(/https?:\/\/\S+/) || '';
if (data.musicRate) {
var newRate = Number(data.musicRate);
@@ -497,9 +588,32 @@ function ehjaxCallback(data) {
} else {
$('#adminMusic').prop('defaultPlaybackRate', 1.0);
}
+ if (data.musicSeek) {
+ opts.musicStartAt = Number(data.musicSeek) || 0;
+ bindLoadedData = true;
+ } else {
+ opts.musicStartAt = 0;
+ }
+ if (data.musicHalt) {
+ opts.musicEndAt = Number(data.musicHalt) || null;
+ bindLoadedData = true;
+ }
+ if (bindLoadedData) {
+ $('#adminMusic').one('loadeddata', adminMusicLoadedData);
+ }
$('#adminMusic').prop('src', adminMusic);
$('#adminMusic').trigger("play");
}
+ } else if (data.syncRegex) {
+ for (var i in data.syncRegex) {
+
+ var regexData = data.syncRegex[i];
+ var regexName = regexData[0];
+ var regexFlags = regexData[1];
+ var regexReplaced = regexData[2];
+
+ replaceRegexes[i] = [new RegExp(regexName, regexFlags), regexReplaced];
+ }
}
}
}
@@ -530,6 +644,27 @@ function sendVolumeUpdate() {
}
}
+function adminMusicEndCheck(event) {
+ if (opts.musicEndAt) {
+ if ($('#adminMusic').prop('currentTime') >= opts.musicEndAt) {
+ $('#adminMusic').off(event);
+ $('#adminMusic').trigger('pause');
+ $('#adminMusic').prop('src', '');
+ }
+ } else {
+ $('#adminMusic').off(event);
+ }
+}
+
+function adminMusicLoadedData(event) {
+ if (opts.musicStartAt && ($('#adminMusic').prop('duration') === Infinity || (opts.musicStartAt <= $('#adminMusic').prop('duration'))) ) {
+ $('#adminMusic').prop('currentTime', opts.musicStartAt);
+ }
+ if (opts.musicEndAt) {
+ $('#adminMusic').on('timeupdate', adminMusicEndCheck);
+ }
+}
+
function subSlideUp() {
$(this).removeClass('scroll');
$(this).css('height', '');
@@ -608,23 +743,32 @@ $(function() {
*
******************************************/
var savedConfig = {
- 'sfontSize': getCookie('fontsize'),
- 'slineHeight': getCookie('lineheight'),
+ fontsize: getCookie('fontsize'), //no need for compatabiliy, cookie name is the same
+ lineheight: getCookie('lineheight'),
'spingDisabled': getCookie('pingdisabled'),
'shighlightTerms': getCookie('highlightterms'),
'shighlightColor': getCookie('highlightcolor'),
'smusicVolume': getCookie('musicVolume'),
'smessagecombining': getCookie('messagecombining'),
+ 'sdarkmode': getCookie('darkmode'),
'scolorPreset': getCookie('colorpreset'),
};
- if (savedConfig.sfontSize) {
- $messages.css('font-size', savedConfig.sfontSize);
- internalOutput('
Loaded font size setting of: '+savedConfig.sfontSize+'', 'internal');
+ if (savedConfig.fontsize) {
+ $messages.css('font-size', savedConfig.fontsize);
+ internalOutput('
Loaded font size setting of: '+savedConfig.fontsize+'', 'internal');
}
- if (savedConfig.slineHeight) {
- $("body").css('line-height', savedConfig.slineHeight);
- internalOutput('
Loaded line height setting of: '+savedConfig.slineHeight+'', 'internal');
+ if (savedConfig.lineheight) {
+ $("body").css('line-height', savedConfig.lineheight);
+ internalOutput('
Loaded line height setting of: '+savedConfig.lineheight+'', 'internal');
+ }
+ // if(savedConfig.sdarkmode == 'true'){
+ // swap();
+ // }
+ if (savedConfig.scolorPreset) {
+ opts.colorPreset = Number(savedConfig.scolorPreset);
+ updateColorPreset();
+ internalOutput('
Loaded color preset of: '+colorPresets[opts.colorPreset]+'', 'internal');
}
if (savedConfig.spingDisabled) {
if (savedConfig.spingDisabled == 'true') {
@@ -634,15 +778,11 @@ $(function() {
internalOutput('
Loaded ping display of: '+(opts.pingDisabled ? 'hidden' : 'visible')+'', 'internal');
}
if (savedConfig.shighlightTerms) {
- var savedTerms = $.parseJSON(savedConfig.shighlightTerms);
- var actualTerms = '';
- for (var i = 0; i < savedTerms.length; i++) {
- if (savedTerms[i]) {
- actualTerms += savedTerms[i] + ', ';
- }
- }
+ var savedTerms = $.parseJSON(savedConfig.shighlightTerms).filter(function (entry) {
+ return entry !== null && /\S/.test(entry);
+ });
+ var actualTerms = savedTerms.length != 0 ? savedTerms.join(', ') : null;
if (actualTerms) {
- actualTerms = actualTerms.substring(0, actualTerms.length - 2);
internalOutput('
Loaded highlight strings of: ' + actualTerms+'', 'internal');
opts.highlightTerms = savedTerms;
}
@@ -651,13 +791,6 @@ $(function() {
opts.highlightColor = savedConfig.shighlightColor;
internalOutput('
Loaded highlight color of: '+savedConfig.shighlightColor+'', 'internal');
}
-
- if (savedConfig.scolorPreset) {
- opts.colorPreset = Number(savedConfig.scolorPreset);
- updateColorPreset();
- internalOutput('
Loaded color preset of: '+colorPresets[opts.colorPreset]+'', 'internal');
- }
-
if (savedConfig.smusicVolume) {
var newVolume = clamp(savedConfig.smusicVolume, 0, 100);
$('#adminMusic').prop('volume', newVolume / 100);
@@ -669,7 +802,7 @@ $(function() {
else{
$('#adminMusic').prop('volume', opts.defaultMusicVolume / 100);
}
-
+
if (savedConfig.smessagecombining) {
if (savedConfig.smessagecombining == 'false') {
opts.messageCombining = false;
@@ -746,76 +879,17 @@ $(function() {
href = escaper(href);
runByond('?action=openLink&link='+href);
}
+ runByond('byond://winset?mapwindow.map.focus=true');
});
- //Fuck everything about this event. Will look into alternatives.
$('body').on('keydown', function(e) {
if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') {
return;
}
-
if (e.ctrlKey || e.altKey || e.shiftKey) { //Band-aid "fix" for allowing ctrl+c copy paste etc. Needs a proper fix.
return;
}
-
- e.preventDefault()
-
- var k = e.which;
- // Hardcoded because else there would be no feedback message.
- if (k == 113) { // F2
- runByond('byond://winset?screenshot=auto');
- internalOutput('Screenshot taken', 'internal');
- }
-
- var c = "";
- switch (k) {
- case 8:
- c = 'BACK';
- case 9:
- c = 'TAB';
- case 13:
- c = 'ENTER';
- case 19:
- c = 'PAUSE';
- case 27:
- c = 'ESCAPE';
- case 33: // Page up
- c = 'NORTHEAST';
- case 34: // Page down
- c = 'SOUTHEAST';
- case 35: // End
- c = 'SOUTHWEST';
- case 36: // Home
- c = 'NORTHWEST';
- case 37:
- c = 'WEST';
- case 38:
- c = 'NORTH';
- case 39:
- c = 'EAST';
- case 40:
- c = 'SOUTH';
- case 45:
- c = 'INSERT';
- case 46:
- c = 'DELETE';
- case 93: // That weird thing to the right of alt gr.
- c = 'APPS';
-
- default:
- c = String.fromCharCode(k);
- }
-
- if (c.length == 0) {
- if (!e.shiftKey) {
- c = c.toLowerCase();
- }
- runByond('byond://winset?mapwindow.map.focus=true;mainwindow.input.text='+c);
- return false;
- } else {
- runByond('byond://winset?mapwindow.map.focus=true');
- return false;
- }
+ runByond('byond://winset?mapwindow.map.focus=true');
});
//Mildly hacky fix for scroll issues on mob change (interface gets resized sometimes, messing up snap-scroll)
@@ -843,6 +917,9 @@ $(function() {
$('#toggleOptions').click(function(e) {
handleToggleClick($subOptions, $(this));
});
+ // $('#darkmodetoggle').click(function(e) {
+ // swap();
+ // });
$('#toggleAudio').click(function(e) {
handleToggleClick($subAudio, $(this));
});
@@ -856,41 +933,31 @@ $(function() {
});
$('#decreaseFont').click(function(e) {
- var fontSize = parseInt($messages.css('font-size'));
- fontSize = fontSize - 1 + 'px';
- $messages.css({'font-size': fontSize});
- setCookie('fontsize', fontSize, 365);
- internalOutput('
Font size set to '+fontSize+'', 'internal');
+ savedConfig.fontsize = Math.max(parseInt(savedConfig.fontsize || 13) - 1, 1) + 'px';
+ $messages.css({'font-size': savedConfig.fontsize});
+ setCookie('fontsize', savedConfig.fontsize, 365);
+ internalOutput('
Font size set to '+savedConfig.fontsize+'', 'internal');
});
$('#increaseFont').click(function(e) {
- var fontSize = parseInt($messages.css('font-size'));
- fontSize = fontSize + 1 + 'px';
- $messages.css({'font-size': fontSize});
- setCookie('fontsize', fontSize, 365);
- internalOutput('
Font size set to '+fontSize+'', 'internal');
+ savedConfig.fontsize = (parseInt(savedConfig.fontsize || 13) + 1) + 'px';
+ $messages.css({'font-size': savedConfig.fontsize});
+ setCookie('fontsize', savedConfig.fontsize, 365);
+ internalOutput('
Font size set to '+savedConfig.fontsize+'', 'internal');
});
$('#decreaseLineHeight').click(function(e) {
- var Heightline = parseFloat($("body").css('line-height'));
- var Sizefont = parseFloat($("body").css('font-size'));
- var lineheightvar = Heightline / Sizefont
- lineheightvar -= 0.1;
- lineheightvar = lineheightvar.toFixed(1)
- $("body").css({'line-height': lineheightvar});
- setCookie('lineheight', lineheightvar, 365);
- internalOutput('
Line height set to '+lineheightvar+'', 'internal');
+ savedConfig.lineheight = Math.max(parseFloat(savedConfig.lineheight || 1.2) - 0.1, 0.1).toFixed(1);
+ $("body").css({'line-height': savedConfig.lineheight});
+ setCookie('lineheight', savedConfig.lineheight, 365);
+ internalOutput('
Line height set to '+savedConfig.lineheight+'', 'internal');
});
$('#increaseLineHeight').click(function(e) {
- var Heightline = parseFloat($("body").css('line-height'));
- var Sizefont = parseFloat($("body").css('font-size'));
- var lineheightvar = Heightline / Sizefont
- lineheightvar += 0.1;
- lineheightvar = lineheightvar.toFixed(1)
- $("body").css({'line-height': lineheightvar});
- setCookie('lineheight', lineheightvar, 365);
- internalOutput('
Line height set to '+lineheightvar+'', 'internal');
+ savedConfig.lineheight = (parseFloat(savedConfig.lineheight || 1.2) + 0.1).toFixed(1);
+ $("body").css({'line-height': savedConfig.lineheight});
+ setCookie('lineheight', savedConfig.lineheight, 365);
+ internalOutput('
Line height set to '+savedConfig.lineheight+'', 'internal');
});
$('#togglePing').click(function(e) {
@@ -908,13 +975,13 @@ $(function() {
// Requires IE 10+ to issue download commands. Just opening a popup
// window will cause Ctrl+S to save a blank page, ignoring innerHTML.
if (!window.Blob) {
- output('
This function is only supported on IE 10+. Upgrade if possible.', 'internal');
+ output('
This function is only supported on IE 10 and up. Upgrade if possible.', 'internal');
return;
}
$.ajax({
type: 'GET',
- url: 'browserOutput.css',
+ url: 'browserOutput_white.css',
success: function(styleData) {
var blob = new Blob(['
Chat Log', $messages.html(), '']);
@@ -958,20 +1025,12 @@ $(function() {
$('body').on('submit', '#highlightTermForm', function(e) {
e.preventDefault();
- var count = 0;
- while (count < opts.highlightLimit) {
+ opts.highlightTerms = [];
+ for (var count = 0; count < opts.highlightLimit; count++) {
var term = $('#highlightTermInput'+count).val();
- if (term) {
- term = term.trim();
- if (term === '') {
- opts.highlightTerms[count] = null;
- } else {
- opts.highlightTerms[count] = term.toLowerCase();
- }
- } else {
- opts.highlightTerms[count] = null;
+ if (term !== null && /\S/.test(term)) {
+ opts.highlightTerms.push(term.trim().toLowerCase());
}
- count++;
}
var color = $('#highlightColor').val();
@@ -992,8 +1051,8 @@ $(function() {
$messages.empty();
opts.messageCount = 0;
});
-
- $('#changeColorPreset').click(function() {
+
+ $('#changeColorPreset').click(function() { //CIT SPECIFIC
opts.colorPreset = (opts.colorPreset+1) % colorPresets.length;
updateColorPreset();
setCookie('colorpreset', opts.colorPreset, 365);
@@ -1026,9 +1085,9 @@ $(function() {
});
$('img.icon').error(iconError);
-
-
-
+
+
+
/*****************************************
*