Cookie Clicker Mod Menu

Mod menu for Cookie Clicker

当前为 2022-08-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Cookie Clicker Mod Menu
  3. // @namespace https://github.com/qba210/cookie-clicker-mod-menu
  4. // @version 1.0
  5. // @description Mod menu for Cookie Clicker
  6. // @author qba210
  7. // @license MIT
  8. // @match *://orteil.dashnet.org/cookieclicker/*
  9. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAqCAMAAAD26OlUAAAAolBMVEU0DAIKBQhqIg8A3v9DLSpuV1URLT9FARDDkzh+ernp1nP///+QgHeMAACpdiE0FRfBqIyov5EAhZXwFwC7XzaDQBTUwbxBTn1NKiPg4OAXPiCaMxaauDTdtGaVXjnKEwr/fgAAWZ5Sb03Fh1D/ieeWABzUQQ3/zC86gSvtEG/cQWKgANXMs6z67bnhwJalQh9g/1B+WkBJDQPKa0G9jnkAAADDKDlIAAAANnRSTlP//////////////////////////////////////////////////////////////////////wChj04xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGx2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDIgNzkuMTY0NDg4LCAyMDIwLzA3LzEwLTIyOjA2OjUzICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIxLjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMi0wOC0wN1QyMDo1NToxNCswMjowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjItMDgtMDdUMjE6MDA6MjgrMDI6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjItMDgtMDdUMjE6MDA6MjgrMDI6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OWRkM2RiMTEtNWViZS01MzQzLWEyOGEtYzAwZDE2MDY3MWFhIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6MGUxNzYxNGUtOTkxNy1hMzRmLTllYWYtYzM4ZTdlYjY2MWIwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6RjVDMTE2MERDQ0NDMTFFQzlBOThCQzEzREI1QkEzRjUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIyIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGNUMxMTYwQUNDQ0MxMUVDOUE5OEJDMTNEQjVCQTNGNSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGNUMxMTYwQkNDQ0MxMUVDOUE5OEJDMTNEQjVCQTNGNSIvPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpiNWRkNTg4NS1jNjA4LWEwNGEtYTYyMi04OTM4NjNlYjcwNGEiIHN0RXZ0OndoZW49IjIwMjItMDgtMDdUMjE6MDA6MjgrMDI6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMi4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6OWRkM2RiMTEtNWViZS01MzQzLWEyOGEtYzAwZDE2MDY3MWFhIiBzdEV2dDp3aGVuPSIyMDIyLTA4LTA3VDIxOjAwOjI4KzAyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjIuMCAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+nkWItQAAANxJREFUOMut1MEOgjAQBFDaY016bbJfsMde+P9fk3GyLg2obHESPcgjw2LbZd0isv7MEoKq/4Qiqr2rWn16Zxbuq3EZN5aCG0ccg6wFqlWVEAGOQ6+1UtWUVB9b9vQ6dAZEiG9Cp3HIy3glPgw4fotDGwSo95TKEJbPQlB8PPegDTPCWu9AG8AWBZf0DGyNfyGTEgAfRKS1OLRFa7U+ksjJ6rkIUT4uNnmFxXHotFY8hCVnMGyXOCRtDdBKWcvNPAN5pOSMSiKOwQ09B4/Hnh+K8/CYD9U34Mnr+QafGozjyeDrtEIAAAAASUVORK5CYII=
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. class Logger {
  17. /**
  18. *
  19. * @param {object} msg
  20. */
  21. static LogInfo(msg) {
  22. console.log("\n[Mod Menu] " + msg);
  23. }
  24.  
  25. /**
  26. *
  27. * @param {object} msg
  28. */
  29. static LogDebug(msg) {
  30. this.LogInfo(msg);
  31. }
  32. }
  33.  
  34. let cheatMenu = document.createElement("div");
  35.  
  36. const translations = [
  37. {
  38. lang: "pl",
  39. langName: "Polski",
  40. hacks: {
  41. "silent-mode": {
  42. name: "Tryb cichy",
  43. desc: "Unika wykrycia przez grę hackowania (np. osiągnięcie 'Oszukane ciastka smakują najgorzej')"
  44. },
  45. "cookie-spam": {
  46. name: "Spamowanie ciastka",
  47. desc: "Po przyciśnięciu ciastka samoczynnie zaczyna na nie klikać (kończy po odciśnięciu)"
  48. },
  49. "dev-tools": {
  50. name: "Menu dewelop.",
  51. desc: "Otwiera menu deweloperskie"
  52. },
  53. "set-cookies": {
  54. name: "Ustaw ilość ciastek",
  55. desc: "Pozwala na zmianę ilości ciastek"
  56. },
  57. "delete-save": {
  58. name: "Usuń zapis"
  59. },
  60. "earn-achievement": {
  61. name: "Odblokuj osiągnięcie",
  62. desc: "Wybierz osiągnięcie z listy i kliknij przycisk aby je zdobyć!"
  63. }
  64. },
  65. strings: {
  66. "dev-tools-confirm": "Jesteś pewien? Tryb cichy nie uchroni Cię od hacków które włączycz w menu deweloperskim.",
  67. "changes-as-you-type": "Wartość zmienia się jak piszesz",
  68. "confirm-save-delete": "Jesteś pewien?"
  69. }
  70. },
  71. {
  72. lang: "en",
  73. langName: "English",
  74. hacks: {
  75. "silent-mode": {
  76. name: "Silent mode",
  77. desc: "Avoids detecting hacks by game (ex.: achievement 'Cheated cookies tastes awful')"
  78. },
  79. "cookie-spam": {
  80. name: "Cookie spam",
  81. desc: "After holding the cookie, starts to click it automatically (ends after releasing)"
  82. },
  83. "dev-tools": {
  84. name: "Dev menu",
  85. desc: "Opens developer menu"
  86. },
  87. "set-cookies": {
  88. name: "Set cookie count",
  89. desc: "Allows you to change cookie count"
  90. },
  91. "delete-save": {
  92. name: "Delete save"
  93. },
  94. "earn-achievement": {
  95. name: "Earn achievement",
  96. desc: "Select achievement from list, then click button to get it!"
  97. }
  98. },
  99. strings: {
  100. "dev-tools-confirm": "Are you sure? Silent mode cannot prevent detecting cheats you activate in developer menu.",
  101. "changes-as-you-type": "Changes as you type",
  102. "confirm-save-delete": "Are you sure?"
  103. }
  104. }
  105. ]
  106.  
  107. let lang = translations.find((lng) => lng.lang === (localStorage.getItem("cheats_lang") ?? "en"));
  108.  
  109. document.body.append(cheatMenu);
  110.  
  111. cheatMenu.outerHTML = `
  112. <div id="hack-menu" style="left: 10px; top: 35px;">
  113. <div id="hack-popup">Hack</div>
  114. <br>
  115. <div id="hacks" style="visibility: hidden;">
  116. <select id="hack-lang-select"></select>
  117. <div class="hack hack-bool" id="hack-silent-mode"></div>
  118. <div class="hack hack-bool" id="hack-cookie-spam"></div>
  119. <div class="hack hack-btn" id="hack-dev-tools"></div>
  120. <div class="hack hack-btn" id="hack-set-cookies"></div>
  121. <div class="hack hack-btn" id="hack-delete-save"></div>
  122. <div class="hack hack-select" id="hack-earn-achievement"></div>
  123. </div>
  124. </div>
  125. <div id="hack-tooltip" style="opacity: 0;left:0;top:0"></div>
  126. <div id="hack-alert-input" style="opacity: 0;visibility: hidden;">
  127. <div id="hack-alert-input-popup">
  128. <h1 id="hack-alert-input-popup-title"></h1>
  129. <div id="hack-alert-input-popup-desc"></div><br>
  130. <input type="number" id="hack-alert-input-popup-input"/><br>
  131. <input type="button" id="hack-alert-input-popup-ok" value="OK"/>
  132. </div>
  133. </div>
  134. `;
  135. let styles = document.createElement("style");
  136. styles.innerText = `
  137. #hack-menu, #hack-tooltip {
  138. z-index: 9000000000;
  139. position: absolute;
  140. box-sizing: border-box;
  141. }
  142. #hack-menu, #hack-menu *:not(#hacks){
  143. box-sizing: border-box;
  144. }
  145. #hack-menu {
  146. display: flex;
  147. flex-direction: column;
  148. align-items: center;
  149. width: clamp(150px, 225px, 25vw);
  150. }
  151. #hacks {
  152. backdrop-filter: blur(5px);
  153. display: flex;
  154. flex-direction: column;
  155. gap: 10px;
  156. padding: 10px;
  157. background-color: rgba(0, 0, 0, .5);
  158. border-radius: 13px;
  159. width: 100%;
  160. box-sizing: content-box;
  161. }
  162. #hack-popup, .hack {
  163. padding: 10px;
  164. font-size: 20px;
  165. background-color: #00a2ff;
  166. border: 7px solid black;
  167. text-align: center;
  168. width: 100%;
  169. }
  170. .hack {
  171. cursor: pointer;
  172. }
  173. .hack:active {
  174. filter: brightness(0.85);
  175. }
  176.  
  177. .hack-bool {
  178. transition: background-color .3s ease-in;
  179. background-color: red;
  180. }
  181. .hack-bool[on] {
  182. background-color: green;
  183. }
  184. #hack-tooltip {
  185. position: absolute;
  186. backdrop-filter: blur(7px);
  187. filter: blur(0px);
  188. transition: all .3s ease-out, left 0s ease, top 0s ease;
  189. background-color: rgba(0, 0, 0, .5);
  190. color: white;
  191. transform: translate(15px, 9.5px);
  192. width: 200px;
  193. padding: 10px;
  194. border-radius: 13px;
  195. pointer-events: none;
  196. }
  197. #hack-lang-select {
  198. z-index: 90000000001;
  199. background-color: black;
  200. color: white;
  201. border-color: white;
  202. width: 100%
  203. }
  204. #hack-lang-select > option {
  205. color: white;
  206. }
  207.  
  208. #hack-popup {
  209. transition: opacity .35s ease-out;
  210. }
  211. #hack-popup:hover, #hack-popup[open] {
  212. opacity: 1;
  213. }
  214. #hack-popup:not(:hover):not([open]) {
  215. opacity: .8;
  216. }
  217.  
  218. #hack-alert-input {
  219. z-index: 9500000000;
  220. background-color: rgba(0, 0, 0, .5);
  221. transition: opacity .3s ease-out;
  222. position: absolute;
  223. left: 0;
  224. top: 0;
  225. right: 0;
  226. bottom: 0;
  227. backdrop-filter: blur(5px);
  228. }
  229. #hack-alert-input-popup {
  230. position: absolute;
  231. left: 50%;
  232. top: 50%;
  233. transform: translate(-50%, -50%);
  234. width: 60%;
  235. height: 50%;
  236. background-color: black;
  237. color: white;
  238. display: flex;
  239. flex-direction: column;
  240. justify-content: center;
  241. font-family: verdana;
  242. text-align: center;
  243. padding: 20px;
  244. border: 3px solid white;
  245. border-radius: 10px;
  246. }
  247. #hack-alert-input-popup-title {
  248. font-size: 2.5em;
  249. }
  250. #hack-alert-input-popup-desc {
  251. font-size: 1.5em;
  252. }
  253. #hack-alert-input-popup-ok {
  254. background-color: #00a2ff;
  255. border: 4px solid white;
  256. text-align: center;
  257. width: 100%;
  258. height: 50px;
  259. font-weight: bold;
  260. cursor: pointer;
  261. }
  262. #hack-alert-input-popup-input {
  263. background-color: black;
  264. color: white;
  265. border-color: white;
  266. }
  267. .select-in-hack {
  268. width: 100%;
  269. border-color: black;
  270. color: white;
  271. background-color: black;
  272. }
  273.  
  274. `;
  275. document.head.append(styles);
  276.  
  277. if (!localStorage.getItem("cheats_lang")) {
  278. localStorage.setItem("cheats_lang", "en");
  279. }
  280. let $hackmenu = document.getElementById("hack-menu");
  281. let $hacks = document.getElementById("hacks");
  282. let $popup = document.getElementById("hack-popup");
  283. let $tooltip = document.getElementById("hack-tooltip");
  284. /**@type {HTMLSelectElement} */
  285. let $langselect = document.getElementById("hack-lang-select");
  286. let $hack_earnachievement = document.getElementById("hack-earn-achievement")
  287.  
  288. let $inputalert = {
  289. alert: document.getElementById("hack-alert-input"),
  290. popup: document.getElementById("hack-alert-input-popup"),
  291. title: document.getElementById("hack-alert-input-popup-title"),
  292. desc: document.getElementById("hack-alert-input-popup-desc"),
  293. input: document.getElementById("hack-alert-input-popup-input"),
  294. ok: document.getElementById("hack-alert-input-popup-ok")
  295. }
  296.  
  297. let $cookie = document.getElementById("bigCookie");
  298.  
  299. reloadLangs();
  300.  
  301. dragElement($hackmenu, $popup);
  302.  
  303. //boolean hack manager
  304. $hacks.querySelectorAll(".hack-bool").forEach(node => {
  305. node.addEventListener("click", (e) => {
  306. if (node.hasAttribute("on")) {
  307. node.removeAttribute("on");
  308. } else {
  309. node.setAttribute("on", "");
  310. }
  311. })
  312. })
  313.  
  314. //class loop
  315. $hackmenu.querySelectorAll(".hack").forEach(_node => {
  316. /**@type {HTMLElement} */
  317. let node = _node;
  318.  
  319. //tooltip
  320. node.addEventListener("mousemove", function (e) {
  321. if (e.target === this) {
  322. showTooltip(node.id, e.pageX, e.pageY);
  323. } else if (e.target instanceof HTMLSelectElement) {
  324. if (e.target.value && e.target.value !== "") {
  325. showTooltip(Array.from(this.querySelectorAll("option")).find(opt => opt.value === e.target.value).innerHTML, e.pageX, e.pageY, false);
  326. }
  327. } else {
  328. hideTooltip();
  329. }
  330. })
  331. node.addEventListener("mouseleave", (e) => {
  332. hideTooltip();
  333. })
  334. })
  335.  
  336. //menu hide/show
  337. $popup.addEventListener("click", (e) => {
  338. if ($hacks.style.visibility === "hidden") {
  339. $hacks.style.visibility = "visible";
  340. $popup.setAttribute("open", "");
  341. } else {
  342. $hacks.style.visibility = "hidden";
  343. $popup.removeAttribute("open");
  344. }
  345. })
  346.  
  347. //add event listener to language select
  348. $langselect.addEventListener("change", (e) => {
  349. setLang($langselect.value);
  350. })
  351.  
  352. //select language
  353. translations.forEach((translation) => {
  354. let option = document.createElement("option");
  355. option.value = translation.lang;
  356. option.innerText = translation.langName;
  357. $langselect.append(option);
  358. Logger.LogInfo(`Loaded language ${translation.lang}: ${translation.langName}`);
  359. })
  360.  
  361. //set current language
  362. $langselect.value = lang.lang;
  363.  
  364. // Debug menu hack
  365. document.getElementById("hack-dev-tools").addEventListener("click", (e) => {
  366. if (isSilentMode()) {
  367. if (confirm(lang.strings["dev-tools-confirm"])) {
  368. Game.OpenSesame();
  369. }
  370. } else {
  371. Game.OpenSesame();
  372. }
  373. })
  374.  
  375. // Cookie spam hack
  376. let cookieSpamWorker;
  377.  
  378. $cookie.addEventListener("mousedown", (e) =>
  379. {
  380. if (document.getElementById("hack-cookie-spam").hasAttribute("on"))
  381. cookieSpamWorker = setInterval(() => $cookie.dispatchEvent(new Event("click")));
  382. })
  383.  
  384. $cookie.addEventListener("mouseup", (e) => {
  385. clearInterval(cookieSpamWorker);
  386. })
  387.  
  388. // Set cookies hack
  389. document.getElementById("hack-set-cookies").addEventListener("click", (e) => {
  390. //show input to user
  391. showInputAlert(lang.hacks["set-cookies"].name, lang.strings["changes-as-you-type"], "number", Game.cookies.toString(), (val) => {
  392. //earn mode decided by silent mode
  393. if (isSilentMode()) {
  394. Game.Earn(-Game.cookies + +val);
  395. } else {
  396. Game.cookies = +val;
  397. }
  398. })
  399. });
  400.  
  401. // Delete save hack
  402. document.getElementById("hack-delete-save").addEventListener("click", (e) => {
  403. //ask user to confirm
  404. if (confirm(lang.strings["confirm-save-delete"])) {
  405. //delete save from localstorage
  406. localStorage.removeItem("CookieClickerGame");
  407. //after this reload to confirm
  408. window.location.reload();
  409. }
  410. });
  411. //earn achievemenr hack
  412. $hack_earnachievement.addEventListener("click", function (e) {
  413. //if clicked on select dont gain achevement
  414. if (e.target !== this) return;
  415. //gain selected achievement
  416. Game.Win($hack_earnachievement.querySelector("select").value)
  417. })
  418.  
  419. /**
  420. * @param {string} lang
  421. */
  422.  
  423. function setLang(lang) {
  424. localStorage.setItem("cheats_lang", lang);
  425. reloadLangs();
  426. }
  427.  
  428. function reloadLangs() {
  429. lang = translations.find((lng) => lng.lang === (localStorage.getItem("cheats_lang") ?? "en"))
  430.  
  431. //for every hack
  432. $hackmenu.querySelectorAll(".hack").forEach(node => {
  433. //add value
  434. node.innerText = (lang.hacks[node.id.replace("hack-", "")] ?? {name: node.id} ).name ?? node.id
  435. })
  436.  
  437. //support for select class
  438. $hackmenu.querySelectorAll(".hack-select").forEach(node => {
  439. let select = document.createElement("select");
  440. select.className = "select-in-hack";
  441. node.append(select);
  442. })
  443.  
  444. // Earn achevement hack
  445.  
  446. //when Game is avaible do
  447. new Promise((res) => {var int = setInterval(() => {if (Game.Achievements) {clearInterval(int); res(Game);}}, 10)}).then(() => {
  448. // foreach achevement ingame
  449. Object.entries(Game.Achievements).forEach((entry) => {
  450. const [key, value] = entry;
  451. //create option with achievement
  452. let option = document.createElement("option");
  453. option.value = key;
  454. option.innerHTML = value.dname;
  455. //add it to hack
  456. $hack_earnachievement.querySelector("select").append(option);
  457. });
  458. })
  459.  
  460. //set current language
  461. $langselect.value = lang.lang;
  462. }
  463.  
  464. function isSilentMode() {
  465. return document.getElementById("hack-silent-mode").hasAttribute("on");
  466. }
  467.  
  468.  
  469. /**
  470. *
  471. * @param {string} id
  472. * @param {number} x
  473. * @param {number} y
  474. */
  475. function showTooltip(id, x, y, translatable = true) {
  476. //show only if id have description
  477. if (translatable) {
  478. if (!lang.hacks[id.replace("hack-", "")]) return;
  479. if (!lang.hacks[id.replace("hack-", "")].desc) return;
  480. }
  481.  
  482. $tooltip.innerText = translatable ? lang.hacks[id.replace("hack-", "")].desc ?? "" : id;
  483. $tooltip.style.left = x.toString() + "px";
  484. $tooltip.style.top = y.toString() + "px";
  485. $tooltip.style.opacity = 1;
  486. }
  487.  
  488. function hideTooltip() {
  489. //$tooltip.innerText = ""; //its needed to be comented because transistion
  490. $tooltip.style.opacity = 0;
  491. }
  492.  
  493. /**
  494. * Shows input alert.
  495. *
  496. * @param {string} title
  497. * @param {string} desc
  498. * @param {string} type
  499. * @param {string} value
  500. * @param {(value: string) => void} setter
  501. */
  502.  
  503. function showInputAlert(title, desc, type, value, setter) {
  504. $inputalert.ok.onclick = () => {
  505. setTimeout(() => $inputalert.alert.style.visibility = "hidden", 301);
  506. $inputalert.alert.style.opacity = "0";
  507. }
  508.  
  509. $inputalert.title.innerText = title;
  510. $inputalert.desc.innerText = desc;
  511.  
  512. $inputalert.input.type = type;
  513. $inputalert.input.value = value.toString();
  514. $inputalert.input.onchange = (e) => setter($inputalert.input.value);
  515.  
  516. $inputalert.alert.style.visibility = "visible";
  517. $inputalert.alert.style.opacity = "1";
  518. }
  519.  
  520. /**
  521. *
  522. * @param {HTMLElement} elmnt element to drag
  523. * @param {HTMLElement?} dragger element dragging elmnt
  524. */
  525. function dragElement(elmnt, dragger) {
  526. var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  527. if (document.getElementById(elmnt.id + "header") || dragger) {
  528. // if present, the header is where you move the DIV from:
  529. (document.getElementById(elmnt.id + "header") ?? dragger).onmousedown = dragMouseDown;
  530. } else {
  531. // otherwise, move the DIV from anywhere inside the DIV:
  532. elmnt.onmousedown = dragMouseDown;
  533. }
  534. function dragMouseDown(e) {
  535. e = e || window.event;
  536. e.preventDefault();
  537. // get the mouse cursor position at startup:
  538. pos3 = e.clientX;
  539. pos4 = e.clientY;
  540. document.onmouseup = closeDragElement;
  541. // call a function whenever the cursor moves:
  542. document.onmousemove = elementDrag;
  543. if (document.getElementById(elmnt.id + "header") || dragger) {
  544. (document.getElementById(elmnt.id + "header") ?? dragger).setAttribute("dragging", "");
  545. }else {
  546. elmnt.setAttribute("dragging", "");
  547. }
  548. }
  549. function elementDrag(e) {
  550. e = e || window.event;
  551. e.preventDefault();
  552. // calculate the new cursor position:
  553. pos1 = pos3 - e.clientX;
  554. pos2 = pos4 - e.clientY;
  555. pos3 = e.clientX;
  556. pos4 = e.clientY;
  557. // set the element's new position:
  558. elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
  559. elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  560. }
  561. function closeDragElement() {
  562. // stop moving when mouse button is released:
  563. document.onmouseup = null;
  564. document.onmousemove = null;
  565.  
  566. if (document.getElementById(elmnt.id + "header") || dragger) {
  567. (document.getElementById(elmnt.id + "header") ?? dragger).removeAttribute("dragging");
  568. }else {
  569. elmnt.removeAttribute("dragging");
  570. }
  571. }
  572. }
  573. })();