Darkmode

Adds a button to enable/disable darkmode

  1. // ==UserScript==
  2. // @name Darkmode
  3. // @namespace http://tampermonkey.net/
  4. // @version prerelease-3.0
  5. // @description Adds a button to enable/disable darkmode
  6. // @author guildedbird
  7. // @match https://pixelplace.io/*
  8. // @grant GM_addStyle
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. const disableOceanColor = false;
  16. const darkOceanColor = "rgba(20, 20, 20, 1)";
  17. const defaultOceanColor = "rgba(204, 204, 204, 0)";
  18. const style = "cover";
  19.  
  20. function updateDarkMode() {
  21. const isDarkModeEnabled = localStorage.getItem('darkModeEnabled') === 'true' ? true : false;
  22.  
  23. if (isDarkModeEnabled) {
  24. darkModeElement.classList.add('selected');
  25. document.body.classList.add('darkmode');
  26. if (!disableOceanColor) {
  27. changeOceanColor(darkOceanColor);
  28. }
  29. } else {
  30. darkModeElement.classList.remove('selected');
  31. document.body.classList.remove('darkmode');
  32. if (!disableOceanColor) {
  33. changeOceanColor(defaultOceanColor);
  34. }
  35. }
  36. }
  37.  
  38. function changeOceanColor(color) {
  39. if (disableOceanColor) return;
  40. const painting = document.querySelector('#painting');
  41. if (painting) {
  42. painting.style.backgroundColor = color;
  43. }
  44. }
  45.  
  46. function darkModeOcean() {
  47. const canvas = document.getElementById("canvas");
  48. if (!canvas) return;
  49. const ctx = canvas.getContext("2d");
  50. const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  51. const data = imageData.data;
  52. for (let i = 0; i < data.length; i += 4) {
  53. if (data[i] === 204 && data[i + 1] === 204 && data[i + 2] === 204) {
  54. data[i + 3] = 0;
  55. }
  56. }
  57. ctx.putImageData(imageData, 0, 0);
  58. }
  59.  
  60. function canvasWait() {
  61. if (!document.getElementById("canvas")) {
  62. setTimeout(canvasWait, 100);
  63. } else {
  64. darkModeOcean();
  65. }
  66. }
  67. canvasWait();
  68.  
  69. const css = ` #painting { position: absolute; display: flex; align-items: center; justify-content: center; top: 0; left: 0; cursor: move; background-size: ${style}; background-position: center; background-repeat: no-repeat; margin: 0; height: 100vh; overflow: hidden; }
  70. .darkmode #chat .messages .row a:hover[style="color:#000000"] { text-shadow: 1px 1px 1px #00000025; filter: brightness(0) saturate(100%) invert(97%) sepia(85%) saturate(12%) hue-rotate(184deg) brightness(103%) contrast(103%); } .darkmode #chat .messages .row a:hover[style="color:#222222"] { text-shadow: 1px 1px 1px #00000025; filter: brightness(0) saturate(100%) invert(97%) sepia(85%) saturate(12%) hue-rotate(184deg) brightness(103%) contrast(103%);`;
  71. const styleElement = document.createElement("style");
  72. styleElement.type = "text/css";
  73. styleElement.appendChild(document.createTextNode(css));
  74. document.head.appendChild(styleElement);
  75.  
  76. const modalContent = document.querySelector('#modals .box[data-id="main"] .box-content[data-id="tools"] div form');
  77.  
  78. const darkModeElement = document.createElement('a');
  79. darkModeElement.href = '#';
  80. darkModeElement.classList.add('input-checkbox');
  81. darkModeElement.setAttribute('data-name', 'tools-enable-darkmode');
  82.  
  83. darkModeElement.innerHTML =
  84. `<div>
  85. <div class="input">
  86. <div></div>
  87. </div>
  88. </div>
  89. <div>
  90. <div class="header">Enable darkmode</div>
  91. <div class="content">
  92. Changes the menu theme to darkmode
  93. </div>
  94. </div>`;
  95.  
  96. const formChildren = modalContent.children;
  97. if (formChildren.length >= 10) {
  98. modalContent.insertBefore(darkModeElement, formChildren[9]);
  99. } else {
  100. modalContent.appendChild(darkModeElement);
  101. }
  102.  
  103. updateDarkMode();
  104.  
  105. darkModeElement.addEventListener('click', function() {
  106. darkModeElement.classList.toggle('selected');
  107. const isDarkModeNowEnabled = darkModeElement.classList.contains('selected');
  108. localStorage.setItem('darkModeEnabled', isDarkModeNowEnabled.toString());
  109. updateDarkMode();
  110.  
  111. setTimeout(() => {
  112. const notifications = document.querySelector('#notification');
  113. if (!notifications) return;
  114.  
  115. const existingNotification = notifications.querySelector('.box');
  116. if (existingNotification) existingNotification.remove();
  117.  
  118. const notification = document.createElement('div');
  119. notification.className = isDarkModeNowEnabled ? 'box success' : 'box warning';
  120. notification.innerHTML =
  121. `<div class="icon"></div>
  122. <div class="content">
  123. <div class="title">Tools</div>
  124. <div class="description">Darkmode ${isDarkModeNowEnabled ? 'enabled' : 'disabled'}</div>
  125. </div>`;
  126.  
  127. notifications.appendChild(notification);
  128. setTimeout(() => {
  129. notification.style.transition = 'opacity 1s';
  130. notification.style.opacity = '0';
  131. setTimeout(() => notification.remove(), 1000);
  132. }, 6000);
  133. });
  134. });
  135.  
  136. document.querySelector('#container #copyright').innerHTML = "<span title='Click to read Terms, Copyright & more (script made by @guildedbird)'>www.pixelplace.io</span>";
  137.  
  138. GM_addStyle(`
  139. .darkmode #loader, .darkmode #loader-canvas{
  140. background: url('https://i.imgur.com/v9kRnaf.png') no-repeat right bottom,
  141. linear-gradient(to bottom, rgba(50,50,50,1), rgba(20,20,20,1));
  142. background-size: 5%;
  143. }
  144. .darkmode #modals .box[data-id=main], .darkmode #social, .darkmode #modals .box, body {
  145. background-color: #161616 !important;
  146. background-image: linear-gradient(to bottom, rgba(30,30,30,1), rgba(20,20,20,1));
  147. border: 1px solid #4b4949;
  148. }
  149. .darkmode .box-x {
  150. background-color: #161616 !important;
  151. background-image: linear-gradient(to bottom, rgba(30,30,30,1), rgba(20,20,20,1));
  152. border: 1px solid #4b4949;
  153. }
  154. wrapper * {
  155. background-color: #161616 !important;
  156. background-image: linear-gradient(to bottom, rgba(30,30,30,1), rgba(20,20,20,1));
  157. border: 1px solid #4b4949;
  158. }
  159. .darkmode .box-x > .box-content-x {
  160. color: #ffffff !important;
  161. }
  162. .darkmode #modals .box[data-id=main] a {
  163. color: #ffffff;
  164. }
  165. .darkmode .btn, .darkmode button, .darkmode .prem-item .gift-item, .darkmode #menu-buttons-right-bottom > a, .darkmode #menu-buttons-right-bottom > a > .notification-bubble.green, .darkmode #modals .box .close, .darkmode #chat .buttons a, .darkmode #profile .box-x .close, .modx .box-x .close, .darkmode #guild .box-x .close, .darkmode #onlineUsers .box-x .close, .darkmode #item .box-x .close, .darkmode #checkout-method .box-x .close, .darkmode #coin-island .box-x .close, .darkmode #gold-rush .box-x .close, .darkmode #pixel-lottery .box-x .close, .darkmode #daily-reward .box-x .close{
  166. background-color: #3b3b3b;
  167. }
  168. .darkmode #painting-grid {
  169. background-image: linear-gradient(to right, #3b3b3b 1px,transparent 1px),linear-gradient(to bottom, #3b3b3b 1px,transparent 1px) !important;
  170. }
  171. .darkmode .btn:hover, .darkmode button:hover, .darkmode .prem-item .darkmode .gift-item:hover, .darkmode #menu-buttons-right-bottom > a:hover, .darkmode #modals .box .close:hover, .darkmode #chat .buttons a:hover, .darkmode #more-colors-below:hover, .darkmode #more-colors-above:hover{
  172. background-color: #4b4b4b;
  173. }
  174. .darkmode #menu-buttons > a:hover, .darkmode #menu-buttons-right-top > a:hover, .darkmode .favorite-btn.active:hover, .darkmode .favorite-btn.filled:hover, .darkmode .more-content-below:hover, .darkmode #menu-buttons-bottom > a:hover, .darkmode #menu-buttons-right-bottom > a:hover, .darkmode #more-colors-below:hover, .darkmode #more-colors-above:hover {
  175. background-color: #3b3b3b !important;
  176. }
  177. .darkmode .painting a, .darkmode .bold.text-center .followers, .darkmode .profile-name a, .darkmode #profile .box-x .box-content-x .user-avatar .edit-user-avatar a, #guild .edit-guild-emblem, .darkmode #guild .open-profile{
  178. color: #8b8b8b;
  179. }
  180. .darkmode .painting a:hover, .darkmode .bold.text-center .followers:hover, .darkmode .darkmode .profile-name a:hover, .darkmode #profile .box-x .box-content-x .user-avatar .edit-user-avatar a:hover{
  181. color: #9b9b9b;
  182. }
  183. .darkmode .c-loader-inner {
  184. background-color: #6b6b6b;
  185. }
  186. .darkmode #guild .box-x .box-content-x div form button{
  187. background-color: #8b8b8b !important;
  188. }
  189. .darkmode #profile .edit-user-avatar, .darkmode #profile .text-center .display-block{
  190. color: #8b8b8b !important;
  191. }
  192. .darkmode #tools, .darkmode #warTimer .box, .darkmode #menu-buttons-right-top > a, .darkmode #menu-buttons > a, .darkmode .favorite-btn.active, .darkmode .favorite-btn.filled, .darkmode .more-content-below, .darkmode #menu-buttons-bottom > a, .darkmode #menu-buttons-right-bottom > a, .darkmode #more-colors-below, .darkmode #more-colors-above{
  193. background-color: #2b2b2b !important;
  194. }
  195. .darkmode #modals .box-content[data-id=top] div form#painting-list {
  196. background-color: transparent !important;
  197. }
  198. .darkmode .box-sub-menu li.selected a, .darkmode .box-sub-menu li.selected a {
  199. background-color: transparent !important;
  200. color: #9c9c9c !important;
  201. }
  202. .darkmode #modals a:hover{
  203. color: #3b3b3b !important;
  204. }
  205. .darkmode #chat .messages .row .user:unstyled:hover{
  206. border-bottom: 1px solid #757575 !important;
  207. }
  208. .darkmode #social .tabs-list a.active {
  209. border-bottom: 1px solid #757575 !important;
  210. }
  211. .darkmode #modals .box > .box-menu li.selected a {
  212. color: #9c9c9c !important;
  213. background-color: #282828 !important;
  214. }
  215. .darkmode .prem-coins:hover .buy-item, .darkmode .prem-item:hover .buy-item {
  216. filter: brightness(1.25) !important;
  217. }
  218. .darkmode #modals .box > .box-sub-menu li a:hover{
  219. border-bottom: 1px solid #757575 !important;
  220. }
  221. .darkmode #modals .box > .box-menu{
  222. background-color: #000000 !important;
  223. border-top: 1px solid #4b4949 !important;
  224. }
  225. .darkmode #modals .box > .box-menu li a {
  226. background-color: #00000000 !important;
  227. }
  228. .darkmode #modals .box > .box-content h1, .darkmode .prem-box .left > div, .darkmode .prem-box .right > div, .darkmode #blog .article .category, .darkmode #social .tabs-list a, .darkmode #social .list table td a, .darkmode #modals .box > .box-header, .darkmode #modals .box > .box-menu li a{
  229. color: #ffffff;
  230. }
  231. .darkmode #loader .text, .darkmode #loader-canvas .text{
  232. color: #ffffff; !important;
  233. }
  234. .darkmode .c-loader{
  235. border: 4px solid #ffffff;
  236. }
  237. .darkmode #modals .box > .box-content, .darkmode #social .list{
  238. scrollbar-color: #8f8f8f #00000000 !important;
  239. }
  240. #modals .box > .box-content .inline-checkbox input[type="checkbox"]:checked:before {
  241. background-color: #3b3b3b;
  242. border: 1px solid #3b3b3b;
  243. }
  244. .darkmode #chat .tabs > a:hover{
  245. color: #ffffff !important;
  246. }
  247. #timemachine {
  248. background-color: rgb(0 0 0 / 75%);
  249. border-bottom: 2px dashed rgba(255, 255, 255, 0.75)
  250. }
  251. #timemachine > div b, #timemachine > div .inf, #timemachine > div .timestamp, #timemachine .check, .request-row .username{
  252. color: white
  253. }
  254. `);
  255.  
  256. const makeDraggable = (element, handle) => {
  257. let offsetX = 0, offsetY = 0, isDragging = false;
  258.  
  259. const mouseMoveHandler = (e) => {
  260. if (!isDragging) return;
  261. element.style.position = 'absolute';
  262. element.style.left = `${e.clientX - offsetX}px`;
  263. element.style.top = `${e.clientY - offsetY}px`;
  264. handle.style.cursor = 'grabbing';
  265. };
  266.  
  267. const mouseUpHandler = () => {
  268. isDragging = false;
  269. handle.style.cursor = 'grab';
  270. document.body.style.userSelect = '';
  271.  
  272. sessionStorage.setItem('toolsLeft', element.style.left);
  273. sessionStorage.setItem('toolsTop', element.style.top);
  274. document.removeEventListener('mousemove', mouseMoveHandler);
  275. document.removeEventListener('mouseup', mouseUpHandler);
  276. };
  277.  
  278. const mouseDownHandler = (e) => {
  279. sessionStorage.removeItem('toolsLeft');
  280. sessionStorage.removeItem('toolsTop');
  281.  
  282. isDragging = true;
  283. offsetX = e.clientX - element.getBoundingClientRect().left;
  284. offsetY = e.clientY - element.getBoundingClientRect().top;
  285. document.addEventListener('mousemove', mouseMoveHandler);
  286. document.addEventListener('mouseup', mouseUpHandler);
  287.  
  288. document.body.style.userSelect = 'none';
  289. };
  290.  
  291. handle.style.cursor = 'grab';
  292. handle.addEventListener('mousedown', mouseDownHandler);
  293. };
  294.  
  295. const waitForElement = (selector, callback) => {
  296. const interval = setInterval(() => {
  297. const element = document.querySelector(selector);
  298. if (element) {
  299. clearInterval(interval);
  300. callback(element);
  301. }
  302. }, 100);
  303. };
  304.  
  305. sessionStorage.removeItem('toolsLeft');
  306. sessionStorage.removeItem('toolsTop');
  307.  
  308. waitForElement('#tools', (tools) => {
  309. const handle = document.createElement('div');
  310. handle.style.position = 'absolute';
  311. handle.style.top = '40px';
  312. handle.style.right = '-35px';
  313. handle.style.width = '20px';
  314. handle.style.height = '20px';
  315. handle.style.padding = '12px';
  316. handle.style.background = '#2b2b2b';
  317. handle.style.border = '2px solid #fff';
  318. handle.style.borderRadius = '15px';
  319. handle.style.cursor = 'grab';
  320. handle.style.display = 'flex';
  321. handle.style.alignItems = 'center';
  322. handle.style.justifyContent = 'center';
  323. handle.style.zIndex = '1000';
  324. handle.style.boxShadow = '0px 0px 5px 0px rgba(0, 0, 0, 0.75)';
  325. handle.title = 'Drag Me!';
  326.  
  327. const img = document.createElement('img');
  328. img.src = 'https://i.imgur.com/DDjMbDW.png';
  329. img.alt = 'Drag Icon';
  330. img.style.width = '18px';
  331. img.style.height = '18px';
  332. img.style.pointerEvents = 'none';
  333.  
  334. handle.appendChild(img);
  335.  
  336. const closeHandle = handle.cloneNode(true);
  337. closeHandle.style.top = '6px';
  338. closeHandle.style.right = '-35px';
  339.  
  340. closeHandle.style.background = '#2b2b2b';
  341. closeHandle.title = 'Close UI';
  342.  
  343. const closeImg = document.createElement('img');
  344. closeImg.src = 'https://pixelplace.io/img/icons/x-modal.svg';
  345. closeImg.alt = 'Close Icon';
  346. closeImg.style.width = '16px';
  347. closeImg.style.height = '16px';
  348. closeImg.style.pointerEvents = 'none';
  349.  
  350. closeHandle.innerHTML = '';
  351. closeHandle.appendChild(closeImg);
  352.  
  353. closeHandle.addEventListener('click', () => {
  354.  
  355. const paintingOwnerToolsLink = document.querySelector('a[title="Painting Owner Tools"]');
  356. if (paintingOwnerToolsLink) {
  357. paintingOwnerToolsLink.click();
  358. }
  359. });
  360.  
  361. const savedLeft = sessionStorage.getItem('toolsLeft');
  362. const savedTop = sessionStorage.getItem('toolsTop');
  363. tools.style.position = 'absolute';
  364. if (savedLeft && savedTop) {
  365. tools.style.left = savedLeft;
  366. tools.style.top = savedTop;
  367. }
  368.  
  369. tools.style.position = 'absolute';
  370. tools.appendChild(closeHandle);
  371. tools.appendChild(handle);
  372.  
  373. makeDraggable(tools, handle);
  374.  
  375. const observer = new MutationObserver(() => {
  376. const display = getComputedStyle(tools).display;
  377. if (display !== 'none') {
  378. const newLeft = sessionStorage.getItem('toolsLeft');
  379. const newTop = sessionStorage.getItem('toolsTop');
  380. if (newLeft && newTop) {
  381. tools.style.left = newLeft;
  382. tools.style.top = newTop;
  383. }
  384. }
  385. });
  386. observer.observe(tools, { attributes: true, attributeFilter: ['style'] });
  387. });
  388. })();