11// ==UserScript==
22// @name FC2 Mahjong Soul Emote Name Replacer
3- // @namespace https://live.fc2.com/
4- // @version 0.2
3+ // @version 0.3
54// @description Replaces "charname-nn.png" text with the actual emote in the FC2 chatbox
65// @author Ling (and Anon)
76// @match https://live.fc2.com/*
87// @grant none
98// ==/UserScript==
109
1110// Use with your -monkey browser extension of choice
12- // This version (v0.2) fixes the bug where the chatbox doesn't automatically fully scroll down when a new message containing an emote is posted
11+ // This version (v0.3) adds optional colors to user >names for better readability
1312
1413( function ( ) {
1514 'use strict' ;
1615
1716 // Set emote size here. 64 works pretty good, 120 too.
18- const emoteSize = 64
17+ const EMOTE_SIZE = 64 ;
18+ // Colorize >names? Set to false to disable this feature.
19+ const COLORIZE_NAMES = true ;
1920 const chatboxSelector = '#js-commentListContainer' ;
2021 const chatboxDisplaySelector = '.chat_display'
2122 const messageSelector = '.js-commentText' ;
23+
2224 const messageContainerSelector = '.js-commentLine' ;
25+ const userNameSelector = '.js-commentUserName' ;
26+ const uidSelector = '.js-commentId' ;
2327 const imageRegex = / ( \w + ) - ( \d + ) \. p n g / i;
2428 const imageBaseUrl = 'https://files.riichi.moe/mjg/game%20resources%20and%20tools/Mahjong%20Soul/game%20files/emotes/' ;
2529
30+
31+ function colorizeUserName ( messageContainer ) {
32+ const userNameElement = messageContainer . querySelector ( userNameSelector ) ;
33+ const uidElement = messageContainer . querySelector ( uidSelector ) ;
34+
35+ if ( userNameElement && uidElement && ! userNameElement . dataset . nameColorized ) {
36+ const uid = uidElement . textContent . trim ( ) ;
37+ if ( uid ) {
38+ var color = '#' + uid . substring ( 0 , 6 ) ;
39+
40+ var r = parseInt ( color . slice ( 1 , 3 ) , 16 ) ;
41+ var g = parseInt ( color . slice ( 3 , 5 ) , 16 ) ;
42+ var b = parseInt ( color . slice ( 5 , 7 ) , 16 ) ;
43+
44+ // Ensure correct contrast against white bg and darken if it's too bright (https://www.w3.org/TR/AERT/#color-contrast)
45+ const brightness = ( ( r * 299 ) + ( g * 587 ) + ( b * 114 ) ) / 1000 ;
46+ if ( brightness > 220 ) {
47+ r = Math . floor ( r * 0.90 ) ;
48+ g = Math . floor ( g * 0.90 ) ;
49+ b = Math . floor ( b * 0.90 ) ;
50+ }
51+
52+ const finalColor = `#${ toHex ( r ) } ${ toHex ( g ) } ${ toHex ( b ) } ` ;
53+
54+ userNameElement . style . color = finalColor ;
55+ userNameElement . dataset . nameColorized = true ;
56+ }
57+ }
58+ }
59+
60+ function toHex ( c ) {
61+ return ( '0' + c . toString ( 16 ) ) . slice ( - 2 ) ;
62+ }
63+
2664 function replaceImagesInMessage ( messageElement ) {
2765 const messageText = messageElement . textContent ;
2866 const newMessageText = messageText . replace ( imageRegex , ( match , charname , number ) => {
2967 const imageUrl = `${ imageBaseUrl } ${ charname } -${ number } .png` ;
30- return `<img src="${ imageUrl } " alt="${ charname } -${ number } " width="${ emoteSize } " height="${ emoteSize } ">` ;
68+ return `<img src="${ imageUrl } " alt="${ charname } -${ number } " width="${ EMOTE_SIZE } " height="${ EMOTE_SIZE } ">` ;
3169 } ) ;
3270
3371 messageElement . innerHTML = newMessageText ;
4179 return ;
4280 }
4381
44- const messages = chatbox . querySelectorAll ( messageSelector ) ;
45- messages . forEach ( message => {
46- if ( ! message . dataset . hasImagesReplaced ) {
82+ const messageContainers = chatbox . querySelectorAll ( messageContainerSelector ) ;
83+ messageContainers . forEach ( container => {
84+ const message = container . querySelector ( messageSelector ) ;
85+ if ( message && ! message . dataset . hasImagesReplaced ) {
4786 replaceImagesInMessage ( message ) ;
4887 message . dataset . hasImagesReplaced = true ;
4988 }
89+ if ( COLORIZE_NAMES ) {
90+ colorizeUserName ( container ) ;
91+ }
5092 } ) ;
93+
5194 const chatboxDisplay = document . querySelector ( chatboxDisplaySelector ) ;
5295 chatboxDisplay . scrollTop = chatboxDisplay . scrollHeight ;
5396 }
64107 message . dataset . hasImagesReplaced = true ;
65108 }
66109 } ) ;
110+ if ( COLORIZE_NAMES ) {
111+ colorizeUserName ( node ) ;
112+ }
67113 }
68114 } ) ;
69115 }
72118 chatboxDisplay . scrollTop = chatboxDisplay . scrollHeight ;
73119 }
74120
121+
75122 function startObservingChatbox ( ) {
76123 const chatbox = document . querySelector ( chatboxSelector ) ;
77124 if ( ! chatbox ) {
89136 processChatbox ( ) ;
90137 startObservingChatbox ( ) ;
91138 } , 2500 ) ;
92- } ) ( ) ;
139+ } ) ( ) ;
0 commit comments