Greasy Fork 还支持 简体中文。

CGChat

The CodinGame web-chat without CodinGame.

  1. // ==UserScript==
  2. // @name CGChat
  3. // @namespace BlaiseEbuth
  4. // @match https://www.codingame.com/*
  5. // @grant none
  6. // @version 1.3.0
  7. // @author BlaiseEbuth
  8. // @description The CodinGame web-chat without CodinGame.
  9. // @icon https://i.imgur.com/E39sADi.png
  10. // ==/UserScript==
  11.  
  12. /*
  13. CGChat is an externalization as a standalone window of the CodinGame's web chat.
  14. Copyright (C) 2020 BlaiseEbuth
  15. This program is free software: you can redistribute it and/or modify
  16. it under the terms of the GNU General Public License as published by
  17. the Free Software Foundation, either version 3 of the License, or
  18. (at your option) any later version.
  19. This program is distributed in the hope that it will be useful,
  20. but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. GNU General Public License for more details.
  23. You should have received a copy of the GNU General Public License
  24. along with this program. If not, see <https://www.gnu.org/licenses/>.
  25. */
  26.  
  27. 'use strict'; // Be strict !
  28.  
  29. // A map to store the clicked links.
  30. var openedLinks = {};
  31.  
  32. // A function that set "CGChat" as window's title.
  33. function setTitle()
  34. {
  35. // The window's title is defined by the page's <title> tag.
  36. var title = document.getElementsByTagName("title");
  37.  
  38. if(title.length == 1) // If the page is loaded.
  39. {
  40. // We change the title.
  41. title[0].innerHTML = "CGChat";
  42. }
  43. }
  44.  
  45. // A function that clean the connexion page.
  46. function signin()
  47. {
  48. // Select all elements that are not related to the connexion process:
  49. var nav = document.getElementById("navigation"); // The navigation bar.
  50. var cookies = document.getElementsByClassName("cg-cookies-banner"); // The cookies notification.
  51. var footer = document.getElementsByClassName("cg-login-form_footer"); // The login form footer that allow to create a new account.
  52. // Hide all of this uselles stuff.
  53. if(nav != null)nav.style.display = "none";
  54. if(cookies.length == 1)cookies[0].style.display = "none";
  55. if(footer.length == 1)footer[0].style.display = "none";
  56. }
  57.  
  58. // A function that adapt the height of the textarea depending on its content.
  59. function adjustMessageBox()
  60. {
  61. // We get the textarea.
  62. var messBox = document.getElementById("chat-input");
  63. // We compute the height difference between the textarea and its content.
  64. var heightDiff = messBox.scrollHeight - messBox.clientHeight;
  65. // If there is a difference:
  66. if(heightDiff > 0)
  67. {
  68. // We get the CSS height value of the textarea.
  69. var messBoxStyle = window.getComputedStyle(messBox);
  70. var messBoxHeight = messBoxStyle.getPropertyValue("height");
  71. var intHeight = parseInt(messBoxHeight.substr(0, messBoxHeight.length - 2));
  72. // If this height is lesser than 150px (the max height we allow):
  73. if(intHeight < 150)
  74. {
  75. // We get the absolute bottom position of the messages zone.
  76. var messWrap = document.getElementsByClassName("messages-wrap")[0];
  77. var messWrapStyle = window.getComputedStyle(messWrap);
  78. var messWrapBot = messWrapStyle.getPropertyValue("bottom");
  79.  
  80. // We compute the new height of the textarea and the one of the chat footer which contain it.
  81. var newHeight = intHeight + heightDiff + 10;
  82. var newBottom = parseInt(messWrapBot.substr(0, messWrapBot.length - 2)) + heightDiff + 10;
  83.  
  84. // We adjust the size and the position of the elements.
  85. messWrap.style.bottom = newBottom.toString() + "px";
  86. document.getElementsByClassName("footer")[0].style.height = newBottom.toString() + "px";
  87. document.getElementsByClassName("send-form")[0].style.height = newHeight.toString() + "px";
  88. messBox.style.height = newHeight.toString() + "px";
  89.  
  90. // We get the messages container.
  91. var messView = document.getElementById("messages");
  92.  
  93. // And we set the scroll bar to the end.
  94. messView.scrollTop = messView.scrollHeight;
  95. }
  96. }
  97. // If the textarea is empty:
  98. if(messBox.value == "")
  99. {
  100. // We reset all the values to the defaults.
  101. document.getElementsByClassName("messages-wrap")[0].style.bottom = "125px";
  102. document.getElementsByClassName("footer")[0].style.height = "125px";
  103. document.getElementsByClassName("send-form")[0].style.height = "45px";
  104. messBox.style.height = "45px";
  105. }
  106. }
  107.  
  108. // The function that manage all the chat-related actions.
  109. function chat()
  110. {
  111. // If we are in the application and jquerry is not loaded yet.
  112. if(window.isNwjs && document.getElementById("jquery") == null)
  113. {
  114. // We get the page's <head> element.
  115. var head = document.getElementsByTagName("head");
  116.  
  117. if(head.length == 1) // If head is defined.
  118. {
  119. // We create a <script> element.
  120. var jquery = document.createElement("script");
  121. // With the "jquerry" id.
  122. jquery.id = "jquery";
  123. jquery.type = "text/javascript";
  124. jquery.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js";
  125. // And we add it to the page's <head>.
  126. head[0].appendChild(jquery);
  127. }
  128. }
  129.  
  130. // We select the navigation bar and the site's content.
  131. var leftCol = document.getElementsByClassName("left-column");
  132. var nav = document.getElementById("navigation");
  133.  
  134. // If they exist we delete them.
  135. if(leftCol.length == 1)leftCol[0].style.display = "none";
  136. if(nav != null)nav.style.display = "none";
  137. // We select the chat's containers.
  138. var wrapper = document.getElementsByClassName("chat-wrapper");
  139. var chat = document.getElementById("chat");
  140.  
  141. // If they exist, we make them occupy the full window.
  142. if(wrapper.length == 1)wrapper[0].style.cssText = "width:100% !important";
  143. if(chat != null)chat.style.cssText = "display:flex !important";
  144. // We check if the chat is minimized.
  145. var small = document.getElementsByClassName("small-chat");
  146.  
  147. // If yes, we maximize it.
  148. if(small.length == 1)small[0].click();
  149.  
  150. // We select the chat itself.
  151. var chatClass = document.getElementsByClassName("chat");
  152. // And some of its elements which need to be removed:
  153. var discord = document.getElementsByTagName("cg-discord-disclaimer"); // The discord disclaimer.
  154. var hideWrapper = document.getElementsByClassName("hide-wrapper"); // The "Hide/Show" button.
  155. var help = document.getElementsByClassName("help-center"); // The help center.
  156.  
  157. // We make the chat occupy 100% of its container.
  158. if(chatClass.length == 1)chatClass[0].style.width = "100%";
  159. // And we delete the useless stuff.
  160. if(discord.length == 1)discord[0].style.display = "none";
  161. if(hideWrapper.length == 1)hideWrapper[0].style.display = "none";
  162. if(help.length == 1)help[0].style.display = "none";
  163.  
  164. // We get the settings popup.
  165. var settings = document.getElementsByClassName("settings-popup");
  166.  
  167. if(settings.length == 1) // If it exist.
  168. {
  169. for(var i = 0; i < 3; i++)
  170. {
  171. // We delete the 3 first elements to only keep the theme settings.
  172. settings[0].children[i].style.display = "none";
  173. }
  174. }
  175. // If the refresh button does not exist yet.
  176. if(document.getElementById("refreshButton") == null)
  177. {
  178. // We create it.
  179. var refreshButton = document.createElement("span");
  180. refreshButton.id = "refreshButton";
  181. refreshButton.style.cssText =
  182. "display : inline-block;\
  183. width : 15px;\
  184. height : 15px;\
  185. margin-left : 8px;\
  186. background-image : url('https://i.imgur.com/rDqWLLQ.png');\
  187. background-size : contain;";
  188. // As well as a wrapper, for display purposes.
  189. var buttonWrapper = document.createElement("span");
  190. buttonWrapper.classList.add("button-wrapper");
  191. buttonWrapper.setAttribute("onClick", "window.location.reload(true)");
  192. buttonWrapper.title = "Reload";
  193. // We get the chat's settings bar.
  194. var settingsBar = document.getElementsByClassName("settings-bar");
  195. // And we add the button and its wrapper to it.
  196. buttonWrapper.appendChild(refreshButton);
  197. settingsBar[0].appendChild(buttonWrapper);
  198. }
  199.  
  200. // We get the messages container.
  201. var messages = document.getElementById("messages");
  202. if(messages != null) // If it is defined.
  203. {
  204. // We make the messages occupy 100% of their container.
  205. messages.style.width = "100%";
  206. if(window.isNwjs) // If in the application.
  207. {
  208. // We select all the links in the messages.
  209. var links = messages.getElementsByTagName("a");
  210.  
  211. if(links.length > 0) // If there's at least one link.
  212. {
  213. // We set a click event on each of those links.
  214. $('a[target=_blank]').on('click', function()
  215. {
  216. // If the link is not in the openedLinks map.
  217. if(!(this.href in openedLinks))
  218. {
  219. // We open it in a browser.
  220. require('nw.gui').Shell.openExternal(this.href);
  221. // And we add the URL in the map with a value of 100.
  222. openedLinks[this.href] = 100;
  223. }
  224. return false;
  225. });
  226. }
  227. }
  228. }
  229. adjustMessageBox();
  230. }
  231.  
  232. // The main() function.
  233. var main = function()
  234. {
  235. // If we don't know if we are in the application or the browser extension.
  236. if(window.isNwjs == null)
  237. {
  238. try
  239. {
  240. // We call a NWJS specific knack and if it is defined we set the 'isNwjs' variable to true.
  241. window.isNwjs = (typeof require('nw.gui') !== "undefined");
  242. }
  243. catch(e)
  244. {
  245. // If not, we set the 'isNwjs' variable to false;
  246. window.isNwjs = false;
  247. }
  248. }
  249. setTitle(); // Change the window's title.
  250.  
  251. for(var l in openedLinks) // For each URL in the map.
  252. {
  253. openedLinks[l]--; // We decrease its countdown.
  254.  
  255. if(openedLinks[l] == 0) // If the countdown reach 0.
  256. {
  257. // We delete the URL from the map.
  258. delete openedLinks[l];
  259. }
  260. }
  261.  
  262. // Get the current URI.
  263. var path = window.location.pathname;
  264.  
  265. if(path == "/start") // If not logged in.
  266. {
  267. // Go to the connexion page.
  268. window.location.replace("/signin");
  269. }
  270. else if(path == "/signin") // If on the connexion page.
  271. {
  272. // Call the signin() function.
  273. signin();
  274. }
  275. else // In other cases.
  276. {
  277. // Lets chat !
  278. chat();
  279. }
  280. }
  281.  
  282. // Repeat the main() function every 100ms.
  283. setInterval(main, 100);