diff --git a/plugins/charcount.js b/plugins/charcount.js new file mode 100644 index 0000000..ce6bf17 --- /dev/null +++ b/plugins/charcount.js @@ -0,0 +1,52 @@ +function setupCharCount() { + if (document.getElementById("charcounter")) return; + if (!document.querySelector("textarea[class*=\"textAreaEnabled-\"]")) return; + + let charcount = document.createElement("span"); + charcount.id = "charcounter"; + charcount.innerHTML = "0/2000"; + charcount.style.right = "40px"; + charcount.style.bottom = "4px"; + charcount.style.opacity = "0.5"; + charcount.style.position = "absolute"; + charcount.style.display = "block"; + charcount.style["font-size"] = "85%"; + + document.querySelector("div[class*=\"channelTextAreaEnabled-\"]").appendChild(charcount); +} + +exports = { + meta: { + author: "Cynosphere", + name: "Character Counter", + desc: "Counts characters in the chatbox." + }, + start: function(){ + let charcount_mo = new MutationObserver(setupCharCount); + charcount_mo.observe(document.querySelector("div[class*=\"app-\"]"), { + childList: true, + subtree: true + }); + + document.addEventListener("keydown", ev => { + if (!document.getElementById("charcounter") || !document.querySelector("textarea[class*=\"textAreaEnabled-\"]")) return; + + setTimeout(()=>{ + let length = document.querySelector("textarea[class*=\"textAreaEnabled-\"]").value.length; + document.getElementById("charcounter").innerHTML = `${length}/2000`; + + if (length > 1999) { + document.getElementById("charcounter").style.color = "#FF0000"; + } else if (length > 1899) { + document.getElementById("charcounter").style.color = "#FF4500"; + } else if (length > 1499) { + document.getElementById("charcounter").style.color = "#FFA500"; + } else if (length > 999) { + document.getElementById("charcounter").style.color = "#F1C40F"; + } else { + document.getElementById("charcounter").style.color = "#FFFFFF"; + } + }, 50); + }); + } +} \ No newline at end of file diff --git a/plugins/dblclickedit.js b/plugins/dblclickedit.js new file mode 100644 index 0000000..7722d64 --- /dev/null +++ b/plugins/dblclickedit.js @@ -0,0 +1,20 @@ +exports.meta = { + author: "Cynosphere, Jiiks", + name: "Double Click Edit", + desc: "Double click messages to edit them." +} +exports.start = function(){ + document.addEventListener("dblclick", ev => { + let target = ev.target; + if(target.className.includes("markup")) { + let msg = target; + let opt = msg.parentNode.querySelector(".btn-option"); + opt.click(); + let options = document.querySelectorAll(".option-popout .btn-item"); + for(i in options){ + let o = options[i]; + if(o.innerHTML == "Edit") o.click(); + } + } + }); +} \ No newline at end of file diff --git a/plugins/fixmentions.js b/plugins/fixmentions.js new file mode 100644 index 0000000..9b1ec02 --- /dev/null +++ b/plugins/fixmentions.js @@ -0,0 +1,14 @@ +exportsexports = { + meta: { + author: "Cynosphere, Adryd", + name: "Mentions Fixer", + desc: "See hidden channel mentions and remove #deleted-channel." + }, + replacements: { + '/t\\.type===(.+)\\.ChannelTypes\\.GUILD_TEXT&&(.+)\\.default\\.can\\((.+)\\.Permissions\\.VIEW_CHANNEL,n,t\\)/':'true', + '/"#deleted-channel"/':'"<#"+e[1]+">"', + '/"@deleted-role"/':'"<@&"+e[1]+">"', + '/guildId:null!=t\\?t\\.guild_id:null,/':'guildId:null!=t.guild_id?t.guild_id:"@me",', + '/"#deleted-channel"/':'"<#"+e[1]+">"' + } +} \ No newline at end of file diff --git a/plugins/imgxis.js b/plugins/imgxis.js new file mode 100644 index 0000000..2ebc5cc --- /dev/null +++ b/plugins/imgxis.js @@ -0,0 +1,452 @@ +/* +IMGXIS made by YellowAfterlife. http://yellowafterlife.itch.io/imgxis (mirror: https://github.com/Cynosphere/IMGXIS) +Ported by Jane (maybejane/statefram) and Cynthia (BoxOfFlex/Cynosphere) to a private client mod. +Re-ported by Cynthia to EndPwn. +*/ +exports.meta = { + author: "Cynosphere, YellowAfterlife", + name: "IMGXIS", + desc: "A better way to view images." +} +exports.start = function(){ + var globalPanner; + var values = {}; + + /*function _hook() { + const imageWrapper = document.querySelectorAll( + 'div[class^="modal-"] div[class^="inner-"] div div[class^="imageWrapper-"]' + )[0]; + const image = imageWrapper.children[0].className == "imgxis-panner" ? imageWrapper.children[0].children[0] : imageWrapper.children[0]; + if (imageWrapper && image) { + onImageOpen(image); + } else { + onImageClose(globalPanner); + } + } + window._hook = _hook;*/ + + function onLoad() { + var css = document.createElement("style"); + css.type = "text/css"; + css.id = "css-imgxis"; + var menuButtonWidth = 32; // in pixels + css.innerHTML = [ + "body {", + " background-image: none;", + " overflow: hidden;", + "}", + // panner area + ".imgxis-panner {", + " position: absolute;", + " left: 0; width: 100%;", + " top: 0; height: 100%;", + "}", + // cursors + ".imgxis-panner, .imgxis-panner img {", + " cursor: move;", + "}", + ".imgxis-panner.colorpick, .imgxis-panner.colorpick img {", + " cursor: default;", + " cursor: copy;", + "}", + // disable interpolation: + ".imgxis-panner.zoomed, .imgxis-panner.zoomed img {", + " -ms-interpolation-mode: nearest-neighbor;", + " image-rendering: optimizeSpeed;", + " image-rendering: -moz-crisp-edges;", + " image-rendering: -webkit-optimize-contrast;", + " image-rendering: -o-crisp-edges;", + " image-rendering: pixelated;", + "}", + // actual image: + ".imgxis-panner img {", + // disable Firefox' centering: + " position: relative;", + " margin: 0;", + "}", + // menu pane on top: + ".imgxis-menu {", + " position: absolute;", + " left: 0; width: 100%;", + " top: 0; height: 24px;", + " background: rgba(0, 0, 0, 0.7);", + " padding: 4px;", + " color: white;", + " font: 16px sans-serif;", + " line-height: 24px;", + " min-width: 1024px;", + "}", + // style the buttons in the menu: + ".imgxis-menu input {", + ` width: ${menuButtonWidth}px;`, + " height: 24px;", + " padding: 0px;", + " margin-right: 2px;", + " float: left;", + "}" + ].join("\n"); + document.head.appendChild(css); + } + onLoad(); + + function onImageOpen(element) { + let wrap = document.querySelector('div[class*="imageWrapper-"]'); + const openOrig = document.querySelectorAll( + 'div a[class^="downloadLink-"]' + )[0]; + openOrig.style.display = "none"; + const origLink = openOrig.getAttribute("href"); + var menuColors = 10; // how many color buttons to show in the menu + // ensure that the page contains nothing but a single : + var q = element; + if (q.tagName != "IMG") return; + // stylesheet: + // the thing is that browser has it's own zoom in controls, and they are + // linked to that particular natively. So we need to make a copy of it, strip + // it of any unneeded styles, and put it in place of the original: + var o = q.cloneNode(); + //remove size downscaling + let reg = /\?width=\d+&height=\d+$/; + let os = o.getAttribute("src"); + let repl = os.replace(reg, ""); + o.setAttribute("src", repl); + // + o.removeAttribute("width"); + o.removeAttribute("height"); + var panner = document.createElement("div"); + globalPanner = panner; + + function check() { + if (!wrap) { + onImageClose(globalPanner); + } + } + var pannerZoomed = false; + panner.className = "imgxis-panner"; + panner.style.height = "100vh"; + panner.style.width = "100vw"; + panner.appendChild(o); + q.parentNode.appendChild(panner); + //q.parentNode.removeChild(q); + q.style.display = "none"; + + function fixWrap() { + let elems = document.querySelectorAll( + 'div[class^="modal-"] > div[class^="inner-"] > div > div[class^="imageWrapper-"]' + ); + elems.forEach(el => { + fixElem(el); + }); + } + + function fixElem(elemToFix) { + if (electron.process.platform == "win32" || electron.process.platform == "darwin") { + menu.style.zIndex = 3003; + menu.style.top = "22px"; + menu.style.position = "absolute"; + menu.style.left = 0; + elemToFix.style.top = "22px"; + elemToFix.style.height = "calc(100vh - 22px)"; + } else { + elemToFix.style.height = "100vh"; + elemToFix.style.top = 0; + } + + elemToFix.style.width = "100vw"; + elemToFix.style.position = "absolute"; + elemToFix.style.zIndex = 3002; + elemToFix.style.left = 0; + } + + var s = o.style; + s.transformOrigin = "top left"; + q = null; + var tile = false; + // + var w = 0, + h = 0, + x = 0, + y = 0, + z = 0, + m = 1; + // pan and zoom: + function update() { + var pz = m > 1; + if (pz != pannerZoomed) { + pannerZoomed = pz; + var cl = panner.classList; + if (pz) cl.add("zoomed"); + else cl.remove("zoomed"); + } + if (tile) { + var p = panner.style; + p.backgroundPosition = `${-x}px ${-y}px`; + p.backgroundSize = `${o.width * m}px`; + } + s.transform = `matrix(${m},0,0,${m},${-x},${-y})`; + } + + function zoomto(zx, zy, d) { + var p = m; + m = Math.pow(2, (z += d)); + var f = m / p; + x = (zx + x) * f - zx; + y = (zy + y) * f - zy; + menu_zoom.innerHTML = (0 | (m * 100)) + "%"; + update(); + } + + // menu: + var menu_zoom = null, + menu; + (function() { + menu = document.createElement("div"); + menu.style.zIndex = 2000; + menu.className = "imgxis-menu"; + + function menubt(s, f, style) { + var bt = document.createElement("input"); + bt.type = "button"; + bt.value = s; + if (style) { + bt.style = style; + } + bt.addEventListener("click", f); + menu.appendChild(bt); + } + var defcolors = [ + "#6A86B7", + "#60C19D", + "#AB7680", + "#FFFFFF", + "#F4F2EC", + "#CAC2BD", + "#88898E", + "#4F556A", + "#1D1F2C", + "#000000" + ]; + + function menucl(i) { + var bt = document.createElement("input"); + bt.type = "color"; + bt.style.zIndex = 1001; + bt.value = GM_getValue(`imgxis-color${i}`, defcolors[i]); + + function bt_apply() { + let bodyElem = document.querySelector('div[class*="backdrop-"]'); + bodyElem.style.backgroundColor = bt.value; + } + bt.addEventListener("click", function(e) { + if (!e.shiftKey) { + e.preventDefault(); + bt_apply(); + } + }); + bt.addEventListener("change", function(e) { + GM_setValue(`imgxis-color${i}`, bt.value); + bt_apply(); + }); + bt.title = `Color ${i + 1}.\nClick to apply.\nShift+click to change.`; + menu.appendChild(bt); + if (i == 0) bt_apply(); + } + for (var i = 0; i < menuColors; i++) menucl(i); + menubt("-", function(_) { + zoomto(window.innerWidth / 2, window.innerHeight / 2, -0.5); + }); + menubt("1:1", function(_) { + var iw = window.innerWidth, + ih = window.innerHeight; + zoomto(iw / 2, ih / 2, -z); + x = (w - iw) / 2; + y = (h - ih) / 2; + update(); + }); + menubt("+", function(_) { + zoomto(window.innerWidth / 2, window.innerHeight / 2, 0.5); + }); + menubt("tile", function(_) { + tile = !tile; + panner.style.backgroundImage = tile ? `url(${o.src})` : ""; + update(); + }); + menubt( + "original", + function(_) { + window.open(origLink); + }, + "width:50px;" + ); + // + menu_zoom = document.createElement("span"); + menu_zoom.innerHTML = "100%"; + menu.appendChild(menu_zoom); + document + .querySelector('div[class*="backdrop-"]') + .parentNode.appendChild(menu); + })(); + // mouse controls: + function onmousewheel(e) { + var d = e.wheelDelta || -e.detail; + d = (d < 0 ? -1 : d > 0 ? 1 : 0) * 0.5; + zoomto(e.pageX, e.pageY, d); + check(); + } + var mx = 0, + my = 0, + mp = false; + + function onmousemove(e) { + var ox = mx; + mx = e.pageX; + var dx = mx - ox; + var oy = my; + my = e.pageY; + var dy = my - oy; + if (mp) { + x -= mx - ox; + y -= my - oy; + update(); + } + check(); + } + + function onmousedown(e) { + onmousemove(e); + fixWrap(); + if (e.which != 3 && !e.altKey) { + // not the right click + e.preventDefault(); // disable image "grab" + mp = true; + } + } + + function onmouseup(e) { + onmousemove(e); + fixWrap(); + mp = false; + } + + // + var refresh_timer = null; + + function refresh() { + var o_src = o.src; + // strip the previous timestamp parameter: + var pos = o_src.indexOf("imgxis-time"); + if (pos >= 0) { + switch (o_src.charAt(pos - 1)) { + case "&": + case "?": + pos--; + break; + } + o_src = o_src.substring(0, pos); + } + // add a timestamp parameter: + o_src += o_src.indexOf("?") >= 0 ? "&" : "?"; + o_src += `imgxis-time=${Date.now()}`; + // + o.src = o_src; + if (tile) panner.style.backgroundImage = `url(${o.src})`; + } + + // + function onkeydown(e) { + switch (e.keyCode) { + case 27: + onImageClose(globalPanner); + break; + case 116: + e.preventDefault(); + if (e.shiftKey) { + if (refresh_timer == null) { + var t = parseFloat(prompt("Refresh interval (seconds)", "15")); + if (!isNaN(t)) + refresh_timer = setInterval(refresh, Math.max(100, t * 1000)); + } else clearInterval(refresh_timer); + } else refresh(); + break; + } + } + window.addEventListener("mousemove", onmousemove); + panner.addEventListener("mousedown", onmousedown); + window.addEventListener("mouseup", onmouseup); + window.addEventListener("mousewheel", onmousewheel); + window.addEventListener("DOMMouseScroll", onmousewheel); + window.addEventListener("keydown", onkeydown); + // consider that the image dimensions may not be available instantly: + var onwait_t = 1000; + var onwait = function onwait() { + w = o.width; + h = o.height; + if (w > 0 && h > 0) { + var iw = window.innerWidth, + lw = w; + var ih = window.innerHeight, + lh = h; + if (lw < iw && lh < ih) + for (var k = 0; k < 3; k++) { + if (lw * 2 < iw && lh * 2 < ih) { + z++; + lw *= 2; + lh *= 2; + } + } + else + while (lw > iw || lh > ih) { + z--; + lw /= 2; + lh /= 2; + } + m = Math.pow(2, z); + menu_zoom.innerHTML = (0 | (m * 100)) + "%"; + x = -(iw - lw) / 2; + y = -(ih - lh) / 2; + update(); + fixWrap(); + } else if (--onwait_t > 0) setTimeout(onwait, 10); + }; + onwait(); + } + + function onImageClose(panner) { + if (panner) { + document.querySelectorAll('div[class*="imgxis-menu"]').forEach(el=>el.remove()); + window.removeEventListener("mousemove", onmousemove); + panner.removeEventListener("mousedown", onmousedown); + window.removeEventListener("mouseup", onmouseup); + window.removeEventListener("mousewheel", onmousewheel); + window.removeEventListener("DOMMouseScroll", onmousewheel); + window.removeEventListener("keydown", onkeydown); + globalPanner = undefined; + } + } + + function GM_getValue(name, defaultV) { + if (values[name]) { + return values[name]; + } else { + return defaultV; + } + } + + function GM_setValue(name, value) { + values[name] = value; + } + + $api.events.hook("MODAL_PUSH",e=>{ + setTimeout(_=>{ + const imageWrapper = document.querySelectorAll('div[class^="modal-"] div[class^="inner-"] div div[class^="imageWrapper-"]')[0]; + const image = (imageWrapper && imageWrapper.children[0].className == "imgxis-panner") ? imageWrapper.children[0].children[0] : (imageWrapper ? imageWrapper.children[0] : imageWrapper); + if (imageWrapper && image) { + onImageOpen(image); + } + },300); + }); + + $api.events.hook("MODAL_POP",e=>{ + onImageClose(globalPanner); + }); +} \ No newline at end of file diff --git a/plugins/localstorage.js b/plugins/localstorage.js new file mode 100644 index 0000000..583f19b --- /dev/null +++ b/plugins/localstorage.js @@ -0,0 +1,10 @@ +exports = { + meta: { + author: "Cynosphere, Adryd", + name: "Restore Local Storage", + desc: "Restore window.localStorage." + }, + replacements: { + 'try{delete window.localStorage}catch(e){}':'' + } +} \ No newline at end of file diff --git a/plugins/notifsound.js b/plugins/notifsound.js new file mode 100644 index 0000000..c3d79d7 --- /dev/null +++ b/plugins/notifsound.js @@ -0,0 +1,20 @@ +exports.meta = { + author: "dr1ft", + name: "Notification Sound Replacer", + desc: "Replaces notification sound with an unused sound thats nicer." +} +exports.start = function(){ + var s = wc.findFunc('playSound')[1].exports; + s.playSoundOriginal = s.playSound; + s.playSound = function (e) { + switch (e) { + case "message1": + return s.playSoundOriginal('message3'); + //new Audio(window._sndURL).play(); + //return s.playSoundOriginal(e, 0); + default: + return s.playSoundOriginal(e); + break; + } + } +} \ No newline at end of file diff --git a/plugins/osutyping.js b/plugins/osutyping.js new file mode 100644 index 0000000..118f1e8 --- /dev/null +++ b/plugins/osutyping.js @@ -0,0 +1,31 @@ +exportsexports = { + meta: { + author: "Cynosphere", + name: "osu! Typing", + desc: "Adds typing sounds from osu!." + }, + start: function(){ + var sounds = []; + for (var i = 1; i < 4; i++) { + sounds.push(new Audio(`https://github.com/statefram/sounds/blob/master/osu_typing_click${i}.wav?raw=true`)); + } + const backspace = new Audio("https://github.com/statefram/sounds/blob/master/osu_typing_erase.wav?raw=true"); + + var keys = {}; + function typingSound(ev) { + for (const sound of sounds) { + sound.pause(); + sound.currentTime = 0; + } + backspace.pause(); + backspace.currentTime = 0; + if (ev.key == "Backspace" || ev.key == "Delete") { + backspace.play(); + } else { + sounds[Math.floor(sounds.length * Math.random())].play(); + } + return false; + } + document.addEventListener("keydown", typingSound); + } +} \ No newline at end of file diff --git a/plugins/spotifyspoof.js b/plugins/spotifyspoof.js new file mode 100644 index 0000000..6457fbf --- /dev/null +++ b/plugins/spotifyspoof.js @@ -0,0 +1,10 @@ +exports = { + meta: { + author: "Cynosphere", + name: "Spotify Premium Spoof", + desc: "Spoofs premium check and allows listen along without premium." + }, + replacements: { + 'r.isPremium=n':'r.isPremium=true' + } +} \ No newline at end of file