// 钢琴 $(function() { var test_mode = (window.location.hash && window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i)); var gSeeOwnCursor = (window.location.hash && window.location.hash.match(/^(?:#.+)*#seeowncursor(?:#.+)*$/i)); var gMidiVolumeTest = (window.location.hash && window.location.hash.match(/^(?:#.+)*#midivolumetest(?:#.+)*$/i)); var gMidiOutTest; if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(elt /*, from*/) { var len = this.length >>> 0; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (; from < len; from++) { if (from in this && this[from] === elt) return from; } return -1; }; } window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function (cb) { setTimeout(cb, 1000 / 30); }; var DEFAULT_VELOCITY = 0.5; var TIMING_TARGET = 1000; // Utility //////////////////////////////////////////////////////////////// var Rect = function(x, y, w, h) { this.x = x; this.y = y; this.w = w; this.h = h; this.x2 = x + w; this.y2 = y + h; }; Rect.prototype.contains = function(x, y) { return (x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2); }; // performing translation //////////////////////////////////////////////////////////////// var Translation = (function() { var strings = { "people are playing": { "pt": "pessoas estão jogando", "es": "personas están jugando", "ru": "человек играет", "fr": "personnes jouent", "ja": "人が遊んでいる", "de": "Leute spielen", "zh": "人在玩", "nl": "mensen spelen", "pl": "osób grają", "hu": "ember játszik" }, "New Room...": { "pt": "Nova Sala ...", "es": "Nueva sala de...", "ru": "Новый номер...", "ja": "新しい部屋", "zh": "新房间", "nl": "nieuwe Kamer", "hu": "új szoba" }, "room name": { "pt": "nome da sala", "es": "sala de nombre", "ru": "название комнаты", "fr": "nom de la chambre", "ja": "ルーム名", "de": "Raumnamen", "zh": "房间名称", "nl": "kamernaam", "pl": "nazwa pokój", "hu": "szoba neve" }, "Visible (open to everyone)": { "pt": "Visível (aberto a todos)", "es": "Visible (abierto a todo el mundo)", "ru": "Visible (открытый для всех)", "fr": "Visible (ouvert à tous)", "ja": "目に見える(誰にでも開いている)", "de": "Sichtbar (offen für alle)", "zh": "可见(向所有人开放)", "nl": "Zichtbaar (open voor iedereen)", "pl": "Widoczne (otwarte dla wszystkich)", "hu": "Látható (nyitott mindenki számára)" }, "Enable Chat": { "pt": "Ativar bate-papo", "es": "Habilitar chat", "ru": "Включить чат", "fr": "Activer discuter", "ja": "チャットを有効にする", "de": "aktivieren Sie chatten", "zh": "启用聊天", "nl": "Chat inschakelen", "pl": "Włącz czat", "hu": "a csevegést" }, "Play Alone": { "pt": "Jogar Sozinho", "es": "Jugar Solo", "ru": "Играть в одиночку", "fr": "Jouez Seul", "ja": "一人でプレイ", "de": "Alleine Spielen", "zh": "独自玩耍", "nl": "Speel Alleen", "pl": "Zagraj sam", "hu": "Játssz egyedül" } // todo: it, tr, th, sv, ar, fi, nb, da, sv, he, cs, ko, ro, vi, id, nb, el, sk, bg, lt, sl, hr // todo: Connecting, Offline mode, input placeholder, Notifications }; var setLanguage = function(lang) { language = lang }; var getLanguage = function() { if(window.navigator && navigator.language && navigator.language.length >= 2) { return navigator.language.substr(0, 2).toLowerCase(); } else { return "en"; } }; var get = function(text, lang) { if(typeof lang === "undefined") lang = language; var row = strings[text]; if(row == undefined) return text; var string = row[lang]; if(string == undefined) return text; return string; }; var perform = function(lang) { if(typeof lang === "undefined") lang = language; $(".translate").each(function(i, ele) { var th = $(this); if(ele.tagName && ele.tagName.toLowerCase() == "input") { if(typeof ele.placeholder != "undefined") { th.attr("placeholder", get(th.attr("placeholder"), lang)) } } else { th.text(get(th.text(), lang)); } }); }; var language = getLanguage(); return { setLanguage: setLanguage, getLanguage: getLanguage, get: get, perform: perform }; })(); Translation.perform(); // AudioEngine classes //////////////////////////////////////////////////////////////// var AudioEngine = function() { }; AudioEngine.prototype.init = function(cb) { this.volume = 0.6; this.sounds = {}; this.paused = true; return this; }; AudioEngine.prototype.load = function(id, url, cb) { }; AudioEngine.prototype.play = function() { }; AudioEngine.prototype.stop = function() { }; AudioEngine.prototype.setVolume = function(vol) { this.volume = vol; }; AudioEngine.prototype.resume = function() { this.paused = false; }; AudioEngineWeb = function() { this.threshold = 1000; this.worker = new Worker("/workerTimer.js"); var self = this; this.worker.onmessage = function(event) { if(event.data.args) if(event.data.args.action==0) { self.actualPlay(event.data.args.id, event.data.args.vol, event.data.args.time, event.data.args.part_id); } else { self.actualStop(event.data.args.id, event.data.args.time, event.data.args.part_id); } } }; AudioEngineWeb.prototype = new AudioEngine(); AudioEngineWeb.prototype.init = function(cb) { AudioEngine.prototype.init.call(this); this.context = new AudioContext({latencyHint: 'interactive'}); this.masterGain = this.context.createGain(); this.masterGain.connect(this.context.destination); this.masterGain.gain.value = this.volume; this.limiterNode = this.context.createDynamicsCompressor(); this.limiterNode.threshold.value = -10; this.limiterNode.knee.value = 0; this.limiterNode.ratio.value = 20; this.limiterNode.attack.value = 0; this.limiterNode.release.value = 0.1; this.limiterNode.connect(this.masterGain); // for synth mix this.pianoGain = this.context.createGain(); this.pianoGain.gain.value = 0.5; this.pianoGain.connect(this.limiterNode); this.synthGain = this.context.createGain(); this.synthGain.gain.value = 0.5; this.synthGain.connect(this.limiterNode); this.playings = {}; if(cb) setTimeout(cb, 0); return this; }; AudioEngineWeb.prototype.load = function(id, url, cb) { var audio = this; var req = new XMLHttpRequest(); req.open("GET", url); req.responseType = "arraybuffer"; req.addEventListener("readystatechange", function(evt) { if(req.readyState !== 4) return; try { audio.context.decodeAudioData(req.response, function(buffer) { audio.sounds[id] = buffer; if(cb) cb(); }); } catch(e) { /*throw new Error(e.message + " / id: " + id + " / url: " + url + " / status: " + req.status + " / ArrayBuffer: " + (req.response instanceof ArrayBuffer) + " / byteLength: " + (req.response && req.response.byteLength ? req.response.byteLength : "undefined"));*/ new Notification({id: "audio-download-error", title: "Problem", text: "For some reason, an audio download failed with a status of " + req.status + ". ", target: "#piano", duration: 10000}); } }); req.send(); }; AudioEngineWeb.prototype.actualPlay = function(id, vol, time, part_id) { //the old play(), but with time insted of delay_ms. if(this.paused) return; if(!this.sounds.hasOwnProperty(id)) return; var source = this.context.createBufferSource(); source.buffer = this.sounds[id]; var gain = this.context.createGain(); gain.gain.value = vol; source.connect(gain); gain.connect(this.pianoGain); source.start(time); // Patch from ste-art remedies stuttering under heavy load if(this.playings[id]) { var playing = this.playings[id]; playing.gain.gain.setValueAtTime(playing.gain.gain.value, time); playing.gain.gain.linearRampToValueAtTime(0.0, time + 0.2); playing.source.stop(time + 0.21); if(enableSynth && playing.voice) { playing.voice.stop(time); } } this.playings[id] = {"source": source, "gain": gain, "part_id": part_id}; if(enableSynth) { this.playings[id].voice = new synthVoice(id, time); } } AudioEngineWeb.prototype.play = function(id, vol, delay_ms, part_id) { if(!this.sounds.hasOwnProperty(id)) return; var time = this.context.currentTime + (delay_ms / 1000); //calculate time on note receive. var delay = delay_ms - this.threshold; if(delay<=0) this.actualPlay(id, vol, time, part_id); else { this.worker.postMessage({delay:delay,args:{action:0/*play*/,id:id, vol:vol, time:time, part_id:part_id}}); // but start scheduling right before play. } } AudioEngineWeb.prototype.actualStop = function(id, time, part_id) { if(this.playings.hasOwnProperty(id) && this.playings[id] && this.playings[id].part_id === part_id) { var gain = this.playings[id].gain.gain; gain.setValueAtTime(gain.value, time); gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16); gain.linearRampToValueAtTime(0.0, time + 0.4); this.playings[id].source.stop(time + 0.41); if(this.playings[id].voice) { this.playings[id].voice.stop(time); } this.playings[id] = null; } }; AudioEngineWeb.prototype.stop = function(id, delay_ms, part_id) { var time = this.context.currentTime + (delay_ms / 1000); var delay = delay_ms - this.threshold; if(delay<=0) this.actualStop(id, time, part_id); else { this.worker.postMessage({delay:delay,args:{action:1/*stop*/, id:id, time:time, part_id:part_id}}); } }; AudioEngineWeb.prototype.setVolume = function(vol) { AudioEngine.prototype.setVolume.call(this, vol); this.masterGain.gain.value = this.volume; }; AudioEngineWeb.prototype.resume = function() { this.paused = false; this.context.resume(); }; // Renderer classes //////////////////////////////////////////////////////////////// var Renderer = function() { }; Renderer.prototype.init = function(piano) { this.piano = piano; this.resize(); return this; }; Renderer.prototype.resize = function(width, height) { if(typeof width == "undefined") width = $(this.piano.rootElement).width(); if(typeof height == "undefined") height = Math.floor(width * 0.2); $(this.piano.rootElement).css({"height": height + "px", marginTop: Math.floor($(window).height() / 2 - height / 2) + "px"}); this.width = width * window.devicePixelRatio; this.height = height * window.devicePixelRatio; }; Renderer.prototype.visualize = function(key, color) { }; var CanvasRenderer = function() { Renderer.call(this); }; CanvasRenderer.prototype = new Renderer(); CanvasRenderer.prototype.init = function(piano) { this.canvas = document.createElement("canvas"); this.ctx = this.canvas.getContext("2d"); piano.rootElement.appendChild(this.canvas); Renderer.prototype.init.call(this, piano); // calls resize() // create render loop var self = this; var render = function() { self.redraw(); requestAnimationFrame(render); }; requestAnimationFrame(render); // add event listeners var mouse_down = false; var last_key = null; $(piano.rootElement).mousedown(function(event) { mouse_down = true; //event.stopPropagation(); event.preventDefault(); var pos = CanvasRenderer.translateMouseEvent(event); var hit = self.getHit(pos.x, pos.y); if(hit) { press(hit.key.note, hit.v); last_key = hit.key; } }); piano.rootElement.addEventListener("touchstart", function(event) { mouse_down = true; //event.stopPropagation(); event.preventDefault(); for(var i in event.changedTouches) { var pos = CanvasRenderer.translateMouseEvent(event.changedTouches[i]); var hit = self.getHit(pos.x, pos.y); if(hit) { press(hit.key.note, hit.v); last_key = hit.key; } } }, false); $(window).mouseup(function(event) { if(last_key) { release(last_key.note); } mouse_down = false; last_key = null; }); /*$(piano.rootElement).mousemove(function(event) { if(!mouse_down) return; var pos = CanvasRenderer.translateMouseEvent(event); var hit = self.getHit(pos.x, pos.y); if(hit && hit.key != last_key) { press(hit.key.note, hit.v); last_key = hit.key; } });*/ return this; }; CanvasRenderer.prototype.resize = function(width, height) { Renderer.prototype.resize.call(this, width, height); if(this.width < 52 * 2) this.width = 52 * 2; if(this.height < this.width * 0.2) this.height = Math.floor(this.width * 0.2); this.canvas.width = this.width; this.canvas.height = this.height; this.canvas.style.width = this.width / window.devicePixelRatio + "px"; this.canvas.style.height = this.height / window.devicePixelRatio + "px"; // calculate key sizes this.whiteKeyWidth = Math.floor(this.width / 52); this.whiteKeyHeight = Math.floor(this.height * 0.9); this.blackKeyWidth = Math.floor(this.whiteKeyWidth * 0.75); this.blackKeyHeight = Math.floor(this.height * 0.5); this.blackKeyOffset = Math.floor(this.whiteKeyWidth - (this.blackKeyWidth / 2)); this.keyMovement = Math.floor(this.whiteKeyHeight * 0.015); this.whiteBlipWidth = Math.floor(this.whiteKeyWidth * 0.7); this.whiteBlipHeight = Math.floor(this.whiteBlipWidth * 0.8); this.whiteBlipX = Math.floor((this.whiteKeyWidth - this.whiteBlipWidth) / 2); this.whiteBlipY = Math.floor(this.whiteKeyHeight - this.whiteBlipHeight * 1.2); this.blackBlipWidth = Math.floor(this.blackKeyWidth * 0.7); this.blackBlipHeight = Math.floor(this.blackBlipWidth * 0.8); this.blackBlipY = Math.floor(this.blackKeyHeight - this.blackBlipHeight * 1.2); this.blackBlipX = Math.floor((this.blackKeyWidth - this.blackBlipWidth) / 2); // prerender white key this.whiteKeyRender = document.createElement("canvas"); this.whiteKeyRender.width = this.whiteKeyWidth; this.whiteKeyRender.height = this.height + 10; var ctx = this.whiteKeyRender.getContext("2d"); if(ctx.createLinearGradient) { var gradient = ctx.createLinearGradient(0, 0, 0, this.whiteKeyHeight); gradient.addColorStop(0, "#eee"); gradient.addColorStop(0.75, "#fff"); gradient.addColorStop(1, "#dad4d4"); ctx.fillStyle = gradient; } else { ctx.fillStyle = "#fff"; } ctx.strokeStyle = "#000"; ctx.lineJoin = "round"; ctx.lineCap = "round"; ctx.lineWidth = 10; ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth); ctx.lineWidth = 4; ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth); // prerender black key this.blackKeyRender = document.createElement("canvas"); this.blackKeyRender.width = this.blackKeyWidth + 10; this.blackKeyRender.height = this.blackKeyHeight + 10; var ctx = this.blackKeyRender.getContext("2d"); if(ctx.createLinearGradient) { var gradient = ctx.createLinearGradient(0, 0, 0, this.blackKeyHeight); gradient.addColorStop(0, "#000"); gradient.addColorStop(1, "#444"); ctx.fillStyle = gradient; } else { ctx.fillStyle = "#000"; } ctx.strokeStyle = "#222"; ctx.lineJoin = "round"; ctx.lineCap = "round"; ctx.lineWidth = 8; ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth); ctx.lineWidth = 4; ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth); // prerender shadows this.shadowRender = []; var y = -this.canvas.height * 2; for(var j = 0; j < 2; j++) { var canvas = document.createElement("canvas"); this.shadowRender[j] = canvas; canvas.width = this.canvas.width; canvas.height = this.canvas.height; var ctx = canvas.getContext("2d"); var sharp = j ? true : false; ctx.lineJoin = "round"; ctx.lineCap = "round"; ctx.lineWidth = 1; ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; ctx.shadowBlur = this.keyMovement * 3; ctx.shadowOffsetY = -y + this.keyMovement; if(sharp) { ctx.shadowOffsetX = this.keyMovement; } else { ctx.shadowOffsetX = 0; ctx.shadowOffsetY = -y + this.keyMovement; } for(var i in this.piano.keys) { if(!this.piano.keys.hasOwnProperty(i)) continue; var key = this.piano.keys[i]; if(key.sharp != sharp) continue; if(key.sharp) { ctx.fillRect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2, y + ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth); } else { ctx.fillRect(this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2, y + ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth); } } } // update key rects for(var i in this.piano.keys) { if(!this.piano.keys.hasOwnProperty(i)) continue; var key = this.piano.keys[i]; if(key.sharp) { key.rect = new Rect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial, 0, this.blackKeyWidth, this.blackKeyHeight); } else { key.rect = new Rect(this.whiteKeyWidth * key.spatial, 0, this.whiteKeyWidth, this.whiteKeyHeight); } } }; CanvasRenderer.prototype.visualize = function(key, color) { key.timePlayed = Date.now(); key.blips.push({"time": key.timePlayed, "color": color}); }; CanvasRenderer.prototype.redraw = function() { var now = Date.now(); var timeLoadedEnd = now - 1000; var timePlayedEnd = now - 100; var timeBlipEnd = now - 1000; this.ctx.save(); this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // draw all keys for(var j = 0; j < 2; j++) { this.ctx.globalAlpha = 1.0; this.ctx.drawImage(this.shadowRender[j], 0, 0); var sharp = j ? true : false; for(var i in this.piano.keys) { if(!this.piano.keys.hasOwnProperty(i)) continue; var key = this.piano.keys[i]; if(key.sharp != sharp) continue; if(!key.loaded) { this.ctx.globalAlpha = 0.2; } else if(key.timeLoaded > timeLoadedEnd) { this.ctx.globalAlpha = ((now - key.timeLoaded) / 1000) * 0.8 + 0.2; } else { this.ctx.globalAlpha = 1.0; } var y = 0; if(key.timePlayed > timePlayedEnd) { y = Math.floor(this.keyMovement - (((now - key.timePlayed) / 100) * this.keyMovement)); } var x = Math.floor(key.sharp ? this.blackKeyOffset + this.whiteKeyWidth * key.spatial : this.whiteKeyWidth * key.spatial); var image = key.sharp ? this.blackKeyRender : this.whiteKeyRender; this.ctx.drawImage(image, x, y); // render blips if(key.blips.length) { var alpha = this.ctx.globalAlpha; var w, h; if(key.sharp) { x += this.blackBlipX; y = this.blackBlipY; w = this.blackBlipWidth; h = this.blackBlipHeight; } else { x += this.whiteBlipX; y = this.whiteBlipY; w = this.whiteBlipWidth; h = this.whiteBlipHeight; } for(var b = 0; b < key.blips.length; b++) { var blip = key.blips[b]; if(blip.time > timeBlipEnd) { this.ctx.fillStyle = blip.color; this.ctx.globalAlpha = alpha - ((now - blip.time) / 1000); this.ctx.fillRect(x, y, w, h); } else { key.blips.splice(b, 1); --b; } y -= Math.floor(h * 1.1); } } } } this.ctx.restore(); }; CanvasRenderer.prototype.renderNoteLyrics = function() { // render lyric for(var part_id in this.noteLyrics) { if(!this.noteLyrics.hasOwnProperty(i)) continue; var lyric = this.noteLyrics[part_id]; var lyric_x = x; var lyric_y = this.whiteKeyHeight + 1; this.ctx.fillStyle = key.lyric.color; var alpha = this.ctx.globalAlpha; this.ctx.globalAlpha = alpha - ((now - key.lyric.time) / 1000); this.ctx.fillRect(x, y, 10, 10); } }; CanvasRenderer.prototype.getHit = function(x, y) { for(var j = 0; j < 2; j++) { var sharp = j ? false : true; // black keys first for(var i in this.piano.keys) { if(!this.piano.keys.hasOwnProperty(i)) continue; var key = this.piano.keys[i]; if(key.sharp != sharp) continue; if(key.rect.contains(x, y)) { var v = y / (key.sharp ? this.blackKeyHeight : this.whiteKeyHeight); v += 0.25; v *= DEFAULT_VELOCITY; if(v > 1.0) v = 1.0; return {"key": key, "v": v}; } } } return null; }; CanvasRenderer.isSupported = function() { var canvas = document.createElement("canvas"); return !!(canvas.getContext && canvas.getContext("2d")); }; CanvasRenderer.translateMouseEvent = function(evt) { var element = evt.target; var offx = 0; var offy = 0; do { if(!element) break; // wtf, wtf? offx += element.offsetLeft; offy += element.offsetTop; } while(element = element.offsetParent); return { x: (evt.pageX - offx) * window.devicePixelRatio, y: (evt.pageY - offy) * window.devicePixelRatio } }; // Soundpack Stuff by electrashave ♥ //////////////////////////////////////////////////////////////// function SoundSelector(piano) { this.initialized = false; this.keys = piano.keys; this.loading = {}; this.notification; this.packs = []; this.piano = piano; this.soundSelection = localStorage.soundSelection ? localStorage.soundSelection : "MPP Classic"; this.addPack({name: "MPP Classic", keys: Object.keys(this.piano.keys), ext: ".mp3", url: "/sounds/mppclassic/"}); } SoundSelector.prototype.addPack = function(pack, load) { var self = this; self.loading[pack.url || pack] = true; function add(obj) { var added = false; for (var i = 0; self.packs.length > i; i++) { if (obj.name == self.packs[i].name) { added = true; break; } } if (added) return console.warn("Sounds already added!!"); //no adding soundpacks twice D:< if (obj.url.substr(obj.url.length-1) != "/") obj.url = obj.url + "/"; var html = document.createElement("li"); html.classList = "pack"; html.innerText = obj.name + " (" + obj.keys.length + " keys)"; html.onclick = function() { self.loadPack(obj.name); self.notification.close(); }; obj.html = html; self.packs.push(obj); self.packs.sort(function(a, b) { if(a.name < b.name) return -1; if(a.name > b.name) return 1; return 0; }); if (load) self.loadPack(obj.name); delete self.loading[obj.url]; } if (typeof pack == "string") { $.getJSON(pack + "info.json").done(function(json) { json.url = pack; add(json); }); } else add(pack); //validate packs?? }; SoundSelector.prototype.addPacks = function(packs) { for (var i = 0; packs.length > i; i++) this.addPack(packs[i]); }; SoundSelector.prototype.init = function() { var self = this; if (self.initialized) return console.warn("Sound selector already initialized!"); if (!!Object.keys(self.loading).length) return setTimeout(function() { self.init(); }, 250); $("#sound-btn").on("click", function() { if (document.getElementById("Notification-Sound-Selector") != null) return self.notification.close(); var html = document.createElement("ul"); //$(html).append("
Current Sound: " + self.soundSelection + "
"); for (var i = 0; self.packs.length > i; i++) { var pack = self.packs[i]; if (pack.name == self.soundSelection) pack.html.classList = "pack enabled"; else pack.html.classList = "pack"; html.appendChild(pack.html); } self.notification = new Notification({title: "Sound Selector", html: html, id: "Sound-Selector", duration: -1, target: "#sound-btn"}); }); self.initialized = true; self.loadPack(self.soundSelection, true); }; SoundSelector.prototype.loadPack = function(pack, f) { for (var i = 0; this.packs.length > i; i++) { var p = this.packs[i]; if (p.name == pack) { pack = p; break; } } if (typeof pack == "string") { console.warn("Sound pack does not exist! Loading default pack..."); return this.loadPack("MPP Classic"); } if (pack.name == this.soundSelection && !f) return; if (pack.keys.length != Object.keys(this.piano.keys).length) { this.piano.keys = {}; for (var i = 0; pack.keys.length > i; i++) this.piano.keys[pack.keys[i]] = this.keys[pack.keys[i]]; this.piano.renderer.resize(); } var self = this; for (var i in this.piano.keys) { if (!this.piano.keys.hasOwnProperty(i)) continue; (function() { var key = self.piano.keys[i]; key.loaded = false; self.piano.audio.load(key.note, pack.url + key.note + pack.ext, function() { key.loaded = true; key.timeLoaded = Date.now(); }); })(); } if(localStorage) localStorage.soundSelection = pack.name; this.soundSelection = pack.name; }; SoundSelector.prototype.removePack = function(name) { var found = false; for (var i = 0; this.packs.length > i; i++) { var pack = this.packs[i]; if (pack.name == name) { this.packs.splice(i, 1); if (pack.name == this.soundSelection) this.loadPack(this.packs[0].name); //add mpp default if none? break; } } if (!found) console.warn("Sound pack not found!"); }; // Pianoctor //////////////////////////////////////////////////////////////// var PianoKey = function(note, octave) { this.note = note + octave; this.baseNote = note; this.octave = octave; this.sharp = note.indexOf("s") != -1; this.loaded = false; this.timeLoaded = 0; this.domElement = null; this.timePlayed = 0; this.blips = []; }; var Piano = function(rootElement) { var piano = this; piano.rootElement = rootElement; piano.keys = {}; var white_spatial = 0; var black_spatial = 0; var black_it = 0; var black_lut = [2, 1, 2, 1, 1]; var addKey = function(note, octave) { var key = new PianoKey(note, octave); piano.keys[key.note] = key; if(key.sharp) { key.spatial = black_spatial; black_spatial += black_lut[black_it % 5]; ++black_it; } else { key.spatial = white_spatial; ++white_spatial; } } if(test_mode) { addKey("c", 2); } else { addKey("a", -1); addKey("as", -1); addKey("b", -1); var notes = "c cs d ds e f fs g gs a as b".split(" "); for(var oct = 0; oct < 7; oct++) { for(var i in notes) { addKey(notes[i], oct); } } addKey("c", 7); } this.renderer = new CanvasRenderer().init(this); window.addEventListener("resize", function() { piano.renderer.resize(); }); window.AudioContext = window.AudioContext || window.webkitAudioContext || undefined; var audio_engine = AudioEngineWeb; this.audio = new audio_engine().init(); }; Piano.prototype.play = function(note, vol, participant, delay_ms, lyric) { if(!this.keys.hasOwnProperty(note) || !participant) return; var key = this.keys[note]; if(key.loaded) this.audio.play(key.note, vol, delay_ms, participant.id); if(gMidiOutTest) gMidiOutTest(key.note, vol * 100, delay_ms); var self = this; setTimeout(function() { self.renderer.visualize(key, participant.color); if(lyric) { } var jq_namediv = $(participant.nameDiv); jq_namediv.addClass("play"); setTimeout(function() { jq_namediv.removeClass("play"); }, 30); }, delay_ms || 0); }; Piano.prototype.stop = function(note, participant, delay_ms) { if(!this.keys.hasOwnProperty(note)) return; var key = this.keys[note]; if(key.loaded) this.audio.stop(key.note, delay_ms, participant.id); if(gMidiOutTest) gMidiOutTest(key.note, 0, delay_ms); }; var gPiano = new Piano(document.getElementById("piano")); var gSoundSelector = new SoundSelector(gPiano); gSoundSelector.addPacks(["/sounds/Emotional_2.0/", "/sounds/Harp/", "/sounds/Music_Box/", "/sounds/Vintage_Upright/", "/sounds/Steinway_Grand/", "/sounds/Emotional/", "/sounds/Untitled/"]); gSoundSelector.init(); var gAutoSustain = false; var gSustain = false; var gHeldNotes = {}; var gSustainedNotes = {}; function press(id, vol) { if(!gClient.preventsPlaying() && gNoteQuota.spend(1)) { gHeldNotes[id] = true; gSustainedNotes[id] = true; gPiano.play(id, vol !== undefined ? vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), 0); gClient.startNote(id, vol); } } function release(id) { if(gHeldNotes[id]) { gHeldNotes[id] = false; if((gAutoSustain || gSustain) && !enableSynth) { gSustainedNotes[id] = true; } else { if(gNoteQuota.spend(1)) { gPiano.stop(id, gClient.getOwnParticipant(), 0); gClient.stopNote(id); gSustainedNotes[id] = false; } } } } function pressSustain() { gSustain = true; } function releaseSustain() { gSustain = false; if(!gAutoSustain) { for(var id in gSustainedNotes) { if(gSustainedNotes.hasOwnProperty(id) && gSustainedNotes[id] && !gHeldNotes[id]) { gSustainedNotes[id] = false; if(gNoteQuota.spend(1)) { gPiano.stop(id, gClient.getOwnParticipant(), 0); gClient.stopNote(id); } } } } } function getParameterByName(name, url = window.location.href) { name = name.replace(/[\[\]]/g, '\\$&'); var regex = new RegExp('[?&]' + name + '(=([^]*)|&|#|$)'), results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, ' ')); } // internet science //////////////////////////////////////////////////////////////// var channel_id = decodeURIComponent(getParameterByName('c')); if(channel_id.substr(0, 1) == "/") channel_id = channel_id.substr(1); if(channel_id == "") channel_id = "lobby"; var isProd = window.location.hostname.includes('multiplayerpiano.com') var wssport = isProd ? 443 : 8081; var protocol = isProd ? 'wss' : 'ws'//// var gClient = new Client(protocol + "://" + (isProd ? 'app.multiplayerpiano.com' : window.location.hostname) + ":" + wssport); gClient.setChannel(channel_id); gClient.start(); gClient.on("disconnect", function(evt) { console.log(evt); }); // Setting status (function() { gClient.on("status", function(status) { $("#status").text(status); }); gClient.on("count", function(count) { if(count > 0) { $("#status").html(''+count+' '+(count==1? 'person is' : 'people are')+' playing'); document.title = "Piano (" + count + ")"; } else { document.title = "Multiplayer Piano"; } }); })(); // Handle changes to participants (function() { gClient.on("participant added", function(part) { part.displayX = 150; part.displayY = 50; // add nameDiv var div = document.createElement("div"); div.className = "name"; div.participantId = part.id; div.textContent = part.name || ""; div.style.backgroundColor = part.color || "#777"; if(gClient.participantId === part.id) { $(div).addClass("me"); } if(gClient.channel && gClient.channel.crown && gClient.channel.crown.participantId === part.id) { $(div).addClass("owner"); } if(gPianoMutes.indexOf(part._id) !== -1) { $(part.nameDiv).addClass("muted-notes"); } if(gChatMutes.indexOf(part._id) !== -1) { $(part.nameDiv).addClass("muted-chat"); } div.style.display = "none"; part.nameDiv = $("#names")[0].appendChild(div); $(part.nameDiv).fadeIn(2000); // sort names var arr = $("#names .name"); arr.sort(function(a, b) { a = a.style.backgroundColor; // todo: sort based on user id instead b = b.style.backgroundColor; if (a > b) return 1; else if (a < b) return -1; else return 0; }); $("#names").html(arr); // add cursorDiv if(gClient.participantId !== part.id || gSeeOwnCursor) { var div = document.createElement("div"); div.className = "cursor"; div.style.display = "none"; part.cursorDiv = $("#cursors")[0].appendChild(div); $(part.cursorDiv).fadeIn(2000); var div = document.createElement("div"); div.className = "name"; div.style.backgroundColor = part.color || "#777" div.textContent = part.name || ""; part.cursorDiv.appendChild(div); } else { part.cursorDiv = undefined; } }); gClient.on("participant removed", function(part) { // remove nameDiv var nd = $(part.nameDiv); var cd = $(part.cursorDiv); cd.fadeOut(2000); nd.fadeOut(2000, function() { nd.remove(); cd.remove(); part.nameDiv = undefined; part.cursorDiv = undefined; }); }); gClient.on("participant update", function(part) { var name = part.name || ""; var color = part.color || "#777"; part.nameDiv.style.backgroundColor = color; part.nameDiv.textContent = name; $(part.cursorDiv) .find(".name") .text(name) .css("background-color", color); }); gClient.on("ch", function(msg) { for(var id in gClient.ppl) { if(gClient.ppl.hasOwnProperty(id)) { var part = gClient.ppl[id]; if(part.id === gClient.participantId) { $(part.nameDiv).addClass("me"); } else { $(part.nameDiv).removeClass("me"); } if(msg.ch.crown && msg.ch.crown.participantId === part.id) { $(part.nameDiv).addClass("owner"); $(part.cursorDiv).addClass("owner"); } else { $(part.nameDiv).removeClass("owner"); $(part.cursorDiv).removeClass("owner"); } if(gPianoMutes.indexOf(part._id) !== -1) { $(part.nameDiv).addClass("muted-notes"); } else { $(part.nameDiv).removeClass("muted-notes"); } if(gChatMutes.indexOf(part._id) !== -1) { $(part.nameDiv).addClass("muted-chat"); } else { $(part.nameDiv).removeClass("muted-chat"); } } } }); function updateCursor(msg) { const part = gClient.ppl[msg.id]; if (part && part.cursorDiv) { part.cursorDiv.style.left = msg.x + "%"; part.cursorDiv.style.top = msg.y + "%"; } } gClient.on("m", updateCursor); gClient.on("participant added", updateCursor); })(); // Handle changes to crown (function() { var jqcrown = $('').appendTo(document.body).hide(); var jqcountdown = $('').appendTo(jqcrown); var countdown_interval; jqcrown.click(function() { gClient.sendArray([{m: "chown", id: gClient.participantId}]); }); gClient.on("ch", function(msg) { if(msg.ch.crown) { var crown = msg.ch.crown; if(!crown.participantId || !gClient.ppl[crown.participantId]) { var land_time = crown.time + 2000 - gClient.serverTimeOffset; var avail_time = crown.time + 15000 - gClient.serverTimeOffset; jqcountdown.text(""); jqcrown.show(); if(land_time - Date.now() <= 0) { jqcrown.css({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"}); } else { jqcrown.css({"left": crown.startPos.x + "%", "top": crown.startPos.y + "%"}); jqcrown.addClass("spin"); jqcrown.animate({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"}, 2000, "linear", function() { jqcrown.removeClass("spin"); }); } clearInterval(countdown_interval); countdown_interval = setInterval(function() { var time = Date.now(); if(time >= land_time) { var ms = avail_time - time; if(ms > 0) { jqcountdown.text(Math.ceil(ms / 1000) + "s"); } else { jqcountdown.text(""); clearInterval(countdown_interval); } } }, 1000); } else { jqcrown.hide(); } } else { jqcrown.hide(); } }); gClient.on("disconnect", function() { jqcrown.fadeOut(2000); }); })(); // Playing notes gClient.on("n", function(msg) { var t = msg.t - gClient.serverTimeOffset + TIMING_TARGET - Date.now(); var participant = gClient.findParticipantById(msg.p); if(gPianoMutes.indexOf(participant._id) !== -1) return; for(var i = 0; i < msg.n.length; i++) { var note = msg.n[i]; var ms = t + (note.d || 0); if(ms < 0) { ms = 0; } else if(ms > 10000) continue; if(note.s) { gPiano.stop(note.n, participant, ms); } else { var vel = (typeof note.v !== "undefined")? parseFloat(note.v) : DEFAULT_VELOCITY; if(!vel) vel = 0; else if(vel < 0) vel = 0; else if (vel > 1) vel = 1; gPiano.play(note.n, vel, participant, ms); if(enableSynth) { gPiano.stop(note.n, participant, ms + 1000); } } } }); // Send cursor updates var mx = 0, last_mx = -10, my = 0, last_my = -10; setInterval(function() { if(Math.abs(mx - last_mx) > 0.1 || Math.abs(my - last_my) > 0.1) { last_mx = mx; last_my = my; gClient.sendArray([{m: "m", x: mx, y: my}]); if(gSeeOwnCursor) { gClient.emit("m", { m: "m", id: gClient.participantId, x: mx, y: my }); } var part = gClient.getOwnParticipant(); if(part) { part.x = mx; part.y = my; } } }, 50); $(document).mousemove(function(event) { mx = ((event.pageX / $(window).width()) * 100).toFixed(2); my = ((event.pageY / $(window).height()) * 100).toFixed(2); }); // Room settings button (function() { gClient.on("ch", function(msg) { if(gClient.isOwner()) { $("#room-settings-btn").show(); } else { $("#room-settings-btn").hide(); } }); $("#room-settings-btn").click(function(evt) { if(gClient.channel && gClient.isOwner()) { var settings = gClient.channel.settings; openModal("#room-settings"); setTimeout(function() { $("#room-settings .checkbox[name=visible]").prop("checked", settings.visible); $("#room-settings .checkbox[name=chat]").prop("checked", settings.chat); $("#room-settings .checkbox[name=crownsolo]").prop("checked", settings.crownsolo); $("#room-settings input[name=color]").val(settings.color); }, 100); } }); $("#room-settings .submit").click(function() { var settings = { visible: $("#room-settings .checkbox[name=visible]").is(":checked"), chat: $("#room-settings .checkbox[name=chat]").is(":checked"), crownsolo: $("#room-settings .checkbox[name=crownsolo]").is(":checked"), color: $("#room-settings input[name=color]").val() }; gClient.setChannelSettings(settings); closeModal(); }); $("#room-settings .drop-crown").click(function() { closeModal(); if(confirm("This will drop the crown...!")) gClient.sendArray([{m: "chown"}]); }); })(); // Handle notifications gClient.on("notification", function(msg) { new Notification(msg); }); // Don't foget spin gClient.on("ch", function(msg) { var chidlo = msg.ch._id.toLowerCase(); if(chidlo === "spin" || chidlo.substr(-5) === "/spin") { $("#piano").addClass("spin"); } else { $("#piano").removeClass("spin"); } }); /*function eb() { if(gClient.channel && gClient.channel._id.toLowerCase() === "test/fishing") { ebsprite.start(gClient); } else { ebsprite.stop(); } } if(ebsprite) { gClient.on("ch", eb); eb(); }*/ // Crownsolo notice gClient.on("ch", function(msg) { let notice = ""; let has_notice = false; if(msg.ch.settings.crownsolo) { has_notice = true; notice += 'This room is set to "only the owner can play."
'; } if(msg.ch.settings['no cussing']){ has_notice = true; notice += 'This room is set to "no cussing."
'; } let notice_div = $("#room-notice"); if(has_notice) { notice_div.html(notice); if(notice_div.is(':hidden')) notice_div.fadeIn(1000); } else { if(notice_div.is(':visible')) notice_div.fadeOut(1000); } }); gClient.on("disconnect", function() { $("#room-notice").fadeOut(1000); }); // Background color (function() { var old_color1 = new Color("#000000"); var old_color2 = new Color("#000000"); function setColor(hex, hex2) { var color1 = new Color(hex); var color2 = new Color(hex2 || hex); if(!hex2) color2.add(-0x40, -0x40, -0x40); var bottom = document.getElementById("bottom"); var duration = 500; var step = 0; var steps = 30; var step_ms = duration / steps; var difference = new Color(color1.r, color1.g, color1.b); difference.r -= old_color1.r; difference.g -= old_color1.g; difference.b -= old_color1.b; var inc1 = new Color(difference.r / steps, difference.g / steps, difference.b / steps); difference = new Color(color2.r, color2.g, color2.b); difference.r -= old_color2.r; difference.g -= old_color2.g; difference.b -= old_color2.b; var inc2 = new Color(difference.r / steps, difference.g / steps, difference.b / steps); var iv; iv = setInterval(function() { old_color1.add(inc1.r, inc1.g, inc1.b); old_color2.add(inc2.r, inc2.g, inc2.b); document.body.style.background = "radial-gradient(ellipse at center, "+old_color1.toHexa()+" 0%,"+old_color2.toHexa()+" 100%)"; bottom.style.background = old_color2.toHexa(); if(++step >= steps) { clearInterval(iv); old_color1 = color1; old_color2 = color2; document.body.style.background = "radial-gradient(ellipse at center, "+color1.toHexa()+" 0%,"+color2.toHexa()+" 100%)"; bottom.style.background = color2.toHexa(); } }, step_ms); } function setColorToDefault() { setColor("#000000", "#000000"); } setColorToDefault(); gClient.on("ch", function(ch) { if(ch.ch.settings) { if(ch.ch.settings.color) { setColor(ch.ch.settings.color, ch.ch.settings.color2); } else { setColorToDefault(); } } }); })(); var gPianoMutes = (localStorage.pianoMutes ? localStorage.pianoMutes : "").split(',').filter(v => v); var gChatMutes = (localStorage.pianoMutes ? localStorage.pianoMutes : "").split(',').filter(v => v); var volume_slider = document.getElementById("volume-slider"); volume_slider.value = gPiano.audio.volume; $("#volume-label").text("Volume: " + Math.floor(gPiano.audio.volume * 100) + "%"); volume_slider.addEventListener("input", function(evt) { var v = +volume_slider.value; gPiano.audio.setVolume(v); if (window.localStorage) localStorage.volume = v; $("#volume-label").text("Volume: " + Math.floor(v * 100) + "%"); }); var Note = function(note, octave) { this.note = note; this.octave = octave || 0; }; var n = function(a, b) { return {note: new Note(a, b), held: false}; }; var key_binding = { 65: n("gs"), 90: n("a"), 83: n("as"), 88: n("b"), 67: n("c", 1), 70: n("cs", 1), 86: n("d", 1), 71: n("ds", 1), 66: n("e", 1), 78: n("f", 1), 74: n("fs", 1), 77: n("g", 1), 75: n("gs", 1), 188: n("a", 1), 76: n("as", 1), 190: n("b", 1), 191: n("c", 2), 222: n("cs", 2), 49: n("gs", 1), 81: n("a", 1), 50: n("as", 1), 87: n("b", 1), 69: n("c", 2), 52: n("cs", 2), 82: n("d", 2), 53: n("ds", 2), 84: n("e", 2), 89: n("f", 2), 55: n("fs", 2), 85: n("g", 2), 56: n("gs", 2), 73: n("a", 2), 57: n("as", 2), 79: n("b", 2), 80: n("c", 3), 189: n("cs", 3), 173: n("cs", 3), // firefox why 219: n("d", 3), 187: n("ds", 3), 61: n("ds", 3), // firefox why 221: n("e", 3) }; var capsLockKey = false; var transpose_octave = 0; function handleKeyDown(evt) { //console.log(evt); var code = parseInt(evt.keyCode); if(key_binding[code] !== undefined) { var binding = key_binding[code]; if(!binding.held) { binding.held = true; var note = binding.note; var octave = 1 + note.octave + transpose_octave; if(evt.shiftKey) ++octave; else if(capsLockKey || evt.ctrlKey) --octave; note = note.note + octave; var vol = velocityFromMouseY(); press(note, vol); } if(++gKeyboardSeq == 3) { gKnowsYouCanUseKeyboard = true; if(window.gKnowsYouCanUseKeyboardTimeout) clearTimeout(gKnowsYouCanUseKeyboardTimeout); if(localStorage) localStorage.knowsYouCanUseKeyboard = true; if(window.gKnowsYouCanUseKeyboardNotification) gKnowsYouCanUseKeyboardNotification.close(); } evt.preventDefault(); evt.stopPropagation(); return false; } else if(code == 20) { // Caps Lock capsLockKey = true; evt.preventDefault(); } else if(code === 0x20) { // Space Bar pressSustain(); evt.preventDefault(); } else if((code === 38 || code === 39) && transpose_octave < 3) { ++transpose_octave; } else if((code === 40 || code === 37) && transpose_octave > -2) { --transpose_octave; } else if(code == 9) { // Tab (don't tab away from the piano) evt.preventDefault(); } else if(code == 8) { // Backspace (don't navigate Back) gAutoSustain = !gAutoSustain; evt.preventDefault(); } }; function handleKeyUp(evt) { var code = parseInt(evt.keyCode); if(key_binding[code] !== undefined) { var binding = key_binding[code]; if(binding.held) { binding.held = false; var note = binding.note; var octave = 1 + note.octave + transpose_octave; if(evt.shiftKey) ++octave; else if(capsLockKey || evt.ctrlKey) --octave; note = note.note + octave; release(note); } evt.preventDefault(); evt.stopPropagation(); return false; } else if(code == 20) { // Caps Lock capsLockKey = false; evt.preventDefault(); } else if(code === 0x20) { // Space Bar releaseSustain(); evt.preventDefault(); } }; function handleKeyPress(evt) { evt.preventDefault(); evt.stopPropagation(); if(evt.keyCode == 27 || evt.keyCode == 13) { //$("#chat input").focus(); } return false; }; var recapListener = function(evt) { captureKeyboard(); }; function captureKeyboard() { $("#piano").off("mousedown", recapListener); $("#piano").off("touchstart", recapListener); $(document).on("keydown", handleKeyDown ); $(document).on("keyup", handleKeyUp); $(window).on("keypress", handleKeyPress ); }; function releaseKeyboard() { $(document).off("keydown", handleKeyDown ); $(document).off("keyup", handleKeyUp); $(window).off("keypress", handleKeyPress ); $("#piano").on("mousedown", recapListener); $("#piano").on("touchstart", recapListener); }; captureKeyboard(); var velocityFromMouseY = function() { return 0.1 + (my / 100) * 0.6; }; // NoteQuota var gNoteQuota = (function() { var last_rat = 0; var nqjq = $("#quota .value"); setInterval(function() { gNoteQuota.tick(); }, 2000); return new NoteQuota(function(points) { // update UI var rat = (points / this.max) * 100; if(rat <= last_rat) nqjq.stop(true, true).css("width", rat.toFixed(0) + "%"); else nqjq.stop(true, true).animate({"width": rat.toFixed(0) + "%"}, 2000, "linear"); last_rat = rat; }); })(); gClient.on("nq", function(nq_params) { gNoteQuota.setParams(nq_params); }); gClient.on("disconnect", function() { gNoteQuota.setParams(NoteQuota.PARAMS_OFFLINE); }); // click participant names (function() { var ele = document.getElementById("names"); var touchhandler = function(e) { var target_jq = $(e.target); if(target_jq.hasClass("name")) { target_jq.addClass("play"); if(e.target.participantId == gClient.participantId) { openModal("#rename", "input[name=name]"); setTimeout(function() { $("#rename input[name=name]").val(gClient.ppl[gClient.participantId].name); $("#rename input[name=color]").val(gClient.ppl[gClient.participantId].color); }, 100); } else if(e.target.participantId) { var id = e.target.participantId; var part = gClient.ppl[id] || null; if(part) { participantMenu(part); e.stopPropagation(); } } } }; ele.addEventListener("mousedown", touchhandler); ele.addEventListener("touchstart", touchhandler); var releasehandler = function(e) { $("#names .name").removeClass("play"); }; document.body.addEventListener("mouseup", releasehandler); document.body.addEventListener("touchend", releasehandler); var removeParticipantMenus = function() { $(".participant-menu").remove(); $(".participantSpotlight").hide(); document.removeEventListener("mousedown", removeParticipantMenus); document.removeEventListener("touchstart", removeParticipantMenus); }; var participantMenu = function(part) { if(!part) return; removeParticipantMenus(); document.addEventListener("mousedown", removeParticipantMenus); document.addEventListener("touchstart", removeParticipantMenus); $("#" + part.id).find(".enemySpotlight").show(); var menu = $(''); $("body").append(menu); // move menu to name position var jq_nd = $(part.nameDiv); var pos = jq_nd.position(); menu.css({ "top": pos.top + jq_nd.height() + 15, "left": pos.left + 6, "background": part.color || "black" }); menu.on("mousedown touchstart", function(evt) { evt.stopPropagation(); var target = $(evt.target); if(target.hasClass("menu-item")) { target.addClass("clicked"); menu.fadeOut(200, function() { removeParticipantMenus(); }); } }); // this spaces stuff out but also can be used for informational $('').appendTo(menu).text(part._id); // add menu items if(gPianoMutes.indexOf(part._id) == -1) { $(' ').appendTo(menu) .on("mousedown touchstart", function(evt) { gPianoMutes.push(part._id); if(localStorage) localStorage.pianoMutes = gPianoMutes.join(','); $(part.nameDiv).addClass("muted-notes"); }); } else { $(' ').appendTo(menu) .on("mousedown touchstart", function(evt) { var i; while((i = gPianoMutes.indexOf(part._id)) != -1) gPianoMutes.splice(i, 1); if(localStorage) localStorage.pianoMutes = gPianoMutes.join(','); $(part.nameDiv).removeClass("muted-notes"); }); } if(gChatMutes.indexOf(part._id) == -1) { $(' ').appendTo(menu) .on("mousedown touchstart", function(evt) { gChatMutes.push(part._id); if(localStorage) localStorage.chatMutes = gChatMutes.join(','); $(part.nameDiv).addClass("muted-chat"); }); } else { $(' ').appendTo(menu) .on("mousedown touchstart", function(evt) { var i; while((i = gChatMutes.indexOf(part._id)) != -1) gChatMutes.splice(i, 1); if(localStorage) localStorage.chatMutes = gChatMutes.join(','); $(part.nameDiv).removeClass("muted-chat"); }); } if(!(gPianoMutes.indexOf(part._id) >= 0) || !(gChatMutes.indexOf(part._id) >= 0)) { $(' ').appendTo(menu) .on("mousedown touchstart", function(evt) { gPianoMutes.push(part._id); if(localStorage) localStorage.pianoMutes = gPianoMutes.join(','); gChatMutes.push(part._id); if(localStorage) localStorage.chatMutes = gChatMutes.join(','); $(part.nameDiv).addClass("muted-notes"); $(part.nameDiv).addClass("muted-chat"); }); } if((gPianoMutes.indexOf(part._id) >= 0) || (gChatMutes.indexOf(part._id) >= 0)) { $(' ').appendTo(menu) .on("mousedown touchstart", function(evt) { var i; while((i = gPianoMutes.indexOf(part._id)) != -1) gPianoMutes.splice(i, 1); while((i = gChatMutes.indexOf(part._id)) != -1) gChatMutes.splice(i, 1); if(localStorage) localStorage.pianoMutes = gPianoMutes.join(','); if(localStorage) localStorage.chatMutes = gChatMutes.join(','); $(part.nameDiv).removeClass("muted-notes"); $(part.nameDiv).removeClass("muted-chat"); }); } if(gClient.isOwner()) { $(' ').appendTo(menu) .on("mousedown touchstart", function(evt) { if(confirm("Give room ownership to "+part.name+"?")) gClient.sendArray([{m: "chown", id: part.id}]); }); $(' ').appendTo(menu) .on("mousedown touchstart", function(evt) { var minutes = prompt("How many minutes? (0-60)", "30"); if(minutes === null) return; minutes = parseFloat(minutes) || 0; var ms = minutes * 60 * 1000; gClient.sendArray([{m: "kickban", _id: part._id, ms: ms}]); }); } menu.fadeIn(100); }; })(); // Notification class //////////////////////////////////////////////////////////////// var Notification = function(par) { if(this instanceof Notification === false) throw("yeet"); EventEmitter.call(this); var par = par || {}; this.id = "Notification-" + (par.id || Math.random()); this.title = par.title || ""; this.text = par.text || ""; this.html = par.html || ""; this.target = $(par.target || "#piano"); this.duration = par.duration || 30000; this["class"] = par["class"] || "classic"; var self = this; var eles = $("#" + this.id); if(eles.length > 0) { eles.remove(); } this.domElement = $('