Texture Pack Manager [Taming.io, Sploop.io, Moomoo.io]

Allows you to change game textures!

安装此脚本
作者推荐脚本

您可能也喜欢Dsync Client [Sploop.io]

安装此脚本
  1. // ==UserScript==
  2. // @name Texture Pack Manager [Taming.io, Sploop.io, Moomoo.io]
  3. // @author Murka
  4. // @description Allows you to change game textures!
  5. // @icon https://i.imgur.com/o0KykL1.png
  6. // @version 0.2
  7. // @match *://taming.io/*
  8. // @match *://sploop.io/*
  9. // @match *://moomoo.io/*
  10. // @match *://*.moomoo.io/*
  11. // @run-at document-start
  12. // @grant none
  13. // @noframes
  14. // @license MIT
  15. // @namespace https://greasyfork.org/users/919633
  16. // ==/UserScript==
  17. /* jshint esversion:8 */
  18.  
  19. /*
  20. Author: Murka
  21. Github: https://github.com/Murka007
  22. Discord: https://discord.gg/sG9cyfGPj5
  23. Greasyfork: https://greasyfork.org/en/users/919633
  24. */
  25.  
  26. (function() {
  27. "use strict";
  28.  
  29. const log = console.log.bind(console);
  30. const storage = {
  31. get(key) {
  32. const value = localStorage.getItem(key);
  33. return value === null ? null : JSON.parse(value);
  34. },
  35. set(key, value) {
  36. localStorage.setItem(key, JSON.stringify(value));
  37. }
  38. };
  39.  
  40. const DATA_TYPES = { TEXTURES: 0, EMOJIS: 1 };
  41. const SEARCH_TYPES = { CONTAINS: 0, EQUALS: 1 };
  42. const TYPES = { 0: "textures", 1: "emojis" };
  43. const TEXT_TYPES = { 0: "URL", 1: "MESSAGE" };
  44. const HEADER_TYPES = { 0: "Texture Manager", 1: "Emoji Manager" };
  45.  
  46. function isURL(string) {
  47. try {
  48. const url = new URL(string);
  49. return /^https?:/.test(url.protocol);
  50. } catch(err) {}
  51. }
  52.  
  53. function isValidImageSrc(src) {
  54. return new Promise(resolve => {
  55. const img = new Image();
  56. img.src = src;
  57. img.onload = () => resolve(isURL(src));
  58. img.onerror = () => resolve(false);
  59. })
  60. }
  61.  
  62. function isValidSetting(option) {
  63. const { id, type, searchType, from, to } = option;
  64.  
  65. const equalToDataTypes = [DATA_TYPES.TEXTURES, DATA_TYPES.EMOJIS].includes(type);
  66. const equalToSearchTypes = [SEARCH_TYPES.CONTAINS, SEARCH_TYPES.EQUALS].includes(searchType);
  67. const isString = typeof from === "string" && typeof to === "string";
  68. const isTextureURL = type === DATA_TYPES.TEXTURES && isURL(to) || type === DATA_TYPES.EMOJIS;
  69.  
  70. return Number.isInteger(id) && equalToDataTypes && equalToSearchTypes && isString && isTextureURL;
  71. }
  72.  
  73. function createSettings() {
  74. const settings = {};
  75. settings.textures = [];
  76. settings.emojis = [];
  77. settings.freeIDS = [];
  78. return settings;
  79. }
  80.  
  81. const settings = (function() {
  82. const defaultSettings = createSettings();
  83. const settings = Object.assign({}, defaultSettings, storage.get("TextureSettings"));
  84.  
  85. for (const key in settings) {
  86. if (!defaultSettings.hasOwnProperty(key)) {
  87. delete settings[key];
  88. } else if (Array.isArray(settings[key]) && key === TYPES[DATA_TYPES[key.toUpperCase()]]) {
  89. for (let i=settings[key].length-1;i>=0;i--) {
  90. if (!isValidSetting(settings[key][i])) {
  91. settings[key].splice(i, 1);
  92. }
  93. }
  94. }
  95. }
  96.  
  97. storage.set("TextureSettings", settings);
  98. return settings;
  99. })();
  100.  
  101. function replaceValue(type, value) {
  102. const testValue = value.match(/img.+$/)[0];
  103. for (const option of settings[TYPES[type]]) {
  104. const { searchType, from, to } = option;
  105. if (type === DATA_TYPES.TEXTURES) {
  106. if (searchType === SEARCH_TYPES.CONTAINS && testValue.includes(from)) return to;
  107. if (searchType === SEARCH_TYPES.EQUALS && testValue === from) return to;
  108. }
  109. }
  110. return value;
  111. }
  112.  
  113. const src = Object.getOwnPropertyDescriptor(Image.prototype, "src").set;
  114. Object.defineProperty(Image.prototype, "src", {
  115. set(link) {
  116. return src.call(this, replaceValue(DATA_TYPES.TEXTURES, link));
  117. }
  118. })
  119.  
  120. const HTML = `
  121. <div id="page-container" class="opened-menu">
  122. <header>
  123. <div class="imageHolder">
  124. <img src="https://i.imgur.com/o0KykL1.png" draggable="false"/>
  125. </div>
  126. <span>Texture Pack Manager</span>
  127. <div id="close-menu">
  128. <svg class="cross-icon"
  129. version="1.1"
  130. xmlns="http://www.w3.org/2000/svg"
  131. width="32" height="32" viewBox="0 0 32 32"
  132. >
  133. <path d="M31.708 25.708c-0-0-0-0-0-0l-9.708-9.708 9.708-9.708c0-0 0-0 0-0 0.105-0.105 0.18-0.227 0.229-0.357 0.133-0.356 0.057-0.771-0.229-1.057l-4.586-4.586c-0.286-0.286-0.702-0.361-1.057-0.229-0.13 0.048-0.252 0.124-0.357 0.228 0 0-0 0-0 0l-9.708 9.708-9.708-9.708c-0-0-0-0-0-0-0.105-0.104-0.227-0.18-0.357-0.228-0.356-0.133-0.771-0.057-1.057 0.229l-4.586 4.586c-0.286 0.286-0.361 0.702-0.229 1.057 0.049 0.13 0.124 0.252 0.229 0.357 0 0 0 0 0 0l9.708 9.708-9.708 9.708c-0 0-0 0-0 0-0.104 0.105-0.18 0.227-0.229 0.357-0.133 0.355-0.057 0.771 0.229 1.057l4.586 4.586c0.286 0.286 0.702 0.361 1.057 0.229 0.13-0.049 0.252-0.124 0.357-0.229 0-0 0-0 0-0l9.708-9.708 9.708 9.708c0 0 0 0 0 0 0.105 0.105 0.227 0.18 0.357 0.229 0.356 0.133 0.771 0.057 1.057-0.229l4.586-4.586c0.286-0.286 0.362-0.702 0.229-1.057-0.049-0.13-0.124-0.252-0.229-0.357z"></path>
  134. </svg>
  135. </div>
  136. </header>
  137.  
  138. <main>
  139. <div id="nav-bar">
  140. <button class="open-menu enabled">Textures</button>
  141. <button class="open-menu">Misc</button>
  142. <button class="open-menu bottom-align">Credits</button>
  143. </div>
  144.  
  145. <div id="menu-page-container">
  146. <div class="menu-page opened">
  147. <h1>Textures</h1>
  148. <p class="description">Add new textures</p>
  149. <div id="texture-container" class="item-container"></div>
  150. <div class="add-item">
  151. <label id="add-texture" class="add-item-button">
  152. <svg class="icon-tx plus-icon" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
  153. <path d="M31 12h-11v-11c0-0.552-0.448-1-1-1h-6c-0.552 0-1 0.448-1 1v11h-11c-0.552 0-1 0.448-1 1v6c0 0.552 0.448 1 1 1h11v11c0 0.552 0.448 1 1 1h6c0.552 0 1-0.448 1-1v-11h11c0.552 0 1-0.448 1-1v-6c0-0.552-0.448-1-1-1z"></path>
  154. </svg>
  155. <span class="text">Add texture</span>
  156. </label>
  157. </div>
  158. </div>
  159.  
  160. <div class="menu-page">
  161. <h1>Misc</h1>
  162. <p class="description">Download, upload or reset your settings</p>
  163. <div class="menu-page-section">
  164. <button id="download-settings" class="manage-storage">DOWNLOAD</button>
  165. <button class="manage-storage">
  166. <input id="upload-settings" type="file"/>
  167. UPLOAD
  168. </button>
  169. <button id="reset-settings" class="manage-storage">RESET</button>
  170. </div>
  171. </div>
  172.  
  173. <div class="menu-page">
  174. <h1>Credits</h1>
  175. <div class="menu-page-section">
  176. <span class="highlight">Author:</span>
  177. <span class="highlight-secondary">Murka</span>
  178. </div>
  179. <div class="menu-page-section">
  180. <span class="highlight">
  181. <svg
  182. class="icon-tx github-icon"
  183. version="1.1"
  184. xmlns="http://www.w3.org/2000/svg"
  185. width="32" height="32" viewBox="0 0 32 32"
  186. >
  187. <path d="M16 0.395c-8.836 0-16 7.163-16 16 0 7.069 4.585 13.067 10.942 15.182 0.8 0.148 1.094-0.347 1.094-0.77 0-0.381-0.015-1.642-0.022-2.979-4.452 0.968-5.391-1.888-5.391-1.888-0.728-1.849-1.776-2.341-1.776-2.341-1.452-0.993 0.11-0.973 0.11-0.973 1.606 0.113 2.452 1.649 2.452 1.649 1.427 2.446 3.743 1.739 4.656 1.33 0.143-1.034 0.558-1.74 1.016-2.14-3.554-0.404-7.29-1.777-7.29-7.907 0-1.747 0.625-3.174 1.649-4.295-0.166-0.403-0.714-2.030 0.155-4.234 0 0 1.344-0.43 4.401 1.64 1.276-0.355 2.645-0.532 4.005-0.539 1.359 0.006 2.729 0.184 4.008 0.539 3.054-2.070 4.395-1.64 4.395-1.64 0.871 2.204 0.323 3.831 0.157 4.234 1.026 1.12 1.647 2.548 1.647 4.295 0 6.145-3.743 7.498-7.306 7.895 0.574 0.497 1.085 1.47 1.085 2.963 0 2.141-0.019 3.864-0.019 4.391 0 0.426 0.288 0.925 1.099 0.768 6.354-2.118 10.933-8.113 10.933-15.18 0-8.837-7.164-16-16-16z"></path>
  188. </svg>
  189. </span>
  190. <span class="highlight-secondary"><a href="https://github.com/Murka007" target="_blank">Murka007</a></span>
  191. </div>
  192. <div class="menu-page-section">
  193. <span class="highlight">
  194. <svg
  195. class="icon-tx discord-icon"
  196. version="1.1"
  197. xmlns="http://www.w3.org/2000/svg"
  198. width="32" height="32" viewBox="0 0 32 32"
  199. >
  200. <path d="M26.963 0c1.875 0 3.387 1.516 3.476 3.3v28.7l-3.569-3.031-1.96-1.784-2.139-1.864 0.893 2.94h-18.717c-1.869 0-3.387-1.42-3.387-3.301v-21.653c0-1.784 1.52-3.303 3.393-3.303h22zM18.805 7.577h-0.040l-0.269 0.267c2.764 0.8 4.101 2.049 4.101 2.049-1.781-0.891-3.387-1.336-4.992-1.516-1.16-0.18-2.32-0.085-3.3 0h-0.267c-0.627 0-1.96 0.267-3.747 0.98-0.623 0.271-0.98 0.448-0.98 0.448s1.336-1.336 4.28-2.049l-0.18-0.18c0 0-2.229-0.085-4.636 1.693 0 0-2.407 4.192-2.407 9.36 0 0 1.333 2.32 4.991 2.408 0 0 0.533-0.711 1.073-1.336-2.053-0.624-2.853-1.872-2.853-1.872s0.179 0.088 0.447 0.267h0.080c0.040 0 0.059 0.020 0.080 0.040v0.008c0.021 0.021 0.040 0.040 0.080 0.040 0.44 0.181 0.88 0.36 1.24 0.533 0.621 0.269 1.42 0.537 2.4 0.715 1.24 0.18 2.661 0.267 4.28 0 0.8-0.18 1.6-0.356 2.4-0.713 0.52-0.267 1.16-0.533 1.863-0.983 0 0-0.8 1.248-2.94 1.872 0.44 0.621 1.060 1.333 1.060 1.333 3.659-0.080 5.080-2.4 5.16-2.301 0-5.16-2.42-9.36-2.42-9.36-2.18-1.619-4.22-1.68-4.58-1.68zM19.029 13.461c0.937 0 1.693 0.8 1.693 1.78 0 0.987-0.76 1.787-1.693 1.787s-1.693-0.8-1.693-1.779c0.003-0.987 0.764-1.784 1.693-1.788zM12.972 13.461c0.933 0 1.688 0.8 1.688 1.78 0 0.987-0.76 1.787-1.693 1.787s-1.693-0.8-1.693-1.779c0-0.987 0.76-1.784 1.699-1.788z"></path>
  201. </svg>
  202. </span>
  203. <span class="highlight-secondary"><a href="https://discord.gg/sG9cyfGPj5" target="_blank">Coding Paradise</a></span>
  204. </div>
  205. </div>
  206. </div>
  207. </main>
  208. </div>`;
  209.  
  210. const CSS = `
  211. :root {
  212. --bg-color: #0c0d11;
  213. --bg-sub-color: #13141b;
  214. --main-color: #7ebab5;
  215. --third-color: #8c9eaf;
  216. --sub-color: #454864;
  217. --sub-alt-color: #171a25;
  218. --text-color: #f6f5f5;
  219. --text-color-active: #777a96;
  220. --highlight-color: #717597;
  221. --highlight-color-secondary: #8b91c2;
  222.  
  223. --add-button-color: #86ebad;
  224. --cancel-button-color: #eb9a86;
  225. --add-button-active-color: #5eaa7b;
  226. --cancel-button-active-color: #a36a5b;
  227. --bin-color: #b64444;
  228. --bin-color-border: #973838;
  229.  
  230. --border-color-opacity: #7ebab56b;
  231. --border-color: #7ebab5;
  232. --popup-bg-color: #232630;
  233. --bg-item-content: #272a36;
  234.  
  235. --roundness: 5px;
  236. --padding: 10px;
  237. --transition-delay: 250ms;
  238.  
  239. --syntax-if: #d76de8;
  240. --syntax-method: #e8e06d;
  241. --syntax-constructor: #6de86d;
  242. }
  243.  
  244. /* DEFAULT MENU */
  245. #page-container {
  246. background: var(--bg-color);
  247. position: absolute;
  248. top: 50%;
  249. left: 50%;
  250. transform: translate(-50%, -50%);
  251. z-index: 50;
  252. padding: var(--padding);
  253. border: 1px solid var(--main-color);
  254. border-radius: var(--roundness);
  255.  
  256. width: 50%;
  257. min-width: 500px;
  258. max-width: 600px;
  259. box-sizing: border-box;
  260. opacity: 0;
  261. font-size: 1.5rem;
  262. }
  263.  
  264. #page-container * {
  265. font-family: Arial, Helvetica, sans-serif!important;
  266. font-weight: 600!important;
  267. }
  268.  
  269. #page-container h1, h2, h3, h4, p {
  270. margin: 0;
  271. }
  272.  
  273. #page-container > header {
  274. background: var(--bg-sub-color);
  275. color: var(--text-color);
  276. border-radius: var(--roundness);
  277. padding: 5px var(--padding);
  278.  
  279. font-size: 1.5em;
  280. font-weight: 600;
  281. letter-spacing: -1px;
  282.  
  283. display: flex;
  284. justify-content: flex-start;
  285. align-items: center;
  286. }
  287. header > span {
  288. margin-left: var(--padding);
  289. }
  290.  
  291. #page-container > main {
  292. display: flex;
  293. width: 100%;
  294. height: 350px;
  295. margin-top: var(--padding);
  296. }
  297.  
  298.  
  299. /* MENU LOGO SIZING */
  300. .imageHolder {
  301. display: flex;
  302. justify-content: center;
  303. align-items: center;
  304. width: 40px;
  305. }
  306. .imageHolder > img {
  307. width: 100%;
  308. height: 100%;
  309. }
  310.  
  311.  
  312. /* NAV BAR */
  313. #nav-bar {
  314. background: var(--bg-sub-color);
  315. padding: var(--padding);
  316. border-radius: var(--roundness);
  317.  
  318. float: left;
  319. display: flex;
  320. flex-direction: column;
  321. }
  322.  
  323.  
  324. /* MENU PAGES */
  325. #menu-page-container {
  326. border-radius: var(--roundness);
  327. margin-left: var(--padding);
  328.  
  329. width: 100%;
  330. height: 100%;
  331. overflow-y: auto;
  332. }
  333. .menu-page {
  334. background: var(--bg-sub-color);
  335. padding: var(--padding);
  336. border-radius: var(--roundness);
  337.  
  338. display: none;
  339. }
  340. .opened {
  341. display: block;
  342. animation: opacity var(--transition-delay) forwards;
  343. }
  344. @keyframes opacity {
  345. from { opacity: 0 }
  346. to { opacity: 1 }
  347. }
  348. @keyframes opacity2 {
  349. from { opacity: 1 }
  350. to { opacity: 0 }
  351. }
  352.  
  353.  
  354. /* HEADER AND PARAGRAPH OF MENU */
  355. .menu-page > h1 {
  356. color: var(--main-color);
  357. font-size: 1.6em;
  358. letter-spacing: -2px;
  359. }
  360. .menu-page > h3 {
  361. color: var(--third-color);
  362. margin-top: 15px;
  363. }
  364. .description {
  365. color: var(--sub-color);
  366. font-weight: 600;
  367. font-size: 0.7em;
  368. }
  369. .highlight {
  370. color: var(--highlight-color);
  371. fill: var(--highlight-color);
  372. }
  373. .highlight-secondary {
  374. color: var(--highlight-color-secondary);
  375. font-size: 0.85em;
  376. margin-left: var(--padding);
  377. }
  378.  
  379. .highlight-secondary > a {
  380. color: var(--highlight-color-secondary);
  381. cursor: pointer!important;
  382. }
  383.  
  384. .menu-page-section {
  385. display: flex;
  386. align-items: center;
  387. margin-top: 10px;
  388. font-weight: 600;
  389. }
  390.  
  391. .icon-tx {
  392. width: 25px;
  393. }
  394.  
  395. /* ADD NEW ITEM */
  396. .add-item {
  397. display: flex;
  398. justify-content: center;
  399. margin-top: 10px;
  400. }
  401. .add-item-button {
  402. display: flex;
  403. justify-content: center;
  404. align-items: center;
  405. cursor: pointer!important;
  406. }
  407. .add-item-button > span {
  408. margin-left: 10px;
  409. }
  410.  
  411.  
  412. /* ADD ITEM BUTTON EFFECTS */
  413. .plus-icon {
  414. width: 20px;
  415. transition: fill var(--transition-delay);
  416. fill: var(--sub-color);
  417. }
  418. .text {
  419. color: var(--sub-color);
  420. transition: color var(--transition-delay);
  421. font-weight: 600;
  422. }
  423. .add-item-button > * {
  424. pointer-events: none;
  425. }
  426. .add-item-button:hover .plus-icon {
  427. fill: var(--text-color);
  428. }
  429. .add-item-button:hover .text {
  430. color: var(--text-color);
  431. }
  432. .add-item-button:active .plus-icon {
  433. fill: var(--text-color-active);
  434. }
  435. .add-item-button:active .text {
  436. color: var(--text-color-active);
  437. }
  438.  
  439.  
  440. /* NAV BAR BUTTON */
  441. .open-menu {
  442. outline: none;
  443. border: none;
  444.  
  445. background: var(--sub-alt-color);
  446. color: var(--text-color);
  447. transition: background var(--transition-delay), color var(--transition-delay);
  448. font-size: 1em;
  449. font-weight: 600;
  450. padding: var(--padding);
  451. cursor: pointer!important;
  452. margin-right: 1px;
  453. }
  454. .open-menu:hover {
  455. background: var(--text-color);
  456. color: var(--sub-alt-color);
  457. }
  458. .open-menu:active {
  459. background: var(--sub-color);
  460. color: var(--sub-alt-color);
  461. }
  462. .open-menu.enabled {
  463. border: solid;
  464. border-width: 0 1px 0 0;
  465. border-color: var(--text-color);
  466. pointer-events: none;
  467. transition: border-color var(--transition-delay);
  468. margin-right: 0px;
  469. }
  470. .bottom-align {
  471. margin-top: auto;
  472. }
  473.  
  474.  
  475. /* ITEM SETTINGS */
  476. .item-content {
  477. position: relative;
  478. display: flex;
  479. flex-direction: row;
  480. align-items: center;
  481. justify-content: space-between;
  482.  
  483. background: var(--bg-item-content);
  484. padding: var(--padding);
  485. padding-right: 17px;
  486. font-size: 0.8rem;
  487. font-weight: 600;
  488. border-radius: var(--roundness);
  489. text-align: center;
  490. margin-top: 15px;
  491. }
  492. .syntax-help {
  493. margin: 0 2px;
  494. }
  495. .type-text {
  496. color: var(--text-color);
  497. }
  498. .syntax-if {
  499. color: var(--syntax-if);
  500. }
  501. .syntax-method {
  502. color: var(--syntax-method);
  503. margin: 0 1px;
  504. }
  505. .syntax-constructor {
  506. color: var(--syntax-constructor);
  507. }
  508. .add-item-input {
  509. outline: none;
  510. border: none;
  511. width: 50px;
  512.  
  513. background: var(--sub-color);
  514. color: var(--text-color);
  515. border: 1px solid;
  516. border-color: transparent;
  517. transition: border-color var(--transition-delay);
  518.  
  519. border-radius: var(--roundness);
  520.  
  521. padding: var(--padding) 5px;
  522. font-size: 0.6rem;
  523. font-weight: 600;
  524. text-align: center;
  525. cursor: text!important;
  526. }
  527. .add-item-input:hover {
  528. border-color: var(--border-color-opacity);
  529. }
  530. .add-item-input:focus {
  531. border-color: var(--text-color);
  532. }
  533. .add-item-input::placeholder {
  534. color: var(--text-color);
  535. opacity: 1; /* Firefox */
  536. }
  537. .add-item-input:-ms-input-placeholder {
  538. color: var(--text-color);
  539. }
  540. .add-item-input::-ms-input-placeholder {
  541. color: var(--text-color);
  542. }
  543.  
  544. .item-container > .item-content > .add-item-input {
  545. /*width: 40px;*/
  546. }
  547. .item-container > .item-content > .custom-select {
  548. /*width: 40px;*/
  549. font-size: 0.5rem;
  550. }
  551.  
  552. .bin-icon {
  553. width: 15px;
  554. height: 15px;
  555. }
  556. .delete-item {
  557. position: absolute;
  558. top: 5px;
  559. right: 0;
  560. opacity: 0;
  561. fill: var(--bin-color);
  562. cursor: pointer!important;
  563. }
  564. .delete-item > * {
  565. pointer-events: none;
  566. }
  567. .item-content:hover .delete-item {
  568. opacity: 1;
  569. }
  570. .item-content > .imageHolder {
  571. width: 30px;
  572. margin-left: 3px;
  573. }
  574.  
  575.  
  576. /* POPUP ADD ITEM */
  577. .popup-container {
  578. position: absolute;
  579. top: 50%;
  580. left: 50%;
  581. transform: translate(-50%, -50%);
  582. background: rgba(0, 0, 0, 0.5);
  583. animation: opacity var(--transition-delay) forwards;
  584.  
  585. display: flex;
  586. justify-content: center;
  587. align-items: center;
  588. z-index: 10;
  589.  
  590. width: 100%;
  591. height: 100%;
  592. box-sizing: border-box;
  593.  
  594. border-radius: var(--roundness);
  595. font-weight: 600;
  596. }
  597.  
  598. .add-item-popup {
  599. padding: var(--padding);
  600. border: 1px solid var(--border-color-opacity);
  601. border-radius: var(--roundness);
  602.  
  603. background: var(--popup-bg-color);
  604. text-align: center;
  605. width: 27rem;
  606. }
  607.  
  608. .popup-header {
  609. color: var(--text-color);
  610. }
  611.  
  612. .popup-to-remove {
  613. animation: opacity2 var(--transition-delay) forwards;
  614. }
  615. /* .add-item-popup > .item-content {
  616. font-size: 0.6rem;
  617. } */
  618.  
  619.  
  620. /* CONFIRM PANEL */
  621. .checkbox-icon {
  622. width: 25px;
  623. height: 25px;
  624. }
  625. .cross-icon {
  626. width: 25px;
  627. height: 25px;
  628. }
  629. .confirm-panel {
  630. margin-top: 15px;
  631. display: flex;
  632. flex-direction: row;
  633. justify-content: space-around;
  634. }
  635. .confirm-panel > label {
  636. display: flex;
  637. justify-content: center;
  638. align-items: center;
  639. cursor: pointer!important;
  640. }
  641. .confirm-panel > label > span {
  642. margin-left: var(--padding);
  643. }
  644. .confirm-panel > label > * {
  645. pointer-events: none;
  646. }
  647.  
  648.  
  649. /* ADD BUTTON */
  650. .add-button {
  651. color: var(--add-button-color);
  652. fill: var(--add-button-color);
  653. transition: color var(--transition-delay), fill var(--transition-delay);
  654. user-select: none;
  655. }
  656. .add-button:active {
  657. color: var(--add-button-active-color);
  658. fill: var(--add-button-active-color);
  659. }
  660.  
  661.  
  662. /* CANCEL BUTTON */
  663. .cancel-button {
  664. color: var(--cancel-button-color);
  665. fill: var(--cancel-button-color);
  666. transition: color var(--transition-delay), fill var(--transition-delay);
  667. user-select: none;
  668. }
  669. .cancel-button:active {
  670. color: var(--cancel-button-active-color);
  671. fill: var(--cancel-button-active-color);
  672. }
  673.  
  674.  
  675. /* CUSTOM SELECT */
  676. .custom-select {
  677. position: relative;
  678. background: var(--sub-alt-color);
  679. color: var(--text-color);
  680. width: 60px;
  681. padding: var(--padding) 5px;
  682.  
  683. user-select: none;
  684. cursor: pointer!important;
  685. font-size: 0.6rem;
  686. margin: 0 2px;
  687.  
  688. border-radius: var(--roundness);
  689. border: 1px solid;
  690. border-color: transparent;
  691. transition: border-color var(--transition-delay);
  692. }
  693. .custom-select * {
  694. cursor: pointer!important;
  695. }
  696. .custom-select:not(.custom-select-enabled):hover {
  697. border-color: var(--border-color-opacity);
  698. }
  699. .custom-select-enabled {
  700. border-radius: 0px;
  701. border-top-left-radius: var(--roundness);
  702. border-top-right-radius: var(--roundness);
  703.  
  704. border-color: var(--sub-color);
  705. border-width: 1px 1px 0 1px;
  706. margin-top: -1px;
  707. transition: none;
  708. }
  709.  
  710. .custom-select-options {
  711. display: none;
  712. position: absolute;
  713. left: -1px;
  714. right: 0;
  715. top: 100%;
  716. width: calc(100% + 2px);
  717. z-index: 11;
  718. }
  719.  
  720. .custom-select-option {
  721. padding: var(--padding) 5px;
  722. background: var(--sub-alt-color);
  723. transition: background var(--transition-delay), color var(--transition-delay);
  724.  
  725. border: solid;
  726. border-color: var(--sub-color);
  727. border-width: 0 1px 1px 1px;
  728. }
  729. .custom-select-option:hover {
  730. background: var(--sub-color);
  731. }
  732. .custom-select-option:active {
  733. background: var(--text-color);
  734. color: var(--sub-alt-color);
  735. }
  736.  
  737. .custom-select-opened {
  738. display: block;
  739. }
  740. .custom-select-opened .custom-select-option:last-child {
  741. border-bottom-left-radius: var(--roundness);
  742. border-bottom-right-radius: var(--roundness);
  743. }
  744.  
  745. .hidden {
  746. display: none;
  747. }
  748.  
  749. .opened-menu {
  750. animation: opacity var(--transition-delay) forwards;
  751. }
  752.  
  753. .closed-menu {
  754. animation: opacity2 var(--transition-delay) forwards;
  755. }
  756.  
  757. #close-menu {
  758. fill: var(--bin-color);
  759. stroke: var(--bin-color-border);
  760. stroke-width: 2px;
  761. margin-left: auto;
  762. cursor: pointer!important;
  763. }
  764. #close-menu > * {
  765. pointer-events: none;
  766. }
  767.  
  768. .manage-storage {
  769. position: relative;
  770. outline: none;
  771. border: none;
  772. background: var(--sub-alt-color);
  773. color: var(--text-color);
  774. border: 1px solid var(--border-color-opacity);
  775. transition: background var(--transition-delay), color var(--transition-delay);
  776.  
  777. padding: var(--padding);
  778. border-radius: var(--roundness);
  779. font-weight: 600;
  780. cursor: pointer!important;
  781. margin-right: var(--padding);
  782. }
  783.  
  784. .manage-storage > input {
  785. position: absolute;
  786. cursor: pointer!important;
  787. width: 100%;
  788. height: 100%;
  789. top: 0;
  790. left: 0;
  791. bottom: 0;
  792. right: 0;
  793. opacity: 0;
  794. }
  795. .manage-storage > input::-webkit-file-upload-button {
  796. cursor: pointer!important;
  797. }
  798.  
  799. .manage-storage:hover {
  800. background: var(--sub-color);
  801. }
  802. .manage-storage:active {
  803. background: var(--text-color);
  804. color: var(--sub-alt-color);
  805. }
  806. .manage-storage:last-child {
  807. margin: 0;
  808. }`;
  809.  
  810.  
  811. const IFRAMECSS = `
  812. #iframe-page-container {
  813. display: block;
  814. position: absolute;
  815. top: 50%;
  816. left: 50%;
  817. transform: translate(-50%, -50%);
  818. z-index: 50;
  819. width: 100%;
  820. height: 100%;
  821. margin: 0;
  822. padding: 0;
  823. border: none;
  824. }
  825. .hidden {
  826. display: none!important;
  827. }
  828. `;
  829.  
  830. window.addEventListener("load", function() {
  831.  
  832. const CODE = `<style>${CSS}</style>${HTML}`;
  833. const iframe = document.createElement("iframe");
  834. const blob = new Blob([CODE], {type: "text/html; charset=utf-8"});
  835. iframe.src = URL.createObjectURL(blob);
  836. iframe.id = "iframe-page-container";
  837. document.body.appendChild(iframe);
  838.  
  839. const style = document.createElement("style");
  840. style.innerHTML = IFRAMECSS;
  841. document.head.appendChild(style);
  842.  
  843.  
  844. iframe.contentWindow.addEventListener("load", function() {
  845. const iframeWindow = iframe.contentWindow;
  846. const iframeDocument = iframeWindow.document;
  847. URL.revokeObjectURL(iframe.src);
  848.  
  849. function getID(type) {
  850. if (settings.freeIDS.length) {
  851. return settings.freeIDS.pop();
  852. }
  853. return settings[TYPES[type]].length;
  854. }
  855.  
  856. function getName() {
  857. return location.hostname.replace(/\.io/, "");
  858. }
  859.  
  860. function saveSettings() {
  861. const data = JSON.stringify(settings, null, 4);
  862. const blob = new Blob([data], { type: "text/plain" });
  863. const elem = document.createElement("a");
  864. elem.href = URL.createObjectURL(blob);
  865. elem.download = `textures${getName()}.txt`;
  866. document.body.appendChild(elem);
  867. elem.click();
  868. URL.revokeObjectURL(elem.href);
  869. document.body.removeChild(elem);
  870. }
  871.  
  872. async function loadSettings(event) {
  873. try {
  874. const value = await event.target.files[0].text();
  875. const data = JSON.parse(value);
  876. Object.assign(settings, data);
  877. storage.set("TextureSettings", data);
  878. render();
  879. event.target.value = "";
  880. } catch(err) {
  881. alert("File invalid");
  882. }
  883. }
  884.  
  885. function resetSettings() {
  886. const data = createSettings();
  887. Object.assign(settings, data);
  888. storage.set("TextureSettings", settings);
  889. render();
  890. }
  891.  
  892. const pageContainer = iframeDocument.querySelector("#page-container");
  893. const openMenu = iframeDocument.querySelectorAll(".open-menu");
  894. const menuPage = iframeDocument.querySelectorAll(".menu-page");
  895. const addTexture = iframeDocument.querySelector("#add-texture");
  896. const addEmoji = iframeDocument.querySelector("#add-emoji");
  897. const textureContainer = iframeDocument.querySelector("#texture-container");
  898. const emojiContainer = iframeDocument.querySelector("#emoji-container");
  899. const closeMenu = iframeDocument.querySelector("#close-menu");
  900. const downloadSettings = iframeDocument.querySelector("#download-settings");
  901. const uploadSettings = iframeDocument.querySelector("#upload-settings");
  902. const resetSettingsData = iframeDocument.querySelector("#reset-settings");
  903.  
  904. function remove(target, className) {
  905. if (!Number.isInteger(target.length)) {
  906. target.classList.remove(className);
  907. return
  908. }
  909. for (const element of target) {
  910. element.classList.remove(className);
  911. }
  912. }
  913.  
  914. for (let i=0;i<openMenu.length;i++) {
  915. openMenu[i].onclick = function() {
  916. remove(openMenu, "enabled");
  917. openMenu[i].classList.add("enabled");
  918.  
  919. remove(menuPage, "opened");
  920. menuPage[i].classList.add("opened");
  921. }
  922. }
  923.  
  924. function removeChildren(element) {
  925. while (element.firstChild) {
  926. element.removeChild(element.lastChild);
  927. }
  928. }
  929.  
  930. function createElement(tagName, options) {
  931. const element = document.createElement(tagName);
  932. for (const key in options) {
  933. element[key] = options[key];
  934. }
  935. return element;
  936. }
  937.  
  938. // Custom select element
  939. function generateSelect(defaultValue, callback) {
  940. const customSelect = createElement("div", { className: "custom-select", tabIndex: 0 });
  941. const currentValue = createElement("span", { className: "current-value" });
  942. const customSelectOptions = createElement("div", { className: "custom-select-options" });
  943.  
  944. const options = [
  945. { value: SEARCH_TYPES.CONTAINS, label: "CONTAINS" },
  946. { value: SEARCH_TYPES.EQUALS, label: "EQUALS" }
  947. ];
  948. options[defaultValue].isDefault = true;
  949.  
  950. function close() {
  951. customSelect.classList.remove("custom-select-enabled");
  952. customSelectOptions.classList.remove("custom-select-opened");
  953. removeChildren(customSelectOptions);
  954. }
  955.  
  956. function addOptions() {
  957. removeChildren(customSelectOptions);
  958. for (const option of options) {
  959. const { value, label, isDefault } = option;
  960. if (isDefault) {
  961. currentValue.textContent = label;
  962. continue;
  963. }
  964.  
  965. const customSelectOption = createElement("div", { className: "custom-select-option" });
  966. customSelectOption.textContent = label;
  967. customSelectOptions.appendChild(customSelectOption);
  968.  
  969. customSelectOption.onclick = function() {
  970. close();
  971. currentValue.textContent = label;
  972.  
  973. options.map(option => (option.isDefault = false));
  974. option.isDefault = true;
  975. callback(option);
  976. }
  977. }
  978. }
  979. addOptions();
  980.  
  981. customSelect.appendChild(currentValue);
  982. customSelect.appendChild(customSelectOptions);
  983.  
  984. customSelect.onclick = function(event) {
  985. if (event.target.className === "custom-select-option") return;
  986. customSelect.classList.toggle("custom-select-enabled");
  987. customSelectOptions.classList.toggle("custom-select-opened");
  988. addOptions();
  989. }
  990. customSelect.onblur = close;
  991.  
  992. return customSelect;
  993. }
  994.  
  995. function getItem(options) {
  996. const item = settings[TYPES[options.type]].find(item => item.id === options.id);
  997. if (!item) throw new Error("Failed to find item");
  998. return item;
  999. }
  1000.  
  1001. function removeItem(options) {
  1002. const item = getItem(options);
  1003. const list = settings[TYPES[options.type]];
  1004. const index = list.indexOf(item);
  1005. settings.freeIDS.push(item.id);
  1006. list.splice(index, 1);
  1007. storage.set("TextureSettings", settings);
  1008. }
  1009.  
  1010. function updateItem(options) {
  1011. const item = getItem(options);
  1012. Object.assign(item, options);
  1013. storage.set("TextureSettings", settings);
  1014. }
  1015.  
  1016. function generateItem(options = {}, isPopup = false) {
  1017. const itemContent = createElement("div", { className: "item-content" });
  1018. itemContent.UploadData = Object.assign({
  1019. id: getID(options.type),
  1020. type: 0,
  1021. searchType: 0,
  1022. from: "",
  1023. to: ""
  1024. }, options);
  1025.  
  1026. const syntaxIF = createElement("div", {
  1027. className: "syntax-help syntax-if",
  1028. textContent: "IF"
  1029. });
  1030. const typeText = createElement("div", {
  1031. className: "syntax-help syntax-constructor",
  1032. textContent: TEXT_TYPES[itemContent.UploadData.type]
  1033. });
  1034. itemContent.appendChild(syntaxIF);
  1035. itemContent.appendChild(typeText);
  1036.  
  1037. const customSelect = generateSelect(itemContent.UploadData.searchType, function(option) {
  1038. itemContent.UploadData.searchType = option.value;
  1039. if (!isPopup) updateItem(itemContent.UploadData);
  1040. });
  1041. itemContent.appendChild(customSelect);
  1042.  
  1043. const inputFrom = createElement("input", {
  1044. className: "add-item-input",
  1045. type: "text",
  1046. placeholder: ". . .",
  1047. value: itemContent.UploadData.from || ""
  1048. });
  1049. inputFrom.onchange = function(event) {
  1050. itemContent.UploadData.from = event.target.value.match(/img.+$/)[0];
  1051. if (!isPopup) updateItem(itemContent.UploadData);
  1052. }
  1053. const replaceWith = createElement("div", {
  1054. className: "syntax-help syntax-method",
  1055. innerHTML: "REPLACE WITH"
  1056. });
  1057. const inputTo = createElement("input", {
  1058. className: "add-item-input",
  1059. type: "text",
  1060. placeholder: ". . .",
  1061. value: itemContent.UploadData.to || ""
  1062. });
  1063. const preview = createElement("div", {
  1064. className: "imageHolder hidden",
  1065. innerHTML: `<img src="" draggable="false"/>`
  1066. });
  1067.  
  1068. async function setSrc(src) {
  1069. preview.classList.add("hidden");
  1070. if (!isURL(src)) return;
  1071. const isValid = await isValidImageSrc(src);
  1072. const child = preview && preview.firstChild;
  1073. if (isValid && child && src !== child.src) {
  1074. preview.classList.remove("hidden");
  1075. child.src = src;
  1076. }
  1077. }
  1078. setSrc(itemContent.UploadData.to);
  1079.  
  1080. inputTo.oninput = function(event) {
  1081. const value = event.target.value;
  1082. setSrc(value);
  1083. }
  1084. inputTo.onchange = function(event) {
  1085. itemContent.UploadData.to = event.target.value;
  1086. if (!isPopup) updateItem(itemContent.UploadData);
  1087. }
  1088. if (!isPopup) {
  1089. const deleteItem = createElement("div", {
  1090. className: "delete-item",
  1091. innerHTML: `
  1092. <svg
  1093. class="icon-tx bin-icon"
  1094. version="1.1"
  1095. xmlns="http://www.w3.org/2000/svg"
  1096. width="32" height="32" viewBox="0 0 32 32"
  1097. >
  1098. <path d="M4 10v20c0 1.1 0.9 2 2 2h18c1.1 0 2-0.9 2-2v-20h-22zM10 28h-2v-14h2v14zM14 28h-2v-14h2v14zM18 28h-2v-14h2v14zM22 28h-2v-14h2v14z"></path>
  1099. <path d="M26.5 4h-6.5v-2.5c0-0.825-0.675-1.5-1.5-1.5h-7c-0.825 0-1.5 0.675-1.5 1.5v2.5h-6.5c-0.825 0-1.5 0.675-1.5 1.5v2.5h26v-2.5c0-0.825-0.675-1.5-1.5-1.5zM18 4h-6v-1.975h6v1.975z"></path>
  1100. </svg>
  1101. `
  1102. });
  1103. deleteItem.onclick = function() {
  1104. if (itemContent.classList.contains("popup-to-remove")) return;
  1105. itemContent.classList.add("popup-to-remove");
  1106. removeItem(itemContent.UploadData);
  1107. setTimeout(() => itemContent.remove(), 250);
  1108. }
  1109. itemContent.appendChild(deleteItem);
  1110. }
  1111. itemContent.appendChild(inputFrom);
  1112. itemContent.appendChild(replaceWith);
  1113. itemContent.appendChild(inputTo);
  1114. if (itemContent.UploadData.type === DATA_TYPES.TEXTURES) itemContent.appendChild(preview);
  1115.  
  1116. return itemContent;
  1117. }
  1118.  
  1119. function addItem(item, options = {}) {
  1120. const container = [textureContainer, emojiContainer][options.type];
  1121. container.appendChild(item);
  1122. }
  1123.  
  1124. function render() {
  1125. removeChildren(textureContainer);
  1126. // removeChildren(emojiContainer);
  1127.  
  1128. for (const key of Object.values(TYPES)) {
  1129. for (const options of settings[key]) {
  1130. const newItem = generateItem(options);
  1131. addItem(newItem, options);
  1132. }
  1133. }
  1134. }
  1135. render();
  1136.  
  1137. function generatePopup(type) {
  1138. const popupContainer = createElement("div", { className: "popup-container" });
  1139. const addItemPopup = createElement("div", { className: "add-item-popup" });
  1140. const popupHeader = createElement("p", { className: "popup-header", textContent: HEADER_TYPES[type] });
  1141. addItemPopup.appendChild(popupHeader);
  1142.  
  1143. const item = generateItem({ type }, true);
  1144. addItemPopup.appendChild(item);
  1145. const confirmPanel = createElement("div", { className: "confirm-panel" });
  1146. const addButton = createElement("label", {
  1147. className: "add-button",
  1148. innerHTML: `
  1149. <svg
  1150. class="checkbox-icon"
  1151. version="1.1"
  1152. xmlns="http://www.w3.org/2000/svg"
  1153. width="32" height="32" viewBox="0 0 32 32"
  1154. >
  1155. <path d="M28 0h-24c-2.2 0-4 1.8-4 4v24c0 2.2 1.8 4 4 4h24c2.2 0 4-1.8 4-4v-24c0-2.2-1.8-4-4-4zM14 24.828l-7.414-7.414 2.828-2.828 4.586 4.586 9.586-9.586 2.828 2.828-12.414 12.414z"></path>
  1156. </svg>
  1157. <span>CONFIRM</span>
  1158. `
  1159. });
  1160. const cancelButton = createElement("label", {
  1161. className: "cancel-button",
  1162. innerHTML: `
  1163. <svg
  1164. class="cross-icon"
  1165. version="1.1"
  1166. xmlns="http://www.w3.org/2000/svg"
  1167. width="32" height="32" viewBox="0 0 32 32"
  1168. >
  1169. <path d="M31.708 25.708c-0-0-0-0-0-0l-9.708-9.708 9.708-9.708c0-0 0-0 0-0 0.105-0.105 0.18-0.227 0.229-0.357 0.133-0.356 0.057-0.771-0.229-1.057l-4.586-4.586c-0.286-0.286-0.702-0.361-1.057-0.229-0.13 0.048-0.252 0.124-0.357 0.228 0 0-0 0-0 0l-9.708 9.708-9.708-9.708c-0-0-0-0-0-0-0.105-0.104-0.227-0.18-0.357-0.228-0.356-0.133-0.771-0.057-1.057 0.229l-4.586 4.586c-0.286 0.286-0.361 0.702-0.229 1.057 0.049 0.13 0.124 0.252 0.229 0.357 0 0 0 0 0 0l9.708 9.708-9.708 9.708c-0 0-0 0-0 0-0.104 0.105-0.18 0.227-0.229 0.357-0.133 0.355-0.057 0.771 0.229 1.057l4.586 4.586c0.286 0.286 0.702 0.361 1.057 0.229 0.13-0.049 0.252-0.124 0.357-0.229 0-0 0-0 0-0l9.708-9.708 9.708 9.708c0 0 0 0 0 0 0.105 0.105 0.227 0.18 0.357 0.229 0.356 0.133 0.771 0.057 1.057-0.229l4.586-4.586c0.286-0.286 0.362-0.702 0.229-1.057-0.049-0.13-0.124-0.252-0.229-0.357z"></path>
  1170. </svg>
  1171. <span>CANCEL</span>
  1172. `
  1173. });
  1174. confirmPanel.appendChild(addButton);
  1175. confirmPanel.appendChild(cancelButton);
  1176. addItemPopup.appendChild(confirmPanel);
  1177. popupContainer.appendChild(addItemPopup);
  1178.  
  1179. function close(event) {
  1180. if (popupContainer.classList.contains("popup-to-remove")) return;
  1181. if (![popupContainer, cancelButton, addButton].includes(event.target)) return;
  1182.  
  1183. popupContainer.classList.add("popup-to-remove");
  1184. setTimeout(() => popupContainer.remove(), 250);
  1185. }
  1186.  
  1187. cancelButton.onclick = close;
  1188. popupContainer.onclick = close;
  1189.  
  1190. addButton.onclick = function(event) {
  1191. if (event.target !== addButton) return;
  1192.  
  1193. const newItem = generateItem(item.UploadData);
  1194. addItem(newItem, newItem.UploadData);
  1195. settings[TYPES[type]].push(newItem.UploadData);
  1196. storage.set("TextureSettings", settings);
  1197. close(event);
  1198. }
  1199.  
  1200. return popupContainer;
  1201. }
  1202.  
  1203. addTexture.onclick = function() {
  1204. pageContainer.appendChild(generatePopup(DATA_TYPES.TEXTURES));
  1205. }
  1206.  
  1207. /* addEmoji.onclick = function() {
  1208. pageContainer.appendChild(generatePopup(DATA_TYPES.EMOJIS));
  1209. } */
  1210.  
  1211. downloadSettings.onclick = saveSettings;
  1212. uploadSettings.onchange = loadSettings;
  1213. resetSettingsData.onclick = resetSettings;
  1214. function toggleMenu() {
  1215. const list = pageContainer.classList;
  1216. list.toggle("closed-menu");
  1217. list.toggle("opened-menu");
  1218. setTimeout(() => {
  1219. list.toggle("hidden");
  1220. iframe.classList.toggle("hidden");
  1221. }, 250);
  1222. }
  1223.  
  1224. closeMenu.onclick = toggleMenu;
  1225.  
  1226. function handleKeydown(event) {
  1227. if (event.code === "Escape") {
  1228. event.preventDefault();
  1229. if (event.repeat) return;
  1230. toggleMenu();
  1231. }
  1232. }
  1233. window.addEventListener("keydown", handleKeydown);
  1234. iframeWindow.addEventListener("keydown", handleKeydown);
  1235. })
  1236. })
  1237.  
  1238. })();