NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name BMPP // @namespace http://tampermonkey.net/ // @version 1.0 // @description Better multiplayer piano experience // @author Krendel // @match http*://www.multiplayerpiano.com/* // @grant GM_log // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @require http://code.jquery.com/jquery-3.5.1.min.js // @run-at document-start // @license MIT // ==/UserScript== /* globals jQuery, $, MPP */ unsafeWindow.BMPP = (function() { 'use strict'; /* ===== Styles ===== */ GM_addStyle(` @import url('https://fonts.googleapis.com/css2?family=Open+Sans&display=swap'); * { image-rendering: auto !important; } body { -webkit-transition: none !important; } html, body { font-size: 18px !important; font-family: 'Open Sans', sans-serif !important; } #ban-button { margin-left: 0.3rem; } #room-controls { position: absolute; top: 0; right: 0; margin: 0.2rem 0.2rem; text-shadow: 0 0 2px black, 0 0 2px black, 0 0 2px black, 0 0 2px black; } #room { font-size: 0.8rem !important; padding: 0 0.1rem !important; height: 1.2rem !important; display: flex !important; align-items: center !important; background-color: #768fa3 !important; border-color: #b4d0ff; width: 300px !important; } #room .more .info { background-color: #5c6878 !important; overflow: hidden !important; } #room .more .info:nth-child(even) { background-color: #495668 !important; } #room .more { background-color: #495668 !important; position: unset !important; border-color: #b4d0ff; } #room .more .new { display: none !important; } #room .more .info:hover { border-left: solid 2px white; } #room .expand { background-color: #5c6878 !important; } #room .info { display: flex !important; align-items: center !important; padding: 0.2rem 0 !important; line-height: unset !important; text-shadow: 0 0 1px black, 0 0 1px black, 0 0 1px black, 0 0 1px black; } #room .info.lobby { color: unset !important; } #room .info.lobby .room-name { color: #7fc1ff; } #room .info.not-visible { color: #353f49 !important; } #bottom { background-color: #040c1c !important; height: 64px !important; } .ugly-button { font-size: 0.8rem !important; background-color: rgba(172,219,255,0.3) !important; -webkit-border-radius: 5px !important; border: solid 1px black !important; height: 14px !important; display: flex !important; align-items: center; } .ugly-button:hover { border-color: white !important; } #names .name.me:after { content: "" !important; } #names .name.me { border: outset 4px white !important; } #names .name { text-shadow: 0 0 2px black, 0 0 2px black, 0 0 2px black, 0 0 2px black; font-weight: bold !important; font-size: 0.8rem !important; } ::-webkit-scrollbar { width: 10px !important; } ::-webkit-scrollbar-track { background: #495668 !important; border-radius: 10px !important; } ::-webkit-scrollbar-thumb { background: #768fa3 !important; border-radius: 10px !important; } #chat { display: flex !important; flex-direction: column !important; align-items: stretch !important; justify-content: stretch !important; bottom: 68px !important; } #chat input { width: unset !important; height: 1rem !important; font-family: inherit !important; font-size: 0.8rem !important; } #chat input:focus { border: solid 1px #5eb2ff !important; } #chat.chatting { background-color: rgba(121,124,181,0.3) !important; box-shadow: none !important; } #chat.chatting ul { overflow-y: auto !important; } #chat ul { overflow-wrap: break-word !important; } #quota { background-color: #c4f8ff !important; } #quota .value { background-color: #325d97 !important; } .room-name { background-color: rgba(14,12,28,0); pointer-events: none; display: flex; justify-content: center; } .room-name-wrapper { z-index: 10; pointer-events: none; } #room .more .info:hover > .room-name-wrapper { display: block; } .rooms-wrapper { position: absolute; width: 100%; bottom: 100%; left: -1px; } .count { display: flex; align-items: center; z-index: 100; justify-content: center; padding: 0.2rem 0.2rem; margin-right: 0.3rem; min-width: 1rem; background-color: #768fa3; } #Notification-share { display: none !important; } #social { display: none !important; } .dialog { background-color: rgba(62,78,110,0.8) !important; border-color: #b4d0ff !important; text-shadow: 0 0 1px black, 0 0 1px black, 0 0 1px black, 0 0 1px black !important; } .dialog .submit { text-shadow: 0 0 1px black, 0 0 1px black, 0 0 1px black, 0 0 1px black !important; padding: 0.5rem 1.5rem !important; right: 0 !important; bottom: 0 !important; display: flex !important; align-items: center; justify-content: center; outline: none !important; } .submit { background-color: #80b2e0 !important; -webkit-box-shadow: none !important; box-shadow: none !important; border-radius: 4px 0 0 0 !important; } .submit:hover { border-left: solid #b4d0ff 1px !important; border-top: solid #b4d0ff 1px !important; } .drop-crown { margin-left: 14px !important; } .notification-body { background-color: #040c1c !important; color: white !important; border: solid 1px #b4d0ff !important; text-shadow: none !important; } .notification .x { color: #b4d0ff !important; font-size: 1.5rem !important; } .title { border-color: #b4d0ff !important; } .notification .pack, .notification .connection { border-color: #b4d0ff !important; } .notification .pack.enabled, .notification .connection.enabled { background-color: rgba(138,183,255,0.4) !important; } .notification .pack.enabled::after { color: #d7e2ff !important; } .notification .connection { background-color: #040c1c !important; } .button-container { display: flex; flex-wrap: wrap; margin: 2px; margin-left: 350px; min-width: 466px; max-width: 900px; } #new-room-btn, #play-alone-btn, #room-settings-btn, #midi-btn, #record-btn, #synth-btn, #sound-btn { position: unset !important; margin: 2px; } .marquee > .marquee-text { animation: marquee 10s linear infinite; } @keyframes marquee { 0% { -moz-transform: translateX(0%); -webkit-transform: translateX(0%); transform: translateX(0%); } 100% { -moz-transform: translateX(-100%); -webkit-transform: translateX(-100%); transform: translateX(-100%); } } .relative { display: flex; justify-content: space-between; } .volume-container { display: flex; flex-direction: column; margin: 8px 5px; } #volume { position: unset !important; margin: 0 !important; width: 140px !important; height: 20px !important; } #volume-slider { background-size: contain !important; } #volume-label { position: unset !important; text-align: right; } input[type=range]#volume-slider { -webkit-appearance: none; /* Hides the slider so that custom slider can be made */ width: 100%; /* Specific width is required for Firefox. */ } input[type=range]#volume-slider:focus { outline: none; /* Removes the blue border. */ } input[type=range]#volume-slider::-ms-track { width: 100%; cursor: pointer; /* Hides the slider so custom styles can be added */ background: transparent; border-color: transparent; color: transparent; } /* Special styling for WebKit/Blink */ input[type=range]#volume-slider::-webkit-slider-thumb { -webkit-appearance: none; border: 1px solid #000000; height: 20px; width: 10px; border-radius: 3px; background: #ffffff; cursor: pointer; margin-top: -14px; box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; position: relative; top: 6px; } /* All the same stuff for Firefox */ input[type=range]#volume-slider::-moz-range-thumb { box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; border: 1px solid #000000; height: 20px; width: 10px; border-radius: 3px; background: #ffffff; cursor: pointer; position: relative; top: 6px; } /* All the same stuff for IE */ input[type=range]#volume-slider::-ms-thumb { box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; border: 1px solid #000000; height: 20px; width: 10px; border-radius: 3px; background: #ffffff; cursor: pointer; position: relative; top: 6px; } .autoban-container { position: absolute; top: 40px; right: 0; margin: 0.2rem 0.2rem; text-shadow: 0 0 2px black, 0 0 2px black, 0 0 2px black, 0 0 2px black; text-align: right; } #autoban-timeout { width: 30px; } .lbl-toggle:focus { outline: none; } input[type='checkbox'].toggle { display: none; } .lbl-toggle { display: block; font-weight: bold; text-transform: uppercase; text-align: center; color: #5d81c8; background: rgba(20, 20, 38, 0.5); cursor: pointer; border-radius: 5px; transition: all 0.25s ease-out; } .lbl-toggle:hover { color: #395999; } .lbl-toggle::before { content: ''; display: inline-block; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 5px solid currentColor; vertical-align: middle; margin-right: .7rem; transform: translateY(-2px); transition: transform .2s ease-out; } .toggle:checked + .lbl-toggle::before { transform: rotate(90deg) translateX(-3px); } .collapsible-content { max-height: 0px; overflow: hidden; transition: max-height .25s ease-in-out; } .toggle:checked + .lbl-toggle + .collapsible-content { max-height: 100vh; } .toggle:checked + .lbl-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .collapsible-content .content-inner { background: rgba(74, 93, 150, .2); border-bottom: 1px solid rgba(120, 190, 255, .5); border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; padding: .5rem; } .collapsible-content label { float: left; } .collapsible-content input:focus { outline: none; } .collapsible-content input[type="text"] { margin-left: 10px; } .sort-container { height: auto !important; padding: 0 !important; position: sticky; top:0; z-index:1000; cursor: auto !important; display: flex; align-items: center; justify-content: space-between; background-color: #2e3547; } .sort-toggle { font-size: 0.7rem; font-weight: bold; text-transform: uppercase; text-align: center; padding: .2rem .4rem !important; margin-right: .4rem !important; color: #5d81c8; flex-grow: 1; position: relative; cursor: pointer; transition: all 0.25s ease-out; } .sort-toggle.sort-asc::after, .sort-toggle.sort-desc::after { content: ''; display: inline-block; border-left: 5px solid transparent; border-right: 5px solid transparent; vertical-align: middle; transform: translateY(-2px); position: absolute; right: 0; top: 12px; transition: transform .2s ease-out; } .sort-toggle.sort-asc::after { border-bottom: 5px solid currentColor; } .sort-toggle.sort-desc::after { border-top: 5px solid currentColor; } `); /* ==== Predefined elements ==== */ const roomControlsDiv = ` <div id="room-controls"> <label for="room-color">Room color:</label> <input type="color" id="room-color"> </div>`; const banButton = '<button id="ban-button">Ban</button>'; const roomsWrapper = '<div class="rooms-wrapper"></div>'; const roomInfoTemplate = '<div class="info"><div class="count"></div><div class="room-name-wrapper"><div class="room-name"></div></div></div>'; const buttonContainer = '<div class="button-container"></div>'; const volumeContainer = '<div class="volume-container"></div>'; const autobanContainer = ` <div class="autoban-container"> <input id="collapsible" class="toggle" type="checkbox"> <label for="collapsible" class="lbl-toggle" tabindex="0">Autoban Options</label> <div class="collapsible-content"> <div class="content-inner"> <div> <label for="autoban-timeout">Timeout (m): </label> <input type="number" id="autoban-timeout"> </div> <div> <label for="autoban-anonymous">Anonymous: </label> <input type="checkbox" id="autoban-anonymous"> </div> <div> <label for="autoban-russian-nicknames">Russian nicknames: </label> <input type="checkbox" id="autoban-russian-nicknames"> </div> <div> <label for="autoban-regex">Regex: </label> <input type="text" id="autoban-regex"> </div> </div> </div> </div> `; const sortContainerTemplate = ` <div class="sort-container"> <div class="sort-by-creation sort-toggle">by creation</div> <div class="sort-by-name sort-toggle">by name</div> <div class="sort-by-count sort-toggle">by count</div> </div> `; const sortType = { byCreationAsc: 0, byCreationDesc: 1, byNameAsc: 2, byNameDesc: 3, byCountAsc: 4, byCountDesc: 5 } const notificationMidiSelector = "#Notification-MIDI-Connections"; const rooms = []; // remove all translations $(".translate").removeClass("translate"); $(function() { const $ = unsafeWindow.$; $.fn.insertAt = function(index, element) { var lastIndex = this.children().length; if (index < 0) { index = Math.max(0, lastIndex + 1 + index); } this.append(element); if (index < lastIndex) { this.children().eq(index).before(this.children().last()); } return this; } /* ==== functions ==== */ function elementReady(selector) { return new Promise((resolve, reject) => { const el = $(selector)[0]; if (el) {resolve(el);} new MutationObserver((mutationRecords, observer) => { // Query for elements matching the specified selector Array.from($(selector).toArray()).forEach((element) => { resolve(element); // Once we have resolved we don't need the observer anymore. observer.disconnect(); }); }).observe(document.documentElement, { childList: true, subtree: true }); }); } function isParticipantOwner(p) { let channel = MPP.client.channel; return channel && channel.crown && channel.crown.participantId === p.id; } function ban(p) { if (isParticipantOwner(p)) return; let timeoutSec = parseInt(GM_getValue("autobanTimeout")); MPP.client.sendArray([{m: "kickban", _id: p._id, ms: (!isNaN(timeoutSec) && timeoutSec >= 1 && timeoutSec <= 60 ? timeoutSec : 1) * 60 * 1000 }]); console.log(`Tried to ban ${p}`); } function banIfAnon(p) { if (p.name.toLowerCase() === "anonymous") { ban(p); } } function banIfRussianNickname(p) { if (/[а-яА-ЯЁё]/.test(p.name)) { ban(p); } } function banIfRegexMatched(p, regex) { if (RegExp(regex).test(p.name)) { ban(p); } } function autobanUser(p) { if (!MPP.client.isOwner() || MPP.client.channel.settings.lobby || isParticipantOwner(p)) return; if (GM_getValue("autobanAnonymous")) { banIfAnon(p); }; if (GM_getValue("autobanRussianNicknames")) { banIfRussianNickname(p); } let regex = GM_getValue("autobanRegex"); if (regex) { banIfRegexMatched(p, regex); } } function invertHex(hex) { let isPrefixed = false; if(hex.length < 6 && hex.length > 7) { console.error("Hex color must be six hex numbers in length."); return false; } if(hex.length == 7 && hex[0] != '#') { console.error("Hex color must begin with #."); return false; } isPrefixed = hex.length == 7; if (isPrefixed) { hex = hex.substring(1); } hex = hex.toUpperCase(); let splitNum = hex.split(""); let resultNum = ""; let simpleNum = "FEDCBA9876".split(""); let complexNum = new Array(); complexNum.A = "5"; complexNum.B = "4"; complexNum.C = "3"; complexNum.D = "2"; complexNum.E = "1"; complexNum.F = "0"; for(let i=0; i<6; i++){ if(!isNaN(splitNum[i])) { resultNum += simpleNum[splitNum[i]]; } else if(complexNum[splitNum[i]]) { resultNum += complexNum[splitNum[i]]; } else { console.error("Hex colors must only include hex numbers 0-9, and A-F"); return false; } } if (isPrefixed) { resultNum = '#' + resultNum; } return resultNum; } function remToPx(count) { let unit = $('html').css('font-size'); if (typeof count !== 'undefined' && count > 0) { return (parseInt(unit) * count); } else { return parseInt(unit); } } function isOverflownHorizontally(element) { return element.scrollWidth > element.clientWidth; } function animateRoomName(targetElement, speed) { if (isOverflownHorizontally($(targetElement).parents(".info")[0])) { $(targetElement).animate({ marginLeft: "-=5" }, { specialEasing: { marginLeft: "linear" }, duration: speed, complete: function() { animateRoomName(this, speed); } }); } } function getUsersInRoom() { return Object.values(MPP.client.ppl); } function getHandlers(element, event) { return $._data($(element).get(0), "events")[event].map(e => e.handler); } function scrollToBottomRooms() { let more = $("#room .more"); more.scrollTop(more.prop("scrollHeight")); } /* =================== */ function insertRoom(room) { let currentSortType = GM_getValue("sortType"); let foundRoomIdx; switch (currentSortType) { case sortType.byCreationAsc: foundRoomIdx = rooms.findIndex(r => r.creationId > room.creationId); break; case sortType.byCreationDesc: foundRoomIdx = rooms.findIndex(r => r.creationId < room.creationId); break; case sortType.byNameAsc: foundRoomIdx = rooms.findIndex(r => r._id > room._id); break; case sortType.byNameDesc: foundRoomIdx = rooms.findIndex(r => r._id < room._id); break; case sortType.byCountAsc: foundRoomIdx = rooms.findIndex(r => r.count > room.count); break; case sortType.byCountDesc: foundRoomIdx = rooms.findIndex(r => r.count < room.count); break; } rooms.splice(foundRoomIdx, 0, room); return foundRoomIdx; } function updateRoom(room) { let foundRoomIdx = rooms.findIndex(r => r._id == room._id); rooms[foundRoomIdx] = room; return foundRoomIdx; } function updateRoomOrder(room) { let currentSortType = GM_getValue("sortType"); let oldRoomIdx = rooms.findIndex(r => r._id == room._id); let foundRoomIdx; switch (currentSortType) { case sortType.byCountAsc: foundRoomIdx = rooms.findIndex(r => r.count > room.count); break; case sortType.byCountDesc: foundRoomIdx = rooms.findIndex(r => r.count < room.count); break; } if (foundRoomIdx == -1) { rooms.splice(oldRoomIdx, 1); rooms.push(room); } else { rooms.splice(oldRoomIdx, 1); rooms.splice(foundRoomIdx, 0, room); } return foundRoomIdx; } function insertDomRoom(room) { let $roomInfo = $(roomInfoTemplate); $roomInfo.attr("roomname", room._id); $(".room-name", $roomInfo).text(room._id); let currentSortType = GM_getValue("sortType"); let foundRoomIdx; switch (currentSortType) { case sortType.byCreationAsc: foundRoomIdx = rooms.findIndex(r => r.creationId > room.creationId); break; case sortType.byCreationDesc: foundRoomIdx = rooms.findIndex(r => r.creationId < room.creationId); break; case sortType.byNameAsc: foundRoomIdx = rooms.findIndex(r => r._id > room._id); break; case sortType.byNameDesc: foundRoomIdx = rooms.findIndex(r => r._id < room._id); break; case sortType.byCountAsc: foundRoomIdx = rooms.findIndex(r => r.count > room.count); break; case sortType.byCountDesc: foundRoomIdx = rooms.findIndex(r => r.count < room.count); break; } $("#room .more").insertAt(foundRoomIdx, $roomInfo); updateDomRoom(room, $roomInfo); return foundRoomIdx; } function appendDomRoom(room) { let $roomInfo = $(roomInfoTemplate); $roomInfo.attr("roomname", room._id); $(".room-name", $roomInfo).text(room._id); $("#room .more").append($roomInfo); updateDomRoom(room, $roomInfo); } function updateDomRoom(room, $roomInfo = $("#room .info[roomname=\"" + (room._id + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0') + "\"]")) { $(".count", $roomInfo).text(room.count); if(room.settings.lobby) $roomInfo.addClass("lobby"); else $roomInfo.removeClass("lobby"); if(!room.settings.chat) $roomInfo.addClass("no-chat"); else $roomInfo.removeClass("no-chat"); if(room.settings.crownsolo) $roomInfo.addClass("crownsolo"); else $roomInfo.removeClass("crownsolo"); if(room.settings['no cussing']) $roomInfo.addClass("no-cussing"); else $roomInfo.removeClass("no-cussing"); if(!room.settings.visible) $roomInfo.addClass("not-visible"); else $roomInfo.removeClass("not-visible"); if(room.banned) $roomInfo.addClass("banned"); else $roomInfo.removeClass("banned"); let initialStyle; // room name scrolling $roomInfo.hover(function() { initialStyle = $(".room-name", this).css("marginLeft"); animateRoomName($(".room-name", this), 50); }, function() { $(".room-name", this).css("marginLeft", initialStyle); $(".room-name", this).stop(); }); } function updateDomRoomOrder(room, $roomInfo = $("#room .info[roomname=\"" + (room._id + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0') + "\"]")) { let currentSortType = GM_getValue("sortType"); let foundRoomIdx; switch (currentSortType) { case sortType.byCountAsc: foundRoomIdx = rooms.findIndex(r => r.count > room.count); break; case sortType.byCountDesc: foundRoomIdx = rooms.findIndex(r => r.count < room.count); break; } $roomInfo = $roomInfo.detach(); $("#room .more").insertAt(foundRoomIdx, $roomInfo); return foundRoomIdx; } function sortRooms(sortingType) { switch (sortingType) { case sortType.byCreationAsc: rooms.sort((a, b) => a.creationId - b.creationId); break; case sortType.byCreationDesc: rooms.sort((a, b) => b.creationId - a.creationId); break; case sortType.byNameAsc: rooms.sort((a, b) => a._id.localeCompare(b._id)); break; case sortType.byNameDesc: rooms.sort((a, b) => b._id.localeCompare(a._id)); break; case sortType.byCountAsc: rooms.sort((a, b) => a.count - b.count); break; case sortType.byCountDesc: rooms.sort((a, b) => b.count - a.count); break; } } function toggleAsc($toggle) { $(".sort-container > *").removeClass("sort-desc"); $(".sort-container > *").removeClass("sort-asc"); $toggle.removeClass("sort-desc"); $toggle.addClass("sort-asc"); } function toggleDesc($toggle) { $(".sort-container > *").removeClass("sort-desc"); $(".sort-container > *").removeClass("sort-asc"); $toggle.removeClass("sort-asc"); $toggle.addClass("sort-desc"); } // replace console log let oldLog = unsafeWindow.console.log; let onLog = function(msg) { if (msg === "output") { setTimeout(() => { $(notificationMidiSelector).remove(); }, 1); } }; unsafeWindow.console.log = function(msg) { if (onLog) { onLog(msg); } oldLog.apply(null, arguments); } // reset properties window.localStorage.knowsYouCanUseKeyboard = true; window.localStorage.gHasBeenHereBefore = true; window.localStorage.volume = 0; // hide midi connections popout elementReady(notificationMidiSelector).then(midiCon => { $(notificationMidiSelector + ' .enabled').trigger("click"); $(notificationMidiSelector + ' ul:nth-of-type(1) li').filter(function() { return $(this).text() === GM_getValue("defaultMidiInput"); }).trigger("click"); $(notificationMidiSelector + ' ul:nth-of-type(2) li').filter(function() { return $(this).text() === GM_getValue("defaultMidiOutput"); }).trigger("click"); setTimeout(() => { $(notificationMidiSelector).remove(); }, 15); }); $('#modal').hide(); // hide notification animation MPP.Notification.prototype.close = function() { window.removeEventListener("resize", this.onresize); this.domElement.remove(); this.emit("close"); } // speed up animation for enter and leaving room MPP.client.on("participant added", function(part) { let nd = $(part.nameDiv); let cd = $(part.cursorDiv); nd.stop(true, true).fadeOut(0).fadeIn(200); cd.stop(true, true).fadeOut(0).fadeIn(200); }); MPP.client.on("participant removed", function(part) { let nd = $(part.nameDiv); let cd = $(part.cursorDiv); cd.stop(true, true).fadeOut(200); nd.stop(true, true).fadeOut(200, function() { nd.remove(); cd.remove(); part.nameDiv = undefined; part.cursorDiv = undefined; }); }); // add ban button to users MPP.client.on("participant added", function(p) { if (!MPP.client.isOwner() || MPP.client.channel.settings.lobby || isParticipantOwner(p)) return; let $banButton = $(banButton).click({ participant: p }, function(e) { ban(e.data.participant); }); elementReady(p.nameDiv).then(elem => $banButton.appendTo(elem)); }); MPP.client.on("participant update", function(p) { if (!MPP.client.isOwner() || MPP.client.channel.settings.lobby || isParticipantOwner(p) || p === null) return; let $banButton = $(banButton).click({ participant: p }, function(e) { ban(e.data.participant); }); setTimeout(() => { if ($("#ban-button", p.nameDiv).length == 0) { elementReady(p.nameDiv).then(elem => $banButton.appendTo(elem)); } }, 100); }); // add room controls let defaultRoomColor = "#273445"; let currentRoomId = 0; MPP.client.on("ch", (msg) => { if (MPP.client.isOwner()) { let settings = msg.ch.settings; if (!$("#room-controls").length) { $("body").append(roomControlsDiv); settings.color = defaultRoomColor; MPP.client.setChannelSettings(settings); } if (msg.ch._id !== currentRoomId) { currentRoomId = msg.ch._id; settings.color = defaultRoomColor; MPP.client.setChannelSettings(settings); } $("#room-color").val(settings.color).change(function(e) { settings.color = e.target.value; MPP.client.setChannelSettings(settings); $("#room-color").css("pointer-events", "none"); setTimeout(function() { $("#room-color").css("pointer-events", "auto"); }, 600) }); } else { $("#room-controls").remove(); } }); // display people count and full room titles $("#room").append(roomsWrapper); $("#room .more").appendTo("#room .rooms-wrapper"); MPP.client._events.ls.shift(); MPP.client.on("ls", ls => { let currentSortType = GM_getValue("sortType"); if (ls.c) { switch (currentSortType) { case sortType.byCreationAsc: toggleAsc($(".sort-by-creation")); break; case sortType.byCreationDesc: toggleDesc($(".sort-by-creation")); break; case sortType.byNameAsc: toggleAsc($(".sort-by-name")); break; case sortType.byNameDesc: toggleDesc($(".sort-by-name")); break; case sortType.byCountAsc: toggleAsc($(".sort-by-count")); break; case sortType.byCountDesc: toggleDesc($(".sort-by-count")); break; } rooms.length = 0; ls.u.forEach(function(r, i) { r.creationId = i; rooms.push(r); }); if (currentSortType != sortType.byCreationAsc) { sortRooms(currentSortType); } rooms.forEach(appendDomRoom); } else { ls.u.forEach(room => { let foundRoomIdx = rooms.findIndex(r => r._id === room._id); if (foundRoomIdx == -1) { insertRoom(room); insertDomRoom(room); } else { updateRoom(room); updateDomRoom(room); // if (currentSortType == sortType.byCountAsc || currentSortType == sortType.byCountDesc) { // updateRoomOrder(room); // updateDomRoomOrder(room); // } } }); } }); // add container for buttons $("#bottom .relative").append(buttonContainer); let $buttonContainer = $("#bottom .relative .button-container"); $("#new-room-btn").appendTo($buttonContainer); $("#play-alone-btn").appendTo($buttonContainer); $("#room-settings-btn").appendTo($buttonContainer); $("#midi-btn").appendTo($buttonContainer); $("#record-btn").appendTo($buttonContainer); $("#synth-btn").appendTo($buttonContainer); $("#sound-btn").appendTo($buttonContainer); // add container to volume $("#bottom .relative").append(volumeContainer); let $volumeContainer = $("#bottom .relative .volume-container"); $("#volume").appendTo($volumeContainer); $("#volume-label").appendTo($volumeContainer); // add autoban options $("body").append(autobanContainer); $(".autoban-container #autoban-timeout").val(GM_getValue("autobanTimeout")).change(function() { GM_setValue("autobanTimeout", $(this).val()); }); $(".autoban-container #autoban-anonymous").prop("checked", GM_getValue("autobanAnonymous")).change(function() { GM_setValue("autobanAnonymous", this.checked); getUsersInRoom().forEach((user) => banIfAnon(user)); }); $(".autoban-container #autoban-russian-nicknames").prop("checked", GM_getValue("autobanRussianNicknames")).change(function() { GM_setValue("autobanRussianNicknames", this.checked); getUsersInRoom().forEach((user) => banIfRussianNickname(user)); }); $(".autoban-container #autoban-regex").val(GM_getValue("autobanRegex")).change(function() { GM_setValue("autobanRegex", $(this).val()); getUsersInRoom().forEach((user) => banIfRegexMatched(user, $(this).val())); }); MPP.client.on("participant added", autobanUser); MPP.client.on("participant update", autobanUser); // manipulate events in order for text input to work let keydownHandlers = []; let keyupHandlers = []; let keypressHandlers = []; $(".autoban-container input[type='text'], .autoban-container input[type='number']").focus(function(evt) { getHandlers(document, "keydown").forEach((handler) => keydownHandlers.push(handler)); getHandlers(document, "keyup").forEach((handler) => keyupHandlers.push(handler)); getHandlers(unsafeWindow, "keypress").forEach((handler) => keypressHandlers.push(handler)); $(document).off("keydown"); $(document).off("keyup"); $(unsafeWindow).off("keypress"); }); $(".autoban-container input[type='text'], .autoban-container input[type='number']").blur(function(evt) { keydownHandlers.forEach((handler) => $(document).on("keydown", handler)); keyupHandlers.forEach((handler) => $(document).on("keyup", handler)); keypressHandlers.forEach((handler) => $(unsafeWindow).on("keypress", handler)); keydownHandlers = []; keyupHandlers = []; keypressHandlers = []; }); // add sorting options to room list let roomClickHandler = getHandlers($("#room")[0], "click")[0]; let documentMousedownHandler = () => {}; $("#room").off("click"); $("#room").click(function(event) { if (!$(event.target).hasClass("sort-toggle")) { roomClickHandler(event); $("#room .more .new").remove(); setTimeout(scrollToBottomRooms, 150); let handlers = getHandlers(document, "mousedown"); for (const handler of handlers) { if (handler.name === "doc_click") { documentMousedownHandler = handler; $(document).off("mousedown", handler); } } } }); $(document).on("mousedown", function(event) { if (!$(event.target).hasClass("sort-toggle")) { documentMousedownHandler(event); } }); $("#room .more").prepend(sortContainerTemplate); $(".sort-toggle").click(function(event) { let $toggle = $(this); if ($toggle.hasClass("sort-asc")) { toggleDesc($toggle); } else { toggleAsc($toggle); } let chosenSortType; if ($(this).is(".sort-by-creation.sort-asc")) { chosenSortType = sortType.byCreationAsc; } if ($(this).is(".sort-by-creation.sort-desc")) { chosenSortType = sortType.byCreationDesc; } if ($(this).is(".sort-by-name.sort-asc")) { chosenSortType = sortType.byNameAsc; } if ($(this).is(".sort-by-name.sort-desc")) { chosenSortType = sortType.byNameDesc; } if ($(this).is(".sort-by-count.sort-asc")) { chosenSortType = sortType.byCountAsc; } if ($(this).is(".sort-by-count.sort-desc")) { chosenSortType = sortType.byCountDesc; } GM_setValue("sortType", chosenSortType); sortRooms(chosenSortType); $("#room .more .info").remove(); rooms.forEach(appendDomRoom); }); }); return { rooms, get defaultMidiInput() { return GM_getValue("defaultMidiInput"); }, set defaultMidiInput(value) { GM_setValue("defaultMidiInput", value); }, get defaultMidiOutput() { return GM_getValue("defaultMidiOutput"); }, set defaultMidiOutput(value) { GM_setValue("defaultMidiOutput", value); } } })();