RB Visual Overhaul

replaces old rank icons and adds new ones, brings back the clock, etc.

  1. // ==UserScript==
  2. // @name RB Visual Overhaul
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-11-25
  5. // @description replaces old rank icons and adds new ones, brings back the clock, etc.
  6. // @author Lemniscata
  7. // @match https://www.rusbionicle.com/*
  8. // @match https://rusbionicle.com/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=rusbionicle.com
  10. // @resource DEFAULT_CSS https://rusbionicle.com/forumsbio/style.php?&id=8&lang=ru
  11. // @resource IMPORTED_CSS https://raw.githubusercontent.com/OSP-Scata/RB-Visual-Overhaul/refs/heads/main/dark_theme/cssoverride.css
  12. // @resource DATABASE https://raw.githubusercontent.com/OSP-Scata/RB-Visual-Overhaul/refs/heads/main/new_ranks/ranks-database.json
  13. // @grant GM_getResourceText
  14. // @grant GM_addStyle
  15. // @grant GM_getValue
  16. // @grant GM_setValue
  17. // @license MIT
  18. // ==/UserScript==
  19.  
  20. (async() => {
  21. 'use strict';
  22. // часы
  23. var clock = document.createElement('div');
  24. clock.innerHTML = '<div id="clock"> \
  25. <center><object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0" width="150" height="150"> \
  26. <param name="wmode" value="transparent" /> \
  27. <param name="movie" value="/clock/clockfinal2.swf" /> \
  28. <param name="quality" value="high" /> \
  29. <embed src="/clock/clockfinal2.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="150" height="150" wmode="transparent"></embed> \
  30. </object> \
  31. </center> \
  32. </div>';
  33. document.getElementById('datebar').appendChild(clock);
  34.  
  35. // тёмная тема и кнопка для переключения
  36. const darkTheme = GM_getResourceText("IMPORTED_CSS");
  37. const lightTheme = GM_getResourceText("DEFAULT_CSS");
  38. var button = document.createElement("button");
  39. var header = document.querySelector('#logodesc');
  40. header.appendChild(button);
  41. var currentTheme = await GM_getValue("theme");
  42. var state;
  43. button.addEventListener('click', toggle);
  44. if (currentTheme == 'light' || typeof currentTheme == 'undefined') {
  45. button.innerText = "Тёмная тема";
  46. state = false;
  47. button.style.cssText = 'float: right; margin: 20px; background-color: #383a40; color: #dbdee1';
  48. }
  49. else if (currentTheme == 'dark') {
  50. GM_addStyle(darkTheme);
  51. state = true;
  52. button.innerText = "Светлая тема";
  53. button.style.cssText = 'float: right; margin: 20px; background-color: unset; color: unset';
  54. }
  55. function toggle(){
  56. if (!state) {
  57. GM_addStyle(darkTheme);
  58. button.innerText = "Светлая тема";
  59. button.style.cssText = 'float: right; margin: 20px; background-color: unset; color: unset';
  60. state = true;
  61. GM_setValue("theme", "dark");
  62. }
  63. else {
  64. GM_addStyle(lightTheme);
  65. button.innerText = "Тёмная тема";
  66. button.style.cssText = 'float: right; margin: 20px; background-color: #383a40; color: #dbdee1';
  67. state = false;
  68. GM_setValue("theme", "light");
  69. }
  70. }
  71.  
  72. const data = JSON.parse(GM_getResourceText("DATABASE"));
  73.  
  74. // замена рангов
  75. // в постах...
  76. var profiles = document.querySelectorAll('td.profile'); // сведения о пользователе в постах
  77. profiles.forEach((profile) => {
  78. var postDetails = profile.querySelector('span.postdetails') // тут ищем число сообщений
  79. var tdPostDetails = profile.querySelector('td.postdetails') // тут название ранга
  80. if (postDetails && tdPostDetails) {
  81. var userData = postDetails.querySelectorAll('b');
  82. if (userData[0].innerText != "Предупреждения:") {
  83. var numberOfPosts1 = parseInt(userData[1].nextSibling.data) // число постов в темах
  84. var topicRank = tdPostDetails.innerText;
  85. if (!topicRank.toLowerCase().includes('администратор') && !topicRank.toLowerCase().includes('модератор') && !topicRank.toLowerCase().includes('узник')) {
  86. for (var i = 0; i < data.length; i++) {
  87. if (numberOfPosts1 < data[i]['postNumber']) {
  88. var rankIcon = profile.querySelector('img[src*="./images/ranks/"]')
  89. rankIcon.src = rankIcon.src.replace(rankIcon.src, data[i-1]['rankIcon']);
  90. rankIcon.setAttribute("alt", data[i-1]['rankName']);
  91. rankIcon.setAttribute("title", data[i-1]['rankName']);
  92. profile.querySelector('td.postdetails').innerText = data[i-1]['rankName'];
  93. break;
  94. }
  95. else if (numberOfPosts1 >= data[data.length-1]['postNumber']) {
  96. rankIcon = profile.querySelector('img[src*="./images/ranks/"]')
  97. rankIcon.src = rankIcon.src.toLowerCase().replace(rankIcon.src, data[i]['rankIcon']);
  98. rankIcon.setAttribute("alt", data[i]['rankName']);
  99. rankIcon.setAttribute("title", data[i]['rankName']);
  100. profile.querySelector('td.postdetails').innerText = data[i-1]['rankName'];
  101. }
  102. }
  103. }
  104. }
  105. }
  106. });
  107.  
  108. // ...и на странице пользователей
  109. var row = document.querySelectorAll('tr.row1, tr.row2'); // сведения о пользователе в списке пользователей
  110. if (row.length == 50) { // потому что на странице 50 пользователей (чтобы не мешало остальному форуму)
  111. row.forEach((td) => {
  112. var userDataInUsers = td.querySelectorAll('td.gen');
  113. var numberOfPosts2 = parseInt(userDataInUsers[1].innerText); // число постов в списке пользователей
  114. var usersRank = userDataInUsers[2].innerHTML; // ранг в списке пользователей
  115. var rankText = usersRank.split("\"")[usersRank.split("\"").length-2];
  116. if ((rankText) && (!rankText.toLowerCase().includes('администратор') && !rankText.toLowerCase().includes('модератор') && !rankText.toLowerCase().includes('узник'))) {
  117. for (var i = 0; i < data.length; i++) {
  118. if (numberOfPosts2 < data[i]['postNumber']) {
  119. var rankIcon = td.querySelector('img[src*="./images/ranks/"]')
  120. rankIcon.src = rankIcon.src.replace(rankIcon.src, data[i-1]['rankIcon']);
  121. rankIcon.setAttribute("alt", data[i-1]['rankName']);
  122. rankIcon.setAttribute("title", data[i-1]['rankName']);
  123. break;
  124. }
  125. else if (numberOfPosts2 >= data[data.length-1]['postNumber']) {
  126. rankIcon = document.querySelector('img[src*="./images/ranks/"]')
  127. rankIcon.src = rankIcon.src.toLowerCase().replace(rankIcon.src, data[i]['rankIcon']);
  128. rankIcon.setAttribute("alt", data[i]['rankName']);
  129. rankIcon.setAttribute("title", data[i]['rankName']);
  130. }
  131. }
  132. }
  133. });
  134. }
  135. // ещё и страница профиля...
  136. var userProfileRank = document.querySelector('td.postdetails'); // ранг в профиле
  137. var canProfile = userProfileRank ? userProfileRank.innerText : ""; // тест на возможность спарсить ранг (если мы не на странице профиля)
  138. if (canProfile) {
  139. userProfileRank = userProfileRank.innerText;
  140. if (!userProfileRank.toLowerCase().includes('администратор') && !userProfileRank.toLowerCase().includes('модератор') && !userProfileRank.toLowerCase().includes('узник')) {
  141. var bGen = document.querySelectorAll('b.gen')
  142. bGen.forEach((tag) => {
  143. if (!isNaN(parseInt(tag.innerText))) {
  144. var numberOfPosts3 = parseInt(tag.innerText) // число постов в профиле
  145. for (var i = 0; i < data.length; i++) {
  146. if (numberOfPosts3 < data[i]['postNumber']) {
  147. var rankIcon = document.querySelector('img[src*="./images/ranks/"]')
  148. rankIcon.src = rankIcon.src.replace(rankIcon.src, data[i-1]['rankIcon']);
  149. rankIcon.setAttribute("alt", data[i-1]['rankName']);
  150. rankIcon.setAttribute("title", data[i-1]['rankName']);
  151. document.querySelector('td.postdetails').innerText = data[i-1]['rankName']
  152. break;
  153. }
  154. else if (numberOfPosts3 >= data[data.length-1]['postNumber']) {
  155. rankIcon = document.querySelector('img[src*="./images/ranks/"]')
  156. rankIcon.src = rankIcon.src.replace(rankIcon.src, data[i]['rankIcon']);
  157. rankIcon.setAttribute("alt", data[i]['rankName']);
  158. rankIcon.setAttribute("title", data[i]['rankName']);
  159. document.querySelector('td.postdetails').innerText = data[i]['rankName']
  160. }
  161. }
  162. }
  163. })
  164. }
  165. }
  166. // иконки онлайн-оффлайн
  167. document.querySelectorAll('img[src*="whosonline.gif"]').forEach((image) => {
  168. image.src = image.src.replace('https://rusbionicle.com/forumsbio/styles/subsilver2/theme/images/whosonline.gif', 'https://raw.githubusercontent.com/OSP-Scata/RB-Visual-Overhaul/refs/heads/main/icons/whosonline.png');
  169. image.src = image.src.replace('https://www.rusbionicle.com/forumsbio/styles/subsilver2/theme/images/whosonline.gif', 'https://raw.githubusercontent.com/OSP-Scata/RB-Visual-Overhaul/refs/heads/main/icons/whosonline.png');
  170. })
  171. document.querySelectorAll('img[src*="./styles/subsilver2/imageset/ru/"]').forEach((image) => {
  172. image.src = image.src.replace('https://rusbionicle.com/forumsbio/styles/subsilver2/imageset/ru/icon_user_offline.gif', 'https://raw.githubusercontent.com/OSP-Scata/RB-Visual-Overhaul/refs/heads/main/icons/icon_user_offline.png');
  173. image.src = image.src.replace('https://www.rusbionicle.com/forumsbio/styles/subsilver2/imageset/ru/icon_user_offline.gif', 'https://raw.githubusercontent.com/OSP-Scata/RB-Visual-Overhaul/refs/heads/main/icons/icon_user_offline.png');
  174. image.src = image.src.replace('https://rusbionicle.com/forumsbio/styles/subsilver2/imageset/ru/icon_user_online.gif', 'https://raw.githubusercontent.com/OSP-Scata/RB-Visual-Overhaul/refs/heads/main/icons/icon_user_online.png');
  175. image.src = image.src.replace('https://www.rusbionicle.com/forumsbio/styles/subsilver2/imageset/ru/icon_user_online.gif', 'https://raw.githubusercontent.com/OSP-Scata/RB-Visual-Overhaul/refs/heads/main/icons/icon_user_online.png');
  176. })
  177.  
  178. /*
  179. // реплейс существующих иконок рангов (старое)
  180. function updateImagesSrc() {
  181. document.querySelectorAll('img[src*="./images/ranks/"]').forEach((previewImage) => {
  182. previewImage.src = previewImage.src.toLowerCase().replace('https://www.rusbionicle.com/forumsbio/images/ranks/', 'https://brickshelf.com/gallery/Roodaka8761/Bionicle/RB-new-ranks/');
  183. })
  184. }
  185.  
  186. if(document.readyState == 'complete') {
  187. updateImagesSrc();
  188. } else {
  189. window.addEventListener('load', updateImagesSrc);
  190. } */
  191. })();