stachu3478

Multiplayer Piano Bot by Peripheral Assassin v1.2

Oct 29th, 2017
2,768
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // www.multiplayerpiano.com
  2. // Multiplayer Piano Mod/Bot - PeriOS
  3. // version 1.4 (uploaded 2019/05/01)
  4. var version = "1.4";
  5. // Created by Peripheral Assassin (Poland)
  6. // Script contains upgraded version of script.js and my scripts and no songs.
  7. // Features:
  8. // -records songs (full automatically)
  9. // -plays these songs
  10. // -collects informations about users
  11. // -and more!
  12.  
  13. // previous versions:
  14. // 1.0: http://pastebin.com/s1y0k9Li
  15. // 1.1: http://pastebin.com/HZeggFjw
  16.  
  17. // How to install:
  18. // Block http://www.multiplayerpiano.com/script.js in your browser (in ad block plus click on the upper icon - settings - add your own filters - paste the link and click 'add' button and refresh MPP site),
  19. // Paste this script into console (F12)
  20.  
  21. // Bugs? Send e-mail to me - [email protected]
  22.  
  23. // No songs while you get back? Type \reload !
  24. // \itball command is disabled PERSISTENT.
  25. // To put clients in other rooms, just click on the room list.
  26. // IMPORTMIDI section is restricted for plain data. Use chooper100's MIDI importer and put generated files with upload buttom.
  27. //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  28.  
  29. //new page handler
  30. /*
  31. var button = document.createElement("div");
  32. button.className = "ugly-button";
  33. button.id = "record-btn";
  34. button.innerText = "Record MP3";
  35. button.style.float = "right";
  36. document.getElementsByClassName("relative")[0].appendChild(button);
  37.  
  38. var button = document.createElement("div");
  39. button.className = "ugly-button";
  40. button.id = "synth-btn";
  41. button.innerText = "Synth";
  42. button.style.float = "right";
  43. document.getElementsByClassName("relative")[0].appendChild(button);
  44. */
  45. //script.js modified
  46.  
  47. // 钢琴
  48.  
  49. $(function() {
  50.  
  51.     var test_mode = (window.location.hash && window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i));
  52.  
  53.     var gSeeOwnCursor = (window.location.hash && window.location.hash.match(/^(?:#.+)*#seeowncursor(?:#.+)*$/i));
  54.  
  55.     var gMidiOutTest = (window.location.hash && window.location.hash.match(/^(?:#.+)*#midiout(?:#.+)*$/i)); // todo this is no longer needed
  56.  
  57.     if (!Array.prototype.indexOf) {
  58.         Array.prototype.indexOf = function(elt /*, from*/) {
  59.             var len = this.length >>> 0;
  60.             var from = Number(arguments[1]) || 0;
  61.             from = (from < 0) ? Math.ceil(from) : Math.floor(from);
  62.             if (from < 0) from += len;
  63.             for (; from < len; from++) {
  64.                 if (from in this && this[from] === elt) return from;
  65.             }
  66.             return -1;
  67.         };
  68.     }
  69.  
  70.     window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame
  71.         || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
  72.         || function (cb) { setTimeout(cb, 1000 / 30); };
  73.  
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80.  
  81.    
  82.  
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89.  
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96.  
  97.  
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.     var DEFAULT_VELOCITY = 0.5;
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146.  
  147.  
  148.  
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155.     var TIMING_TARGET = 1000;
  156.  
  157.     var localSound = true;
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173.  
  174.  
  175. // Utility
  176.  
  177. ////////////////////////////////////////////////////////////////
  178.  
  179.  
  180.  
  181. var Rect = function(x, y, w, h) {
  182.     this.x = x;
  183.     this.y = y;
  184.     this.w = w;
  185.     this.h = h;
  186.     this.x2 = x + w;
  187.     this.y2 = y + h;
  188. };
  189. Rect.prototype.contains = function(x, y) {
  190.     return (x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2);
  191. };
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204.  
  205.  
  206.  
  207.  
  208. // performing translation
  209.  
  210. ////////////////////////////////////////////////////////////////
  211.  
  212.     var Translation = (function() {
  213.         var strings = {
  214.             "people are playing": {
  215.                 "pt": "pessoas estão jogando",
  216.                 "es": "personas están jugando",
  217.                 "ru": "человек играет",
  218.                 "fr": "personnes jouent",
  219.                 "ja": "人が遊んでいる",
  220.                 "de": "Leute spielen",
  221.                 "zh": "人在玩",
  222.                 "nl": "mensen spelen",
  223.                 "pl": "osób grają",
  224.                 "hu": "ember játszik"
  225.             },
  226.             "New Room...": {
  227.                 "pt": "Nova Sala ...",
  228.                 "es": "Nueva sala de...",
  229.                 "ru": "Новый номер...",
  230.                 "ja": "新しい部屋",
  231.                 "zh": "新房间",
  232.                 "nl": "nieuwe Kamer",
  233.                 "hu": "új szoba"
  234.             },
  235.             "room name": {
  236.                 "pt": "nome da sala",
  237.                 "es": "sala de nombre",
  238.                 "ru": "название комнаты",
  239.                 "fr": "nom de la chambre",
  240.                 "ja": "ルーム名",
  241.                 "de": "Raumnamen",
  242.                 "zh": "房间名称",
  243.                 "nl": "kamernaam",
  244.                 "pl": "nazwa pokój",
  245.                 "hu": "szoba neve"
  246.             },
  247.             "Visible (open to everyone)": {
  248.                 "pt": "Visível (aberto a todos)",
  249.                 "es": "Visible (abierto a todo el mundo)",
  250.                 "ru": "Visible (открытый для всех)",
  251.                 "fr": "Visible (ouvert à tous)",
  252.                 "ja": "目に見える(誰にでも開いている)",
  253.                 "de": "Sichtbar (offen für alle)",
  254.                 "zh": "可见(向所有人开放)",
  255.                 "nl": "Zichtbaar (open voor iedereen)",
  256.                 "pl": "Widoczne (otwarte dla wszystkich)",
  257.                 "hu": "Látható (nyitott mindenki számára)"
  258.             },
  259.             "Enable Chat": {
  260.                 "pt": "Ativar bate-papo",
  261.                 "es": "Habilitar chat",
  262.                 "ru": "Включить чат",
  263.                 "fr": "Activer discuter",
  264.                 "ja": "チャットを有効にする",
  265.                 "de": "aktivieren Sie chatten",
  266.                 "zh": "启用聊天",
  267.                 "nl": "Chat inschakelen",
  268.                 "pl": "Włącz czat",
  269.                 "hu": "a csevegést"
  270.             },
  271.             "Play Alone": {
  272.                 "pt": "Jogar Sozinho",
  273.                 "es": "Jugar Solo",
  274.                 "ru": "Играть в одиночку",
  275.                 "fr": "Jouez Seul",
  276.                 "ja": "一人でプレイ",
  277.                 "de": "Alleine Spielen",
  278.                 "zh": "独自玩耍",
  279.                 "nl": "Speel Alleen",
  280.                 "pl": "Zagraj sam",
  281.                 "hu": "Játssz egyedül"
  282.             }
  283.             // todo: it, tr, th, sv, ar, fi, nb, da, sv, he, cs, ko, ro, vi, id, nb, el, sk, bg, lt, sl, hr
  284.             // todo: Connecting, Offline mode, input placeholder, Notifications
  285.         };
  286.  
  287.         var setLanguage = function(lang) {
  288.             language = lang
  289.         };
  290.  
  291.         var getLanguage = function() {
  292.             if(window.navigator && navigator.language && navigator.language.length >= 2) {
  293.                 return navigator.language.substr(0, 2).toLowerCase();
  294.             } else {
  295.                 return "en";
  296.             }
  297.         };
  298.  
  299.         var get = function(text, lang) {
  300.             if(typeof lang === "undefined") lang = language;
  301.             var row = strings[text];
  302.             if(row == undefined) return text;
  303.             var string = row[lang];
  304.             if(string == undefined) return text;
  305.             return string;
  306.         };
  307.  
  308.         var perform = function(lang) {
  309.             if(typeof lang === "undefined") lang = language;
  310.             $(".translate").each(function(i, ele) {
  311.                 var th = $(this);
  312.                 if(ele.tagName && ele.tagName.toLowerCase() == "input") {
  313.                     if(typeof ele.placeholder != "undefined") {
  314.                         th.attr("placeholder", get(th.attr("placeholder"), lang))
  315.                     }
  316.                 } else {
  317.                     th.text(get(th.text(), lang));
  318.                 }
  319.             });
  320.         };
  321.  
  322.         var language = getLanguage();
  323.  
  324.         return {
  325.             setLanguage: setLanguage,
  326.             getLanguage: getLanguage,
  327.             get: get,
  328.             perform: perform
  329.         };
  330.     })();
  331.  
  332.     Translation.perform();
  333.  
  334.  
  335.  
  336.  
  337.  
  338.  
  339.  
  340.  
  341.  
  342.  
  343.  
  344.  
  345.  
  346.  
  347.  
  348. // AudioEngine classes
  349.  
  350. ////////////////////////////////////////////////////////////////
  351.  
  352.     var AudioEngine = function() {
  353.     };
  354.  
  355.     AudioEngine.prototype.init = function(cb) {
  356.         this.volume = 0.6;
  357.         this.sounds = {};
  358.         this.paused = true;
  359.         return this;
  360.     };
  361.  
  362.     AudioEngine.prototype.load = function(id, url, cb) {
  363.     };
  364.  
  365.     AudioEngine.prototype.play = function() {
  366.     };
  367.  
  368.     AudioEngine.prototype.stop = function() {
  369.     };
  370.  
  371.     AudioEngine.prototype.setVolume = function(vol) {
  372.         this.volume = vol;
  373.     };
  374.    
  375.     AudioEngine.prototype.resume = function() {
  376.         this.paused = false;
  377.     };
  378.  
  379.  
  380.     AudioEngineWeb = function() {
  381.         this.threshold = 1000;
  382.         this.worker = new Worker("/workerTimer.js");
  383.         var self = this;
  384.         this.worker.onmessage = function(event)
  385.             {
  386.                 if(event.data.args)
  387.                 if(event.data.args.action==0)
  388.                 {
  389.                     self.actualPlay(event.data.args.id, event.data.args.vol, event.data.args.time, event.data.args.part_id);
  390.                 }
  391.                 else
  392.                 {
  393.                     self.actualStop(event.data.args.id, event.data.args.time, event.data.args.part_id);
  394.                 }
  395.             }
  396.     };
  397.  
  398.     AudioEngineWeb.prototype = new AudioEngine();
  399.  
  400.     AudioEngineWeb.prototype.init = function(cb) {
  401.         AudioEngine.prototype.init.call(this);
  402.  
  403.         this.context = new AudioContext();
  404.  
  405.         this.masterGain = this.context.createGain();
  406.         this.masterGain.connect(this.context.destination);
  407.         this.masterGain.gain.value = this.volume;
  408.  
  409.         this.limiterNode = this.context.createDynamicsCompressor();
  410.         this.limiterNode.threshold.value = -10;
  411.         this.limiterNode.knee.value = 0;
  412.         this.limiterNode.ratio.value = 20;
  413.         this.limiterNode.attack.value = 0;
  414.         this.limiterNode.release.value = 0.1;
  415.         this.limiterNode.connect(this.masterGain);
  416.  
  417.         // for synth mix
  418.         this.pianoGain = this.context.createGain();
  419.         this.pianoGain.gain.value = 0.5;
  420.         this.pianoGain.connect(this.limiterNode);
  421.         this.synthGain = this.context.createGain();
  422.         this.synthGain.gain.value = 0.5;
  423.         this.synthGain.connect(this.limiterNode);
  424.  
  425.         this.playings = {};
  426.        
  427.         if(cb) setTimeout(cb, 0);
  428.         return this;
  429.     };
  430.  
  431.     AudioEngineWeb.prototype.load = function(id, url, cb) {
  432.         var audio = this;
  433.         var req = new XMLHttpRequest();
  434.         req.open("GET", url);
  435.         req.responseType = "arraybuffer";
  436.         req.addEventListener("readystatechange", function(evt) {
  437.             if(req.readyState !== 4) return;
  438.             try {
  439.                 audio.context.decodeAudioData(req.response, function(buffer) {
  440.                     audio.sounds[id] = buffer;
  441.                     if(cb) cb();
  442.                 });
  443.             } catch(e) {
  444.                 /*throw new Error(e.message
  445.                     + " / id: " + id
  446.                     + " / url: " + url
  447.                     + " / status: " + req.status
  448.                     + " / ArrayBuffer: " + (req.response instanceof ArrayBuffer)
  449.                     + " / byteLength: " + (req.response && req.response.byteLength ? req.response.byteLength : "undefined"));*/
  450.                 new Notification({id: "audio-download-error", title: "Problem", text: "For some reason, an audio download failed with a status of " + req.status + ". ",
  451.                     target: "#piano", duration: 10000});
  452.             }
  453.         });
  454.         req.send();
  455.     };
  456.  
  457.     AudioEngineWeb.prototype.actualPlay = function(id, vol, time, part_id) { //the old play(), but with time insted of delay_ms.
  458.         if(this.paused) return;
  459.         if(!this.sounds.hasOwnProperty(id)) return;
  460.         var source = this.context.createBufferSource();
  461.         source.buffer = this.sounds[id];
  462.         var gain = this.context.createGain();
  463.         gain.gain.value = vol;
  464.         source.connect(gain);
  465.         gain.connect(this.pianoGain);
  466.         source.start(time);
  467.         // Patch from ste-art remedies stuttering under heavy load
  468.         if(this.playings[id]) {
  469.             var playing = this.playings[id];
  470.             playing.gain.gain.setValueAtTime(playing.gain.gain.value, time);
  471.             playing.gain.gain.linearRampToValueAtTime(0.0, time + 0.2);
  472.             playing.source.stop(time + 0.21);
  473.             if(enableSynth && playing.voice) {
  474.                 playing.voice.stop(time);
  475.             }
  476.         }
  477.         this.playings[id] = {"source": source, "gain": gain, "part_id": part_id};
  478.  
  479.         if(enableSynth) {
  480.             this.playings[id].voice = new synthVoice(id, time);
  481.         }
  482.     }
  483.    
  484.     AudioEngineWeb.prototype.play = function(id, vol, delay_ms, part_id)
  485.     {
  486.         if(!this.sounds.hasOwnProperty(id)) return;
  487.         var time = this.context.currentTime + (delay_ms / 1000); //calculate time on note receive.
  488.         var delay = delay_ms - this.threshold;
  489.         if(delay<=0) this.actualPlay(id, vol, time, part_id);
  490.         else {
  491.             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.
  492.         }
  493.     }
  494.    
  495.     AudioEngineWeb.prototype.actualStop = function(id, time, part_id) {
  496.         if(this.playings.hasOwnProperty(id) && this.playings[id] && this.playings[id].part_id === part_id) {
  497.             var gain = this.playings[id].gain.gain;
  498.             gain.setValueAtTime(gain.value, time);
  499.             gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16);
  500.             gain.linearRampToValueAtTime(0.0, time + 0.4);
  501.             this.playings[id].source.stop(time + 0.41);
  502.            
  503.  
  504.             if(this.playings[id].voice) {
  505.                 this.playings[id].voice.stop(time);
  506.             }
  507.  
  508.             this.playings[id] = null;
  509.         }
  510.     };
  511.  
  512.     AudioEngineWeb.prototype.stop = function(id, delay_ms, part_id) {
  513.             var time = this.context.currentTime + (delay_ms / 1000);
  514.             var delay = delay_ms - this.threshold;
  515.             if(delay<=0) this.actualStop(id, time, part_id);
  516.             else {
  517.                 this.worker.postMessage({delay:delay,args:{action:1/*stop*/, id:id, time:time, part_id:part_id}});
  518.             }
  519.     };
  520.  
  521.     AudioEngineWeb.prototype.setVolume = function(vol) {
  522.         AudioEngine.prototype.setVolume.call(this, vol);
  523.         this.masterGain.gain.value = this.volume;
  524.     };
  525.    
  526.     AudioEngineWeb.prototype.resume = function() {
  527.         this.paused = false;
  528.         this.context.resume();
  529.     };
  530.  
  531.  
  532.  
  533.  
  534.  
  535.  
  536.  
  537.  
  538.  
  539.  
  540.  
  541.  
  542.  
  543.  
  544.  
  545.  
  546.  
  547.  
  548.  
  549.  
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556. // Renderer classes
  557.  
  558. ////////////////////////////////////////////////////////////////
  559.  
  560.     var Renderer = function() {
  561.     };
  562.  
  563.     Renderer.prototype.init = function(piano) {
  564.         this.piano = piano;
  565.         this.resize();
  566.         return this;
  567.     };
  568.  
  569.     Renderer.prototype.resize = function(width, height) {
  570.         if(typeof width == "undefined") width = $(this.piano.rootElement).width();
  571.         if(typeof height == "undefined") height = Math.floor(width * 0.2);
  572.         $(this.piano.rootElement).css({"height": height + "px", marginTop: Math.floor($(window).height() / 2 - height / 2) + "px"});
  573.         this.width = width * window.devicePixelRatio;
  574.         this.height = height * window.devicePixelRatio;
  575.     };
  576.  
  577.     Renderer.prototype.visualize = function(key, color) {
  578.     };
  579.  
  580.  
  581.  
  582.  
  583.     var CanvasRenderer = function() {
  584.         Renderer.call(this);
  585.     };
  586.  
  587.     CanvasRenderer.prototype = new Renderer();
  588.  
  589.     CanvasRenderer.prototype.init = function(piano) {
  590.         this.canvas = document.createElement("canvas");
  591.         this.ctx = this.canvas.getContext("2d");
  592.         piano.rootElement.appendChild(this.canvas);
  593.  
  594.         Renderer.prototype.init.call(this, piano); // calls resize()
  595.  
  596.         // create render loop
  597.         var self = this;
  598.         var render = function() {
  599.             self.redraw();
  600.             requestAnimationFrame(render);
  601.         };
  602.         requestAnimationFrame(render);
  603.  
  604.         // add event listeners
  605.         var mouse_down = false;
  606.         var last_key = null;
  607.         $(piano.rootElement).mousedown(function(event) {
  608.             mouse_down = true;
  609.             //event.stopPropagation();
  610.             event.preventDefault();
  611.  
  612.             var pos = CanvasRenderer.translateMouseEvent(event);
  613.             var hit = self.getHit(pos.x, pos.y);
  614.             if(hit) {
  615.                 press(hit.key.note, hit.v);
  616.                 last_key = hit.key;
  617.             }
  618.         });
  619.         piano.rootElement.addEventListener("touchstart", function(event) {
  620.             mouse_down = true;
  621.             //event.stopPropagation();
  622.             event.preventDefault();
  623.             for(var i in event.changedTouches) {
  624.                 var pos = CanvasRenderer.translateMouseEvent(event.changedTouches[i]);
  625.                 var hit = self.getHit(pos.x, pos.y);
  626.                 if(hit) {
  627.                     press(hit.key.note, hit.v);
  628.                     last_key = hit.key;
  629.                 }
  630.             }
  631.         }, false);
  632.         $(window).mouseup(function(event) {
  633.             if(last_key) {
  634.                 release(last_key.note);
  635.             }
  636.             mouse_down = false;
  637.             last_key = null;
  638.         });
  639.         /*$(piano.rootElement).mousemove(function(event) {
  640.             if(!mouse_down) return;
  641.             var pos = CanvasRenderer.translateMouseEvent(event);
  642.             var hit = self.getHit(pos.x, pos.y);
  643.             if(hit && hit.key != last_key) {
  644.                 press(hit.key.note, hit.v);
  645.                 last_key = hit.key;
  646.             }
  647.         });*/
  648.  
  649.         return this;
  650.     };
  651.  
  652.     CanvasRenderer.prototype.resize = function(width, height) {
  653.         Renderer.prototype.resize.call(this, width, height);
  654.         if(this.width < 52 * 2) this.width = 52 * 2;
  655.         if(this.height < this.width * 0.2) this.height = Math.floor(this.width * 0.2);
  656.         this.canvas.width = this.width;
  657.         this.canvas.height = this.height;
  658.         this.canvas.style.width = this.width / window.devicePixelRatio + "px";
  659.         this.canvas.style.height = this.height / window.devicePixelRatio + "px";
  660.        
  661.         // calculate key sizes
  662.         this.whiteKeyWidth = Math.floor(this.width / 52);
  663.         this.whiteKeyHeight = Math.floor(this.height * 0.9);
  664.         this.blackKeyWidth = Math.floor(this.whiteKeyWidth * 0.75);
  665.         this.blackKeyHeight = Math.floor(this.height * 0.5);
  666.  
  667.         this.blackKeyOffset = Math.floor(this.whiteKeyWidth - (this.blackKeyWidth / 2));
  668.         this.keyMovement = Math.floor(this.whiteKeyHeight * 0.015);
  669.  
  670.         this.whiteBlipWidth = Math.floor(this.whiteKeyWidth * 0.7);
  671.         this.whiteBlipHeight = Math.floor(this.whiteBlipWidth * 0.8);
  672.         this.whiteBlipX = Math.floor((this.whiteKeyWidth - this.whiteBlipWidth) / 2);
  673.         this.whiteBlipY = Math.floor(this.whiteKeyHeight - this.whiteBlipHeight * 1.2);
  674.         this.blackBlipWidth = Math.floor(this.blackKeyWidth * 0.7);
  675.         this.blackBlipHeight = Math.floor(this.blackBlipWidth * 0.8);
  676.         this.blackBlipY = Math.floor(this.blackKeyHeight - this.blackBlipHeight * 1.2);
  677.         this.blackBlipX = Math.floor((this.blackKeyWidth - this.blackBlipWidth) / 2);
  678.        
  679.         // prerender white key
  680.         this.whiteKeyRender = document.createElement("canvas");
  681.         this.whiteKeyRender.width = this.whiteKeyWidth;
  682.         this.whiteKeyRender.height = this.height + 10;
  683.         var ctx = this.whiteKeyRender.getContext("2d");
  684.         if(ctx.createLinearGradient) {
  685.             var gradient = ctx.createLinearGradient(0, 0, 0, this.whiteKeyHeight);
  686.             gradient.addColorStop(0, "#eee");
  687.             gradient.addColorStop(0.75, "#fff");
  688.             gradient.addColorStop(1, "#dad4d4");
  689.             ctx.fillStyle = gradient;
  690.         } else {
  691.             ctx.fillStyle = "#fff";
  692.         }
  693.         ctx.strokeStyle = "#000";
  694.         ctx.lineJoin = "round";
  695.         ctx.lineCap = "round";
  696.         ctx.lineWidth = 10;
  697.         ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
  698.         ctx.lineWidth = 4;
  699.         ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
  700.        
  701.         // prerender black key
  702.         this.blackKeyRender = document.createElement("canvas");
  703.         this.blackKeyRender.width = this.blackKeyWidth + 10;
  704.         this.blackKeyRender.height = this.blackKeyHeight + 10;
  705.         var ctx = this.blackKeyRender.getContext("2d");
  706.         if(ctx.createLinearGradient) {
  707.             var gradient = ctx.createLinearGradient(0, 0, 0, this.blackKeyHeight);
  708.             gradient.addColorStop(0, "#000");
  709.             gradient.addColorStop(1, "#444");
  710.             ctx.fillStyle = gradient;
  711.         } else {
  712.             ctx.fillStyle = "#000";
  713.         }
  714.         ctx.strokeStyle = "#222";
  715.         ctx.lineJoin = "round";
  716.         ctx.lineCap = "round";
  717.         ctx.lineWidth = 8;
  718.         ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
  719.         ctx.lineWidth = 4;
  720.         ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
  721.  
  722.         // prerender shadows
  723.         this.shadowRender = [];
  724.         var y = -this.canvas.height * 2;
  725.         for(var j = 0; j < 2; j++) {
  726.             var canvas = document.createElement("canvas");
  727.             this.shadowRender[j] = canvas;
  728.             canvas.width = this.canvas.width;
  729.             canvas.height = this.canvas.height;
  730.             var ctx = canvas.getContext("2d");
  731.             var sharp = j ? true : false;
  732.             ctx.lineJoin = "round";
  733.             ctx.lineCap = "round";
  734.             ctx.lineWidth = 1;
  735.             ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
  736.             ctx.shadowBlur = this.keyMovement * 3;
  737.             ctx.shadowOffsetY = -y + this.keyMovement;
  738.             if(sharp) {
  739.                 ctx.shadowOffsetX = this.keyMovement;
  740.             } else {
  741.                 ctx.shadowOffsetX = 0;
  742.                 ctx.shadowOffsetY = -y + this.keyMovement;
  743.             }
  744.             for(var i in this.piano.keys) {
  745.                 if(!this.piano.keys.hasOwnProperty(i)) continue;
  746.                 var key = this.piano.keys[i];
  747.                 if(key.sharp != sharp) continue;
  748.  
  749.                 if(key.sharp) {
  750.                     ctx.fillRect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
  751.                         y + ctx.lineWidth / 2,
  752.                         this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
  753.                 } else {
  754.                     ctx.fillRect(this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
  755.                         y + ctx.lineWidth / 2,
  756.                         this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
  757.                 }
  758.             }
  759.         }
  760.  
  761.         // update key rects
  762.         for(var i in this.piano.keys) {
  763.             if(!this.piano.keys.hasOwnProperty(i)) continue;
  764.             var key = this.piano.keys[i];
  765.             if(key.sharp) {
  766.                 key.rect = new Rect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial, 0,
  767.                     this.blackKeyWidth, this.blackKeyHeight);
  768.             } else {
  769.                 key.rect = new Rect(this.whiteKeyWidth * key.spatial, 0,
  770.                     this.whiteKeyWidth, this.whiteKeyHeight);
  771.             }
  772.         }
  773.     };
  774.  
  775.     CanvasRenderer.prototype.visualize = function(key, color) {
  776.         key.timePlayed = Date.now();
  777.         key.blips.push({"time": key.timePlayed, "color": color});
  778.     };
  779.  
  780.     CanvasRenderer.prototype.redraw = function() {
  781.         var now = Date.now();
  782.         var timeLoadedEnd = now - 1000;
  783.         var timePlayedEnd = now - 100;
  784.         var timeBlipEnd = now - 1000;
  785.  
  786.         this.ctx.save();
  787.         this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  788.         // draw all keys
  789.         for(var j = 0; j < 2; j++) {
  790.             this.ctx.globalAlpha = 1.0;
  791.             this.ctx.drawImage(this.shadowRender[j], 0, 0);
  792.             var sharp = j ? true : false;
  793.             for(var i in this.piano.keys) {
  794.                 if(!this.piano.keys.hasOwnProperty(i)) continue;
  795.                 var key = this.piano.keys[i];
  796.                 if(key.sharp != sharp) continue;
  797.  
  798.                 if(!key.loaded) {
  799.                     this.ctx.globalAlpha = 0.2;
  800.                 } else if(key.timeLoaded > timeLoadedEnd) {
  801.                     this.ctx.globalAlpha = ((now - key.timeLoaded) / 1000) * 0.8 + 0.2;
  802.                 } else {
  803.                     this.ctx.globalAlpha = 1.0;
  804.                 }
  805.                 var y = 0;
  806.                 if(key.timePlayed > timePlayedEnd) {
  807.                     y = Math.floor(this.keyMovement - (((now - key.timePlayed) / 100) * this.keyMovement));
  808.                 }
  809.                 var x = Math.floor(key.sharp ? this.blackKeyOffset + this.whiteKeyWidth * key.spatial
  810.                     : this.whiteKeyWidth * key.spatial);
  811.                 var image = key.sharp ? this.blackKeyRender : this.whiteKeyRender;
  812.                 this.ctx.drawImage(image, x, y);
  813.  
  814.                 // render blips
  815.                 if(key.blips.length) {
  816.                     var alpha = this.ctx.globalAlpha;
  817.                     var w, h;
  818.                     if(key.sharp) {
  819.                         x += this.blackBlipX;
  820.                         y = this.blackBlipY;
  821.                         w = this.blackBlipWidth;
  822.                         h = this.blackBlipHeight;
  823.                     } else {
  824.                         x += this.whiteBlipX;
  825.                         y = this.whiteBlipY;
  826.                         w = this.whiteBlipWidth;
  827.                         h = this.whiteBlipHeight;
  828.                     }
  829.                     for(var b = 0; b < key.blips.length; b++) {
  830.                         var blip = key.blips[b];
  831.                         if(blip.time > timeBlipEnd) {
  832.                             this.ctx.fillStyle = blip.color;
  833.                             this.ctx.globalAlpha = alpha - ((now - blip.time) / 1000);
  834.                             this.ctx.fillRect(x, y, w, h);
  835.                         } else {
  836.                             key.blips.splice(b, 1);
  837.                             --b;
  838.                         }
  839.                         y -= Math.floor(h * 1.1);
  840.                     }
  841.                 }
  842.             }
  843.         }
  844.         this.ctx.restore();
  845.     };
  846.  
  847.     CanvasRenderer.prototype.getHit = function(x, y) {
  848.         for(var j = 0; j < 2; j++) {
  849.             var sharp = j ? false : true; // black keys first
  850.             for(var i in this.piano.keys) {
  851.                 if(!this.piano.keys.hasOwnProperty(i)) continue;
  852.                 var key = this.piano.keys[i];
  853.                 if(key.sharp != sharp) continue;
  854.                 if(key.rect.contains(x, y)) {
  855.                     var v = y / (key.sharp ? this.blackKeyHeight : this.whiteKeyHeight);
  856.                     v += 0.25;
  857.                     v *= DEFAULT_VELOCITY;
  858.                     if(v > 1.0) v = 1.0;
  859.                     return {"key": key, "v": v};
  860.                 }
  861.             }
  862.         }
  863.         return null;
  864.     };
  865.  
  866.  
  867.     CanvasRenderer.isSupported = function() {
  868.         var canvas = document.createElement("canvas");
  869.         return !!(canvas.getContext && canvas.getContext("2d"));
  870.     };
  871.  
  872.     CanvasRenderer.translateMouseEvent = function(evt) {
  873.         var element = evt.target;
  874.         var offx = 0;
  875.         var offy = 0;
  876.         do {
  877.             if(!element) break; // wtf, wtf?
  878.             offx += element.offsetLeft;
  879.             offy += element.offsetTop;
  880.         } while(element = element.offsetParent);
  881.         return {
  882.             x: (evt.pageX - offx) * window.devicePixelRatio,
  883.             y: (evt.pageY - offy) * window.devicePixelRatio
  884.         }
  885.     };
  886.  
  887.  
  888.  
  889.  
  890.  
  891.  
  892.  
  893.  
  894.  
  895.  
  896.  
  897. // Soundpack Stuff by electrashave ♥
  898.  
  899. ////////////////////////////////////////////////////////////////
  900.  
  901.     function SoundSelector(piano) {
  902.         this.initiated = false;
  903.         this.keys = piano.keys;
  904.         this.loading = {};
  905.         this.notification;
  906.         this.packs = [];
  907.         this.piano = piano;
  908.         this.soundSelection = localStorage.soundSelection || "MPP Classic";
  909.         this.addPack({name: "MPP Classic", keys: Object.keys(this.piano.keys), ext: ".mp3", url: "/sounds/mppclassic/"});
  910.     }
  911.  
  912.     SoundSelector.prototype.addPack = function(pack, load) {
  913.         var self = this;
  914.         self.loading[pack.url || pack] = true;
  915.         function add(obj) {
  916.             if (self.packs.indexOf(obj) != -1) return; //no adding packs twice D:<
  917.             var html =document.createElement("li");
  918.             html.classList = "pack";
  919.             html.innerText = obj.name + " (" + obj.keys.length + " keys)";
  920.             html.onclick = function() {
  921.                 self.loadPack(obj.name);
  922.                 self.notification.close();
  923.             };
  924.             obj.html = html;
  925.             self.packs.push(obj);
  926.             self.packs.sort(function(a, b) {
  927.                 if(a.name < b.name) return -1;
  928.                 if(a.name > b.name) return 1;
  929.                 return 0;
  930.             });
  931.             if (load) self.loadPack(obj.name);
  932.             delete self.loading[obj.url];
  933.         }
  934.  
  935.         if (typeof pack == "string") {
  936.             $.getJSON(pack + "/info.json").done(function(json) {
  937.                 json.url = pack;
  938.                 add(json);
  939.             });
  940.         } else add(pack); //validate packs??
  941.     };
  942.  
  943.     SoundSelector.prototype.addPacks = function(packs) {
  944.         for (var i = 0; packs.length > i; i++) this.addPack(packs[i]);
  945.     };
  946.  
  947.     SoundSelector.prototype.init = function() {
  948.         var self = this;
  949.         if (self.initialized) return console.warn("Sound selector already initialized!");
  950.  
  951.         if (!!Object.keys(self.loading).length) return setTimeout(function() {
  952.             self.init();
  953.         }, 250);
  954.  
  955.         $("#sound-btn").on("click", function() {
  956.             if (document.getElementById("Notification-Sound-Selector") != null) return;
  957.             var html = document.createElement("ul");
  958.             $(html).append("<h1>Current Sound: " + self.soundSelection + "</h1>");
  959.  
  960.             for (var i = 0; self.packs.length > i; i++) {
  961.                 var pack = self.packs[i];
  962.                 if (pack.name == self.soundSelection) pack.html.classList = "pack enabled";
  963.                 else pack.html.classList = "pack";
  964.                 html.appendChild(pack.html);
  965.             }
  966.             self.notification = new Notification({title: "Sound Selector:", html: html, id: "Sound-Selector", duration: -1, target: "#sound-btn"});
  967.         });
  968.         self.initialized = true;
  969.         self.loadPack(self.soundSelection, true);
  970.     };
  971.  
  972.     SoundSelector.prototype.loadPack = function(pack, f) {
  973.         for (var i = 0; this.packs.length > i; i++) {
  974.             var p = this.packs[i];
  975.             if (p.name == pack) {
  976.                 pack = p;
  977.                 break;
  978.             }
  979.         }
  980.         if (typeof pack == "String") {
  981.             console.warn("Sound pack does not exist! Loading default pack...");
  982.             pack = this.packs[0];
  983.         }
  984.  
  985.         if (pack.name == this.soundSelection && !f) return;
  986.         if (pack.keys.length != Object.keys(this.piano.keys).length) {
  987.             this.piano.keys = {};
  988.             for (var i = 0; pack.keys.length > i; i++) this.piano.keys[pack.keys[i]] = this.keys[pack.keys[i]];
  989.             this.piano.renderer.resize();
  990.         }
  991.  
  992.         var self = this;
  993.         for (var i in this.piano.keys) {
  994.             if (!this.piano.keys.hasOwnProperty(i)) continue;
  995.             (function() {
  996.                 var key = self.piano.keys[i];
  997.                 key.loaded = false;
  998.                 self.piano.audio.load(key.note, pack.url + key.note + pack.ext, function() {
  999.                     key.loaded = true;
  1000.                     key.timeLoaded = Date.now();
  1001.                 });
  1002.             })();
  1003.         }
  1004.         localStorage.soundSelection = pack.name;
  1005.         this.soundSelection = pack.name;
  1006.     };
  1007.  
  1008.     SoundSelector.prototype.removePack = function(name) {
  1009.         var found = false;
  1010.         for (var i = 0; this.packs.length > i; i++) {
  1011.             var pack = this.packs[i];
  1012.             if (pack.name == name) {
  1013.                 this.packs.splice(i, 1);
  1014.                 if (pack.name == this.soundSelection) this.loadPack(this.packs[0].name); //add mpp default if none?
  1015.                 break;
  1016.             }
  1017.         }
  1018.         if (!found) console.warn("Sound pack not found!");
  1019.     };
  1020.  
  1021.  
  1022.  
  1023.  
  1024.  
  1025.  
  1026.  
  1027.  
  1028.  
  1029.  
  1030.  
  1031. // Pianoctor
  1032.  
  1033. ////////////////////////////////////////////////////////////////
  1034.  
  1035.     var PianoKey = function(note, octave) {
  1036.         this.note = note + octave;
  1037.         this.baseNote = note;
  1038.         this.octave = octave;
  1039.         this.sharp = note.indexOf("s") != -1;
  1040.         this.loaded = false;
  1041.         this.timeLoaded = 0;
  1042.         this.domElement = null;
  1043.         this.timePlayed = 0;
  1044.         this.blips = [];
  1045.     };
  1046.  
  1047.     var Piano = function(rootElement) {
  1048.    
  1049.         var piano = this;
  1050.         piano.rootElement = rootElement;
  1051.         piano.keys = {};
  1052.        
  1053.         var white_spatial = 0;
  1054.         var black_spatial = 0;
  1055.         var black_it = 0;
  1056.         var black_lut = [2, 1, 2, 1, 1];
  1057.         var addKey = function(note, octave) {
  1058.             var key = new PianoKey(note, octave);
  1059.             piano.keys[key.note] = key;
  1060.             if(key.sharp) {
  1061.                 key.spatial = black_spatial;
  1062.                 black_spatial += black_lut[black_it % 5];
  1063.                 ++black_it;
  1064.             } else {
  1065.                 key.spatial = white_spatial;
  1066.                 ++white_spatial;
  1067.             }
  1068.         }
  1069.         if(test_mode) {
  1070.             addKey("c", 2);
  1071.         } else {
  1072.             addKey("a", -1);
  1073.             addKey("as", -1);
  1074.             addKey("b", -1);
  1075.             var notes = "c cs d ds e f fs g gs a as b".split(" ");
  1076.             for(var oct = 0; oct < 7; oct++) {
  1077.                 for(var i in notes) {
  1078.                     addKey(notes[i], oct);
  1079.                 }
  1080.             }
  1081.             addKey("c", 7);
  1082.         }
  1083.  
  1084.  
  1085.         this.renderer = new CanvasRenderer().init(this);
  1086.        
  1087.         window.addEventListener("resize", function() {
  1088.             piano.renderer.resize();
  1089.         });
  1090.  
  1091.  
  1092.         window.AudioContext = window.AudioContext || window.webkitAudioContext || undefined;
  1093.         var audio_engine = AudioEngineWeb;
  1094.         this.audio = new audio_engine().init();
  1095.     };
  1096.  
  1097.     Piano.prototype.play = function(note, vol, participant, delay_ms,audio = true) {
  1098.         if(!this.keys.hasOwnProperty(note)) return;
  1099.         var key = this.keys[note];
  1100.         if(audio){
  1101.             if(key.loaded) this.audio.play(key.note, vol, delay_ms, participant.id);
  1102.         };
  1103.         if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, vol * 100, delay_ms);
  1104.         var self = this;
  1105.         var jq_namediv = $(typeof participant == "undefined" ? null : participant.nameDiv);
  1106.         if(jq_namediv) {
  1107.             setTimeout(function() {
  1108.                 self.renderer.visualize(key, typeof participant == "undefined" ? "yellow" : (participant.color || "#777"));
  1109.                 jq_namediv.addClass("play");
  1110.                 setTimeout(function() {
  1111.                     jq_namediv.removeClass("play");
  1112.                 }, 30);
  1113.             }, delay_ms);
  1114.         }
  1115.     };
  1116.  
  1117.     Piano.prototype.stop = function(note, participant, delay_ms) {
  1118.         if(!this.keys.hasOwnProperty(note)) return;
  1119.         var key = this.keys[note];
  1120.         if(key.loaded) this.audio.stop(key.note, delay_ms, participant.id);
  1121.         if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, 0, delay_ms);
  1122.     };
  1123.    
  1124.     var gPiano = new Piano(document.getElementById("piano"));
  1125.    
  1126.     var gSoundSelector = new SoundSelector(gPiano);
  1127.     gSoundSelector.addPacks(["/sounds/Emotional_2.0/", "/sounds/Harp/", "/sounds/Music_Box/", "/sounds/Vintage_Upright/", "/sounds/Steinway_Grand/", "/sounds/Emotional/", "/sounds/Untitled/"]);
  1128.     gSoundSelector.init();
  1129.  
  1130.  
  1131.  
  1132.  
  1133.  
  1134.  
  1135.  
  1136.     gAutoSustain = true; //!(window.location.hash && window.location.hash.match(/^(?:#.+)*#sustain(?:#.+)*$/));
  1137.     var gSustain = false;
  1138.     volumeFactor = 0.5;
  1139.     gHeldNotes = {};gNHeld = 0;
  1140.     var gSustainedNotes = {};
  1141.  
  1142.     function press(id, vol,ns) {
  1143.     for(var y = 0;y<v;y++){//var vol = parseFloat(vol * Math.pow(volumeFactor,y)) || vol;
  1144.     setTimeout(function(){
  1145.         var ixd = id;
  1146.         if(!ixd){return false};
  1147.         if(!gClient.preventsPlaying()) {
  1148.             if(bluesMode)ixd = toBlues(ixd);
  1149.             var delay = 0;
  1150.             //console.log(y);
  1151.             for(var n = -octaveMode;n<=octaveMode;n++){
  1152.                 var note = keys[keys.indexOf(ixd)+12*n];
  1153.                 if(note){
  1154.                     if(!ns)gHeldNotes[note] = true;
  1155.                     gSustainedNotes[note] = true;
  1156.                     gPiano.play(note, vol !== undefined ? vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), delay, localSound);
  1157.                     gClient.startNote(note, vol);
  1158.                     delay += octaveDelay;
  1159.                     vq--;
  1160.                 };
  1161.             };
  1162.             if(gClient.noteBuffer.length > 85){
  1163.                 MPP.client.sendArray([{m: "n", t: gClient.noteBufferTime + gClient.serverTimeOffset, n: gClient.noteBuffer}]);
  1164.                 gClient.noteBuffer = [];
  1165.                 gClient.noteBufferTime = 0;
  1166.                 console.log("packet oversize");
  1167.             };
  1168.         }else if(playing){
  1169.             clearTimeout(timeout);playing=0;
  1170.             MPP.chat.send("Play prevention detected. Automatically stopping the track.");
  1171.         }
  1172.     },y*d)};
  1173.     }
  1174.  
  1175.     function release(id,rep) {
  1176.         var id = id;
  1177.         if(bluesMode)id = toBlues(id);
  1178.         if(gHeldNotes[id]) {
  1179.             gHeldNotes[id] = false;
  1180.             if((gAutoSustain || gSustain) && !enableSynth) {
  1181.                 gSustainedNotes[id] = true;
  1182.             } else {
  1183.                 if(gNoteQuota.spend(1)) {
  1184.                     gPiano.stop(id, gClient.getOwnParticipant(), 0);
  1185.                     gClient.stopNote(id);
  1186.                     gSustainedNotes[id] = false;
  1187.                 }
  1188.             };
  1189.         }
  1190.     if(!rep && octaveMode == 1){release(keys[keys.indexOf(id)-12],true);release(keys[keys.indexOf(id)+12],true)}
  1191.     }
  1192.  
  1193.     function pressSustain() {
  1194.         gSustain = true;
  1195.     }
  1196.  
  1197.     function releaseSustain() {
  1198.         gSustain = false;
  1199.         if(!gAutoSustain) {
  1200.             for(var id in gSustainedNotes) {
  1201.                 if(gSustainedNotes.hasOwnProperty(id) && gSustainedNotes[id] && !gHeldNotes[id]) {
  1202.                     gSustainedNotes[id] = false;
  1203.                     if(gNoteQuota.spend(1)) {
  1204.                         gPiano.stop(id, gClient.getOwnParticipant(), 0);
  1205.                         gClient.stopNote(id);
  1206.                     }
  1207.                 }
  1208.             }
  1209.         }
  1210.     }
  1211.  
  1212.  
  1213.  
  1214.  
  1215.  
  1216.  
  1217.  
  1218.  
  1219.  
  1220. // internet science
  1221.  
  1222. ////////////////////////////////////////////////////////////////
  1223.  
  1224.     var channel_id = decodeURIComponent(window.location.pathname);
  1225.     if(channel_id.substr(0, 1) == "/") channel_id = channel_id.substr(1);
  1226.     if(channel_id == "") channel_id = "lobby";
  1227.  
  1228.     var wssport = window.location.hostname == "www.multiplayerpiano.com" ? 443 : 8080;
  1229.     var gClient = new Client("ws://" + window.location.hostname + ":" + wssport);
  1230.     gClient.setChannel(channel_id);
  1231.     gClient.start();
  1232.  
  1233.  
  1234.     // Setting status
  1235.     (function() {
  1236.         gClient.on("status", function(status) {
  1237.             $("#status").text(status);
  1238.         });
  1239.         gClient.on("count", function(count) {
  1240.             if(count > 0) {
  1241.                 $("#status").html('<span class="number">'+count+'</span> '+(count==1? 'person is' : 'people are')+' playing');
  1242.                 document.title = "Piano (" + count + ")";
  1243.             } else {
  1244.                 document.title = "Multiplayer Piano";
  1245.             }
  1246.         });
  1247.     })();
  1248.  
  1249.     // Handle changes to participants
  1250.     (function() {
  1251.         gClient.on("participant added", function(part) {
  1252.  
  1253.             part.displayX = 150;
  1254.             part.displayY = 50;
  1255.  
  1256.             // add nameDiv
  1257.             var div = document.createElement("div");
  1258.             div.className = "name";
  1259.             div.participantId = part.id;
  1260.             div.textContent = part.name || "";
  1261.             div.style.backgroundColor = part.color || "#777";
  1262.             if(gClient.participantId === part.id) {
  1263.                 $(div).addClass("me");
  1264.             }
  1265.             if(gClient.channel && gClient.channel.crown && gClient.channel.crown.participantId === part.id) {
  1266.                 $(div).addClass("owner");
  1267.             }
  1268.             if(gPianoMutes.indexOf(part._id) !== -1) {
  1269.                 $(part.nameDiv).addClass("muted-notes");
  1270.             }
  1271.             if(gChatMutes.indexOf(part._id) !== -1) {
  1272.                 $(part.nameDiv).addClass("muted-chat");
  1273.             }
  1274.             div.style.display = "none";
  1275.             part.nameDiv = $("#names")[0].appendChild(div);
  1276.             $(part.nameDiv).fadeIn(2000);
  1277.  
  1278.             // sort names
  1279.             var arr = $("#names .name");
  1280.             arr.sort(function(a, b) {
  1281.                 a = a.style.backgroundColor; // todo: sort based on user id instead
  1282.                 b = b.style.backgroundColor;
  1283.                 if (a > b) return 1;
  1284.                 else if (a < b) return -1;
  1285.                 else return 0;
  1286.             });
  1287.             $("#names").html(arr);
  1288.  
  1289.             // add cursorDiv
  1290.             if(gClient.participantId !== part.id || gSeeOwnCursor) {
  1291.                 var div = document.createElement("div");
  1292.                 div.className = "cursor";
  1293.                 div.style.display = "none";
  1294.                 part.cursorDiv = $("#cursors")[0].appendChild(div);
  1295.                 $(part.cursorDiv).fadeIn(2000);
  1296.  
  1297.                 var div = document.createElement("div");
  1298.                 div.className = "name";
  1299.                 div.style.backgroundColor = part.color || "#777"
  1300.                 div.textContent = part.name || "";
  1301.                 part.cursorDiv.appendChild(div);
  1302.  
  1303.             } else {
  1304.                 part.cursorDiv = undefined;
  1305.             }
  1306.         });
  1307.         gClient.on("participant removed", function(part) {
  1308.             // remove nameDiv
  1309.             var nd = $(part.nameDiv);
  1310.             var cd = $(part.cursorDiv);
  1311.             cd.fadeOut(2000);
  1312.             nd.fadeOut(2000, function() {
  1313.                 nd.remove();
  1314.                 cd.remove();
  1315.                 part.nameDiv = undefined;
  1316.                 part.cursorDiv = undefined;
  1317.             });
  1318.         });
  1319.         gClient.on("participant update", function(part) {
  1320.             var name = part.name || "";
  1321.             var color = part.color || "#777";
  1322.             part.nameDiv.style.backgroundColor = color;
  1323.             part.nameDiv.textContent = name;
  1324.             $(part.cursorDiv)
  1325.             .find(".name")
  1326.             .text(name)
  1327.             .css("background-color", color);
  1328.         });
  1329.         gClient.on("ch", function(msg) {
  1330.             for(var id in gClient.ppl) {
  1331.                 if(gClient.ppl.hasOwnProperty(id)) {
  1332.                     var part = gClient.ppl[id];
  1333.                     if(part.id === gClient.participantId) {
  1334.                         $(part.nameDiv).addClass("me");
  1335.                     } else {
  1336.                         $(part.nameDiv).removeClass("me");
  1337.                     }
  1338.                     if(msg.ch.crown && msg.ch.crown.participantId === part.id) {
  1339.                         $(part.nameDiv).addClass("owner");
  1340.                         $(part.cursorDiv).addClass("owner");
  1341.                     } else {
  1342.                         $(part.nameDiv).removeClass("owner");
  1343.                         $(part.cursorDiv).removeClass("owner");
  1344.                     }
  1345.                     if(gPianoMutes.indexOf(part._id) !== -1) {
  1346.                         $(part.nameDiv).addClass("muted-notes");
  1347.                     } else {
  1348.                         $(part.nameDiv).removeClass("muted-notes");
  1349.                     }
  1350.                     if(gChatMutes.indexOf(part._id) !== -1) {
  1351.                         $(part.nameDiv).addClass("muted-chat");
  1352.                     } else {
  1353.                         $(part.nameDiv).removeClass("muted-chat");
  1354.                     }
  1355.                 }
  1356.             }
  1357.         });
  1358.         function updateCursor(msg) {
  1359.             const part = gClient.ppl[msg.id];
  1360.             if (part && part.cursorDiv) {
  1361.                 part.cursorDiv.style.left = msg.x + "%";
  1362.                 part.cursorDiv.style.top = msg.y + "%";
  1363.             }
  1364.         }
  1365.         gClient.on("m", updateCursor);
  1366.         gClient.on("participant added", updateCursor);
  1367.     })();
  1368.  
  1369.  
  1370.     // Handle changes to crown
  1371.     (function() {
  1372.         var jqcrown = $('<div id="crown"></div>').appendTo(document.body).hide();
  1373.         var jqcountdown = $('<span></span>').appendTo(jqcrown);
  1374.         var countdown_interval;
  1375.         jqcrown.click(function() {
  1376.             gClient.sendArray([{m: "chown", id: gClient.participantId}]);
  1377.         });
  1378.         gClient.on("ch", function(msg) {
  1379.             if(msg.ch.crown) {
  1380.                 var crown = msg.ch.crown;
  1381.                 if(!crown.participantId || !gClient.ppl[crown.participantId]) {
  1382.                     var land_time = crown.time + 2000 - gClient.serverTimeOffset;
  1383.                     var avail_time = crown.time + 15000 - gClient.serverTimeOffset;
  1384.                     jqcountdown.text("");
  1385.                     jqcrown.show();
  1386.                     if(land_time - Date.now() <= 0) {
  1387.                         jqcrown.css({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"});
  1388.                     } else {
  1389.                         jqcrown.css({"left": crown.startPos.x + "%", "top": crown.startPos.y + "%"});
  1390.                         jqcrown.addClass("spin");
  1391.                         jqcrown.animate({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"}, 2000, "linear", function() {
  1392.                             jqcrown.removeClass("spin");
  1393.                         });
  1394.                     }
  1395.                     clearInterval(countdown_interval);
  1396.                     countdown_interval = setInterval(function() {
  1397.                         var time = Date.now();
  1398.                         if(time >= land_time) {
  1399.                             var ms = avail_time - time;
  1400.                             if(ms > 0) {
  1401.                                 jqcountdown.text(Math.ceil(ms / 1000) + "s");
  1402.                             } else {
  1403.                                 jqcountdown.text("");
  1404.                                 clearInterval(countdown_interval);
  1405.                             }
  1406.                         }
  1407.                     }, 1000);
  1408.                 } else {
  1409.                     jqcrown.hide();
  1410.                 }
  1411.             } else {
  1412.                 jqcrown.hide();
  1413.             }
  1414.         });
  1415.         gClient.on("disconnect", function() {
  1416.             jqcrown.fadeOut(2000);
  1417.         });
  1418.     })();
  1419.  
  1420.    
  1421.     // Playing notes
  1422.     gClient.on("n", function(msg) {
  1423.         var t = msg.t - gClient.serverTimeOffset + TIMING_TARGET - Date.now();
  1424.         var participant = gClient.findParticipantById(msg.p);
  1425.         if(gPianoMutes.indexOf(participant._id) !== -1)
  1426.             return;
  1427.         for(var i = 0; i < msg.n.length; i++) {
  1428.             var note = msg.n[i];
  1429.             var ms = t + (note.d || 0);
  1430.             if(ms < 0) {
  1431.                 ms = 0;
  1432.             }
  1433.             else if(ms > 10000) continue;
  1434.             if(note.s) {
  1435.                 gPiano.stop(note.n, participant, ms);
  1436.             } else {
  1437.                 var vel = (typeof note.v !== "undefined")? parseFloat(note.v) : DEFAULT_VELOCITY;
  1438.                 if(vel < 0) vel = 0; else if (vel > 1) vel = 1;
  1439.                 gPiano.play(note.n, vel, participant, ms);
  1440.                 if(enableSynth) {
  1441.                     gPiano.stop(note.n, participant, ms + 1000);
  1442.                 }
  1443.             }
  1444.         }
  1445.     });
  1446.  
  1447.     // Send cursor updates
  1448.     mx = 0;
  1449.     last_mx = -10;
  1450.     my = 0;
  1451.     last_my = -10;
  1452.     setInterval(function() {
  1453.         if(Math.abs(mx - last_mx) > 0.1 || Math.abs(my - last_my) > 0.1) {
  1454.             last_mx = mx;
  1455.             last_my = my;
  1456.             gClient.sendArray([{m: "m", x: mx, y: my}]);
  1457.             if(gSeeOwnCursor) {
  1458.                 gClient.emit("m", { m: "m", id: gClient.participantId, x: mx, y: my });
  1459.             }
  1460.             var part = gClient.getOwnParticipant();
  1461.             if(part) {
  1462.                 part.x = mx;
  1463.                 part.y = my;
  1464.             }
  1465.         }
  1466.     }, 50);
  1467.     $(document).mousemove(function(event) {
  1468.         mx = ((event.pageX / $(window).width()) * 100).toFixed(2);
  1469.         my = ((event.pageY / $(window).height()) * 100).toFixed(2);
  1470.     });
  1471.  
  1472.  
  1473.     // Room settings button
  1474.     (function() {
  1475.         gClient.on("ch", function(msg) {
  1476.             if(gClient.isOwner()) {
  1477.                 $("#room-settings-btn").show();
  1478.             } else {
  1479.                 $("#room-settings-btn").hide();
  1480.             }
  1481.         });
  1482.         $("#room-settings-btn").click(function(evt) {
  1483.             if(gClient.channel && gClient.isOwner()) {
  1484.                 var settings = gClient.channel.settings;
  1485.                 openModal("#room-settings");
  1486.                 setTimeout(function() {
  1487.                     $("#room-settings .checkbox[name=visible]").prop("checked", settings.visible);
  1488.                     $("#room-settings .checkbox[name=chat]").prop("checked", settings.chat);
  1489.                     $("#room-settings .checkbox[name=crownsolo]").prop("checked", settings.crownsolo);
  1490.                     $("#room-settings input[name=color]").val(settings.color);
  1491.                 }, 100);
  1492.             }
  1493.         });
  1494.         $("#room-settings .submit").click(function() {
  1495.             var settings = {
  1496.                 visible: $("#room-settings .checkbox[name=visible]").is(":checked"),
  1497.                 chat: $("#room-settings .checkbox[name=chat]").is(":checked"),
  1498.                 crownsolo: $("#room-settings .checkbox[name=crownsolo]").is(":checked"),
  1499.                 color: $("#room-settings input[name=color]").val()
  1500.             };
  1501.             gClient.sendArray([{m: "chset", set: settings}]);
  1502.             closeModal();
  1503.         });
  1504.         $("#room-settings .drop-crown").click(function() {
  1505.             closeModal();
  1506.             if(confirm("This will drop the crown...!"))
  1507.                 gClient.sendArray([{m: "chown"}]);
  1508.         });
  1509.     })();
  1510.  
  1511.     // Handle notifications
  1512.     gClient.on("notification", function(msg) {
  1513.         new Notification(msg);
  1514.     });
  1515.  
  1516.     // Don't foget spin
  1517.     gClient.on("ch", function(msg) {
  1518.         var chidlo = msg.ch._id.toLowerCase();
  1519.         if(chidlo === "spin" || chidlo.substr(-5) === "/spin") {
  1520.             $("#piano").addClass("spin");
  1521.         } else {
  1522.             $("#piano").removeClass("spin");
  1523.         }
  1524.     });
  1525.  
  1526.     /*function eb() {
  1527.         if(gClient.channel && gClient.channel._id.toLowerCase() === "test/fishing") {
  1528.             ebsprite.start(gClient);
  1529.         } else {
  1530.             ebsprite.stop();
  1531.         }
  1532.     }
  1533.     if(ebsprite) {
  1534.         gClient.on("ch", eb);
  1535.         eb();
  1536.     }*/
  1537.  
  1538.     // Crownsolo notice
  1539.     gClient.on("ch", function(msg) {
  1540.         if(msg.ch.settings.crownsolo) {
  1541.             if($("#crownsolo-notice").length == 0) {
  1542.                 $('<div id="crownsolo-notice">').text('This room is set to "only the owner can play."').appendTo("body").fadeIn(1000);
  1543.             }
  1544.         } else {
  1545.             $("#crownsolo-notice").remove();
  1546.         }
  1547.     });
  1548.     gClient.on("disconnect", function() {
  1549.         $("#crownsolo-notice").remove();
  1550.     });
  1551.  
  1552.  
  1553.     // Background color
  1554.     (function() {
  1555.         var old_color1 = new Color("#3b5054");
  1556.         var old_color2 = new Color("#3b5054");
  1557.         function setColor(hex) {
  1558.             var color1 = new Color(hex);
  1559.             var color2 = new Color(hex);
  1560.             color2.add(-0x40, -0x40, -0x40);
  1561.  
  1562.             var bottom = document.getElementById("bottom");
  1563.            
  1564.             var duration = 500;
  1565.             var step = 0;
  1566.             var steps = 30;
  1567.             var step_ms = duration / steps;
  1568.             var difference = new Color(color1.r, color1.g, color1.b);
  1569.             difference.r -= old_color1.r;
  1570.             difference.g -= old_color1.g;
  1571.             difference.b -= old_color1.b;
  1572.             var inc = new Color(difference.r / steps, difference.g / steps, difference.b / steps);
  1573.             var iv;
  1574.             iv = setInterval(function() {
  1575.                 old_color1.add(inc.r, inc.g, inc.b);
  1576.                 old_color2.add(inc.r, inc.g, inc.b);
  1577.                 document.body.style.background = "radial-gradient(ellipse at center, "+old_color1.toHexa()+" 0%,"+old_color2.toHexa()+" 100%)";
  1578.                 bottom.style.background = old_color2.toHexa();
  1579.                 if(++step >= steps) {
  1580.                     clearInterval(iv);
  1581.                     old_color1 = color1;
  1582.                     old_color2 = color2;
  1583.                     document.body.style.background = "radial-gradient(ellipse at center, "+color1.toHexa()+" 0%,"+color2.toHexa()+" 100%)";
  1584.                     bottom.style.background = color2.toHexa();
  1585.                 }
  1586.             }, step_ms);
  1587.         }
  1588.  
  1589.         setColor("#3b5054");
  1590.  
  1591.         gClient.on("ch", function(ch) {
  1592.             if(ch.ch.settings) {
  1593.                 if(ch.ch.settings.color) {
  1594.                     setColor(ch.ch.settings.color);
  1595.                 } else {
  1596.                     setColor("#3b5054");
  1597.                 }
  1598.             }
  1599.         });
  1600.     })();
  1601.  
  1602.  
  1603.  
  1604.  
  1605.  
  1606.  
  1607.     var gPianoMutes = [];
  1608.  
  1609.     var gChatMutes = [];
  1610.    
  1611.  
  1612.  
  1613.  
  1614.  
  1615.  
  1616.  
  1617.  
  1618.  
  1619.  
  1620.    
  1621.  
  1622.    
  1623.    
  1624.  
  1625.  
  1626.  
  1627.     var volume_slider = document.getElementById("volume-slider");
  1628.     volume_slider.value = gPiano.audio.volume;
  1629.     $("#volume-label").text("Volume: " + Math.floor(gPiano.audio.volume * 100) + "%");
  1630.     volume_slider.addEventListener("input", function(evt) {
  1631.         var v = +volume_slider.value;
  1632.         gPiano.audio.setVolume(v);
  1633.         if (window.localStorage) localStorage.volume = v;
  1634.         $("#volume-label").text("Volume: " + Math.floor(v * 100) + "%");
  1635.     });
  1636.  
  1637.  
  1638.  
  1639.  
  1640.     var Note = function(note, octave) {
  1641.         this.note = note;
  1642.         this.octave = octave || 0;
  1643.     };
  1644.  
  1645.  
  1646. var n = function(a, b) { return {note: new Note(a, b), held: false}; };
  1647.     key_binding = {
  1648.         65: n("gs"),
  1649.         90: n("a"),
  1650.         83: n("as"),
  1651.         88: n("b"),
  1652.         67: n("c", 1),
  1653.         70: n("cs", 1),
  1654.         86: n("d", 1),
  1655.         71: n("ds", 1),
  1656.         66: n("e", 1),
  1657.         78: n("f", 1),
  1658.         74: n("fs", 1),
  1659.         77: n("g", 1),
  1660.         75: n("gs", 1),
  1661.         188: n("a", 1),
  1662.         76: n("as", 1),
  1663.         190: n("b", 1),
  1664.         191: n("c", 2),
  1665.         222: n("cs", 2),
  1666.  
  1667.         49: n("gs", 1),
  1668.         81: n("a", 1),
  1669.         50: n("as", 1),
  1670.         87: n("b", 1),
  1671.         69: n("c", 2),
  1672.         52: n("cs", 2),
  1673.         82: n("d", 2),
  1674.         53: n("ds", 2),
  1675.         84: n("e", 2),
  1676.         89: n("f", 2),
  1677.         55: n("fs", 2),
  1678.         85: n("g", 2),
  1679.         56: n("gs", 2),
  1680.         73: n("a", 2),
  1681.         57: n("as", 2),
  1682.         79: n("b", 2),
  1683.         80: n("c", 3),
  1684.         189: n("cs", 3),
  1685.         219: n("d", 3),
  1686.         187: n("ds", 3),
  1687.         221: n("e", 3),
  1688.         220: n("f",3),
  1689.         144: n("fs",3),
  1690.         103: n("g",3),
  1691.         111: n("gs",3),
  1692.         104: n("a",3),
  1693.         106: n("as",3),
  1694.         105: n("b",3),
  1695.         107: n("c",4),
  1696.         173: n("cs", 3), // firefox why
  1697.         61: n("ds", 3), // firefox why
  1698.     };
  1699.  
  1700.     capsLockKey = false;
  1701.  
  1702.     transpose_octave = 0;transpose_up = 0;transpose_down = 0;keys_down = "65 90 83 88 67 70 86 71 66 78 74 77 75 188 76 190 191 222".split(" ");octaveMode = 0;
  1703.    
  1704.     function handleKeyDown(evt) {
  1705.         //console.log(evt);
  1706.         var code = parseInt(evt.keyCode);
  1707.         if(key_binding[code] !== undefined) {
  1708.             var binding = key_binding[code];
  1709.             if(!binding.held) {
  1710.                 binding.held = true;
  1711.  
  1712.                 var note = binding.note;
  1713.                 var octave = 1 + note.octave + transpose_octave;
  1714.                 if(keys_down.indexOf(code.toString())!==-1)
  1715.                 {octave+=transpose_down}else
  1716.                 {octave+=transpose_up};
  1717.                 if(evt.shiftKey) ++octave;
  1718.                 else if(capsLockKey || evt.ctrlKey) --octave;
  1719.                 note = note.note + octave;
  1720.                 var vol = velocityFromMouseY();
  1721.                 press(note, vol);
  1722.             }
  1723.  
  1724.             if(++gKeyboardSeq == 3) {
  1725.                 gKnowsYouCanUseKeyboard = true;
  1726.                 if(window.gKnowsYouCanUseKeyboardTimeout) clearTimeout(gKnowsYouCanUseKeyboardTimeout);
  1727.                 if(localStorage) localStorage.knowsYouCanUseKeyboard = true;
  1728.                 if(window.gKnowsYouCanUseKeyboardNotification) gKnowsYouCanUseKeyboardNotification.close();
  1729.             }
  1730.  
  1731.             evt.preventDefault();
  1732.             evt.stopPropagation();
  1733.             return false;
  1734.         } else if(code == 20) { // Caps Lock
  1735.             capsLockKey = true;
  1736.             evt.preventDefault();
  1737.         } else if(code === 0x20) { // Space Bar
  1738.             pressSustain();
  1739.             evt.preventDefault();
  1740.         } else if((code === 38) && transpose_up < 3) {
  1741.             ++transpose_up;
  1742.         } else if((code === 40) && transpose_up > -2) {
  1743.             --transpose_up;
  1744.         } else if((code === 39) && transpose_down < 3) {
  1745.             ++transpose_down;
  1746.         } else if((code === 37) && transpose_down > -2) {
  1747.             --transpose_down;
  1748.         } else if(code == 9) { // Tab (don't tab away from the piano)
  1749.             evt.preventDefault();
  1750.         } else if(code == 8) { // Backspace (don't navigate Back)
  1751.             gAutoSustain = !gAutoSustain;
  1752.             evt.preventDefault();
  1753.         }
  1754.     };
  1755.  
  1756.     function handleKeyUp(evt) {
  1757.         var code = parseInt(evt.keyCode);
  1758.         if(key_binding[code] !== undefined) {
  1759.             var binding = key_binding[code];
  1760.             if(binding.held) {
  1761.                 binding.held = false;
  1762.                
  1763.                 var note = binding.note;
  1764.                 var octave = 1 + note.octave + transpose_octave;
  1765.                 if(evt.shiftKey) ++octave;
  1766.                 else if(capsLockKey || evt.ctrlKey) --octave;
  1767.                 note = note.note + octave;
  1768.                 release(note);
  1769.             }
  1770.  
  1771.             evt.preventDefault();
  1772.             evt.stopPropagation();
  1773.             return false;
  1774.         } else if(code == 20) { // Caps Lock
  1775.             capsLockKey = false;
  1776.             evt.preventDefault();
  1777.         } else if(code === 0x20) { // Space Bar
  1778.             releaseSustain();
  1779.             evt.preventDefault();
  1780.         }
  1781.     };
  1782.  
  1783.     function handleKeyPress(evt) {
  1784.         evt.preventDefault();
  1785.         evt.stopPropagation();
  1786.         if(evt.keyCode == 27 || evt.keyCode == 13) {
  1787.             //$("#chat input").focus();
  1788.         }
  1789.         return false;
  1790.     };
  1791.  
  1792.     var recapListener = function(evt) {
  1793.         captureKeyboard();
  1794.     };
  1795.  
  1796.     function captureKeyboard() {
  1797.         $("#piano").off("mousedown", recapListener);
  1798.         $("#piano").off("touchstart", recapListener);
  1799.         $(document).on("keydown", handleKeyDown );
  1800.         $(document).on("keyup", handleKeyUp);
  1801.         $(window).on("keypress", handleKeyPress );
  1802.     };
  1803.  
  1804.     function releaseKeyboard() {
  1805.         $(document).off("keydown", handleKeyDown );
  1806.         $(document).off("keyup", handleKeyUp);
  1807.         $(window).off("keypress", handleKeyPress );
  1808.         $("#piano").on("mousedown", recapListener);
  1809.         $("#piano").on("touchstart", recapListener);
  1810.     };
  1811.  
  1812.     captureKeyboard();
  1813.  
  1814.  
  1815.     velocityFromMouseY = function() {
  1816.         return 0.1 + (my / 100) * 0.6;
  1817.     };
  1818.  
  1819.  
  1820.  
  1821.  
  1822.  
  1823.     // NoteQuota
  1824.     var gNoteQuota = (function() {
  1825.         var last_rat = 0;
  1826.         var nqjq = $("#quota .value");
  1827.         setInterval(function() {
  1828.             gNoteQuota.tick();
  1829.         }, 2000);
  1830.         return new NoteQuota(function(points) {
  1831.             // update UI
  1832.             var rat = (points / this.max) * 100;
  1833.             if(rat <= last_rat)
  1834.                 nqjq.stop(true, true).css("width", rat.toFixed(0) + "%");
  1835.             else
  1836.                 nqjq.stop(true, true).animate({"width": rat.toFixed(0) + "%"}, 2000, "linear");
  1837.             last_rat = rat;
  1838.         });
  1839.     })();
  1840.     gClient.on("nq", function(nq_params) {
  1841.         gNoteQuota.setParams(nq_params);
  1842.     });
  1843.     gClient.on("disconnect", function() {
  1844.         gNoteQuota.setParams(NoteQuota.PARAMS_OFFLINE);
  1845.     });
  1846.  
  1847.  
  1848.  
  1849.     // click participant names
  1850.     (function() {
  1851.         var ele = document.getElementById("names");
  1852.         var touchhandler = function(e) {
  1853.             var target_jq = $(e.target);
  1854.             if(target_jq.hasClass("name")) {
  1855.                 target_jq.addClass("play");
  1856.                 if(e.target.participantId == gClient.participantId) {
  1857.                     openModal("#rename", "input[name=name]");
  1858.                     setTimeout(function() {
  1859.                         $("#rename input[name=name]").val(gClient.ppl[gClient.participantId].name);
  1860.                         $("#rename input[name=color]").val(gClient.ppl[gClient.participantId].color);
  1861.                     }, 100);
  1862.                 } else if(e.target.participantId) {
  1863.                     var id = e.target.participantId;
  1864.                     var part = gClient.ppl[id] || null;
  1865.                     if(part) {
  1866.                         participantMenu(part);
  1867.                         e.stopPropagation();
  1868.                     }
  1869.                 }
  1870.             }
  1871.         };
  1872.         ele.addEventListener("mousedown", touchhandler);
  1873.         ele.addEventListener("touchstart", touchhandler);
  1874.         var releasehandler = function(e) {
  1875.             $("#names .name").removeClass("play");
  1876.         };
  1877.         document.body.addEventListener("mouseup", releasehandler);
  1878.         document.body.addEventListener("touchend", releasehandler);
  1879.  
  1880.         var removeParticipantMenus = function() {
  1881.             $(".participant-menu").remove();
  1882.             $(".participantSpotlight").hide();
  1883.             document.removeEventListener("mousedown", removeParticipantMenus);
  1884.             document.removeEventListener("touchstart", removeParticipantMenus);
  1885.         };
  1886.  
  1887.         var participantMenu = function(part) {
  1888.             if(!part) return;
  1889.             removeParticipantMenus();
  1890.             document.addEventListener("mousedown", removeParticipantMenus);
  1891.             document.addEventListener("touchstart", removeParticipantMenus);
  1892.             $("#" + part.id).find(".enemySpotlight").show();
  1893.             var menu = $('<div class="participant-menu"></div>');
  1894.             $("body").append(menu);
  1895.             // move menu to name position
  1896.             var jq_nd = $(part.nameDiv);
  1897.             var pos = jq_nd.position();
  1898.             menu.css({
  1899.                 "top": pos.top + jq_nd.height() + 15,
  1900.                 "left": pos.left + 6,
  1901.                 "background": part.color || "black"
  1902.             });
  1903.             menu.on("mousedown touchstart", function(evt) {
  1904.                 evt.stopPropagation();
  1905.                 var target = $(evt.target);
  1906.                 if(target.hasClass("menu-item")) {
  1907.                     target.addClass("clicked");
  1908.                     menu.fadeOut(200, function() {
  1909.                         removeParticipantMenus();
  1910.                     });
  1911.                 }
  1912.             });
  1913.             // this spaces stuff out but also can be used for informational
  1914.             $('<div class="info"></div>').appendTo(menu).text(part._id);
  1915.             // add menu items
  1916.             if(gPianoMutes.indexOf(part._id) == -1) {
  1917.                 $('<div class="menu-item">Mute Notes</div>').appendTo(menu)
  1918.                 .on("mousedown touchstart", function(evt) {
  1919.                     gPianoMutes.push(part._id);
  1920.                     $(part.nameDiv).addClass("muted-notes");
  1921.                 });
  1922.             } else {
  1923.                 $('<div class="menu-item">Unmute Notes</div>').appendTo(menu)
  1924.                 .on("mousedown touchstart", function(evt) {
  1925.                     var i;
  1926.                     while((i = gPianoMutes.indexOf(part._id)) != -1)
  1927.                         gPianoMutes.splice(i, 1);
  1928.                     $(part.nameDiv).removeClass("muted-notes");
  1929.                 });
  1930.             }
  1931.             if(gChatMutes.indexOf(part._id) == -1) {
  1932.                 $('<div class="menu-item">Mute Chat</div>').appendTo(menu)
  1933.                 .on("mousedown touchstart", function(evt) {
  1934.                     gChatMutes.push(part._id);
  1935.                     $(part.nameDiv).addClass("muted-chat");
  1936.                 });
  1937.             } else {
  1938.                 $('<div class="menu-item">Unmute Chat</div>').appendTo(menu)
  1939.                 .on("mousedown touchstart", function(evt) {
  1940.                     var i;
  1941.                     while((i = gChatMutes.indexOf(part._id)) != -1)
  1942.                         gChatMutes.splice(i, 1);
  1943.                     $(part.nameDiv).removeClass("muted-chat");
  1944.                 });
  1945.             }
  1946.             if(!(gPianoMutes.indexOf(part._id) >= 0) || !(gChatMutes.indexOf(part._id) >= 0)) {
  1947.                 $('<div class="menu-item">Mute Completely</div>').appendTo(menu)
  1948.                 .on("mousedown touchstart", function(evt) {
  1949.                     gPianoMutes.push(part._id);
  1950.                     gChatMutes.push(part._id);
  1951.                     $(part.nameDiv).addClass("muted-notes");
  1952.                     $(part.nameDiv).addClass("muted-chat");
  1953.                 });
  1954.             }
  1955.             if((gPianoMutes.indexOf(part._id) >= 0) || (gChatMutes.indexOf(part._id) >= 0)) {
  1956.                 $('<div class="menu-item">Unmute Completely</div>').appendTo(menu)
  1957.                 .on("mousedown touchstart", function(evt) {
  1958.                     var i;
  1959.                     while((i = gPianoMutes.indexOf(part._id)) != -1)
  1960.                         gPianoMutes.splice(i, 1);
  1961.                     while((i = gChatMutes.indexOf(part._id)) != -1)
  1962.                         gChatMutes.splice(i, 1);
  1963.                     $(part.nameDiv).removeClass("muted-notes");
  1964.                     $(part.nameDiv).removeClass("muted-chat");
  1965.                 });
  1966.             }
  1967.             if(gClient.isOwner()) {
  1968.                 $('<div class="menu-item give-crown">Give Crown</div>').appendTo(menu)
  1969.                 .on("mousedown touchstart", function(evt) {
  1970.                     if(confirm("Give room ownership to "+part.name+"?"))
  1971.                         gClient.sendArray([{m: "chown", id: part.id}]);
  1972.                 });
  1973.                 $('<div class="menu-item kickban">Kickban</div>').appendTo(menu)
  1974.                 .on("mousedown touchstart", function(evt) {
  1975.                     var minutes = prompt("How many minutes? (0-60)", "30");
  1976.                     if(minutes === null) return;
  1977.                     minutes = parseFloat(minutes) || 0;
  1978.                     var ms = minutes * 60 * 1000;
  1979.                     gClient.sendArray([{m: "kickban", _id: part._id, ms: ms}]);
  1980.                 });
  1981.             }
  1982.             menu.fadeIn(100);
  1983.         };
  1984.     })();
  1985.    
  1986.  
  1987.  
  1988.  
  1989.  
  1990.  
  1991.  
  1992.  
  1993.  
  1994.  
  1995.  
  1996.  
  1997.  
  1998.  
  1999.  
  2000.  
  2001. // Notification class
  2002.  
  2003. ////////////////////////////////////////////////////////////////
  2004.  
  2005.     var Notification = function(par) {
  2006.         EventEmitter.call(this);
  2007.  
  2008.         var par = par || {};
  2009.  
  2010.         this.id = "Notification-" + (par.id || Math.random());
  2011.         this.title = par.title || "";
  2012.         this.text = par.text || "";
  2013.         this.html = par.html || "";
  2014.         this.target = $(par.target || "#piano");
  2015.         this.duration = par.duration || 30000;
  2016.         this["class"] = par["class"] || "classic";
  2017.        
  2018.         var self = this;
  2019.         var eles = $("#" + this.id);
  2020.         if(eles.length > 0) {
  2021.             eles.remove();
  2022.         }
  2023.         this.domElement = $('<div class="notification"><div class="notification-body"><div class="title"></div>' +
  2024.             '<div class="text"></div></div><div class="x">x</div></div>');
  2025.         this.domElement[0].id = this.id;
  2026.         this.domElement.addClass(this["class"]);
  2027.         this.domElement.find(".title").text(this.title);
  2028.         if(this.text.length > 0) {
  2029.             this.domElement.find(".text").text(this.text);
  2030.         } else if(this.html instanceof HTMLElement) {
  2031.             this.domElement.find(".text")[0].appendChild(this.html);
  2032.         } else if(this.html.length > 0) {
  2033.             this.domElement.find(".text").html(this.html);
  2034.         }
  2035.         document.body.appendChild(this.domElement.get(0));
  2036.        
  2037.         this.position();
  2038.         this.onresize = function() {
  2039.             self.position();
  2040.         };
  2041.         window.addEventListener("resize", this.onresize);
  2042.  
  2043.         this.domElement.find(".x").click(function() {
  2044.             self.close();
  2045.         });
  2046.  
  2047.         if(this.duration > 0) {
  2048.             setTimeout(function() {
  2049.                 self.close();
  2050.             }, this.duration);
  2051.         }
  2052.  
  2053.         return this;
  2054.     }
  2055.  
  2056.     mixin(Notification.prototype, EventEmitter.prototype);
  2057.     Notification.prototype.constructor = Notification;
  2058.  
  2059.     Notification.prototype.position = function() {
  2060.         var pos = this.target.offset();
  2061.         var x = pos.left - (this.domElement.width() / 2) + (this.target.width() / 4);
  2062.         var y = pos.top - this.domElement.height() - 8;
  2063.         var width = this.domElement.width();
  2064.         if(x + width > $("body").width()) {
  2065.             x -= ((x + width) - $("body").width());
  2066.         }
  2067.         if(x < 0) x = 0;
  2068.         this.domElement.offset({left: x, top: y});
  2069.     };
  2070.  
  2071.     Notification.prototype.close = function() {
  2072.         var self = this;
  2073.         window.removeEventListener("resize",  this.onresize);
  2074.         this.domElement.fadeOut(500, function() {
  2075.             self.domElement.remove();
  2076.             self.emit("close");
  2077.         });
  2078.     };
  2079.  
  2080.  
  2081.  
  2082.  
  2083.  
  2084.  
  2085.  
  2086.  
  2087.  
  2088.  
  2089.  
  2090.  
  2091.  
  2092.  
  2093.  
  2094. // set variables from settings or set settings
  2095.  
  2096. ////////////////////////////////////////////////////////////////
  2097.  
  2098.     var gKeyboardSeq = 0;
  2099.     var gKnowsYouCanUseKeyboard = false;
  2100.     if(localStorage && localStorage.knowsYouCanUseKeyboard) gKnowsYouCanUseKeyboard = true;
  2101.     if(!gKnowsYouCanUseKeyboard) {
  2102.         window.gKnowsYouCanUseKeyboardTimeout = setTimeout(function() {
  2103.             window.gKnowsYouCanUseKeyboardNotification = new Notification({title: "Did you know!?!",
  2104.                 text: "You can play the piano with your keyboard, too.  Try it!", target: "#piano", duration: 10000});
  2105.         }, 30000);
  2106.     }
  2107.  
  2108.  
  2109.  
  2110.  
  2111.     if(window.localStorage) {
  2112.  
  2113.         if(localStorage.volume) {
  2114.             volume_slider.value = localStorage.volume;
  2115.             gPiano.audio.setVolume(localStorage.volume);
  2116.             $("#volume-label").text("Volume: " + Math.floor(gPiano.audio.volume * 100) + "%");
  2117.         }
  2118.         else localStorage.volume = gPiano.audio.volume;
  2119.  
  2120.         window.gHasBeenHereBefore = (localStorage.gHasBeenHereBefore || false);
  2121.         if(gHasBeenHereBefore) {
  2122.         }
  2123.         localStorage.gHasBeenHereBefore = true;
  2124.        
  2125.     }
  2126.    
  2127.    
  2128.    
  2129.    
  2130.     // warn user about loud noises before starting sound (no autoplay)
  2131.     openModal("#sound-warning");
  2132.     var user_interact = function(evt) {
  2133.         document.removeEventListener("click", user_interact);
  2134.         closeModal();
  2135.         MPP.piano.audio.resume();
  2136.     }
  2137.     document.addEventListener("click", user_interact);
  2138.  
  2139.  
  2140.  
  2141.  
  2142.  
  2143.  
  2144.  
  2145.  
  2146.  
  2147.  
  2148.  
  2149.  
  2150.  
  2151. // New room, change room
  2152.  
  2153. ////////////////////////////////////////////////////////////////
  2154.  
  2155.     $("#room > .info").text("--");
  2156.     gClient.on("ch", function(msg) {
  2157.         var channel = msg.ch;
  2158.         var info = $("#room > .info");
  2159.         info.text(channel._id);
  2160.         if(channel.settings.lobby) info.addClass("lobby");
  2161.         else info.removeClass("lobby");
  2162.         if(!channel.settings.chat) info.addClass("no-chat");
  2163.         else info.removeClass("no-chat");
  2164.         if(channel.settings.crownsolo) info.addClass("crownsolo");
  2165.         else info.removeClass("crownsolo");
  2166.         if(!channel.settings.visible) info.addClass("not-visible");
  2167.         else info.removeClass("not-visible");
  2168.     });
  2169.     gClient.on("ls", function(ls) {
  2170.         for(var i in ls.u) {
  2171.             if(!ls.u.hasOwnProperty(i)) continue;
  2172.             var room = ls.u[i];
  2173.             var info = $("#room .info[roomname=\"" + (room._id + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0') + "\"]");
  2174.             if(info.length == 0) {
  2175.                 info = $("<div class=\"info\"></div>");
  2176.                 info.attr("roomname", room._id);
  2177.                 $("#room .more").append(info);
  2178.             }
  2179.             info.text(room._id + " (" + room.count + ")");
  2180.             if(room.settings.lobby) info.addClass("lobby");
  2181.             else info.removeClass("lobby");
  2182.             if(!room.settings.chat) info.addClass("no-chat");
  2183.             else info.removeClass("no-chat");
  2184.             if(room.settings.crownsolo) info.addClass("crownsolo");
  2185.             else info.removeClass("crownsolo");
  2186.             if(!room.settings.visible) info.addClass("not-visible");
  2187.             else info.removeClass("not-visible");
  2188.             if(room.banned) info.addClass("banned");
  2189.             else info.removeClass("banned");
  2190.         }
  2191.     });
  2192.     $("#room").on("click", function(evt) {
  2193.         evt.stopPropagation();
  2194.  
  2195.         // clicks on a new room
  2196.         if($(evt.target).hasClass("info") && $(evt.target).parents(".more").length) {
  2197.             $("#room .more").fadeOut(250);
  2198.             var selected_name = $(evt.target).attr("roomname");
  2199.             if(typeof selected_name != "undefined") {
  2200.                 changeRoom(selected_name, "right");
  2201.             }
  2202.             return false;
  2203.         }
  2204.         // clicks on "New Room..."
  2205.         else if($(evt.target).hasClass("new")) {
  2206.             openModal("#new-room", "input[name=name]");
  2207.         }
  2208.         // all other clicks
  2209.         var doc_click = function(evt) {
  2210.             if($(evt.target).is("#room .more")) return;
  2211.             $(document).off("mousedown", doc_click);
  2212.             $("#room .more").fadeOut(250);
  2213.             gClient.sendArray([{m: "-ls"}]);
  2214.         }
  2215.         $(document).on("mousedown", doc_click);
  2216.         $("#room .more .info").remove();
  2217.         $("#room .more").show();
  2218.         gClient.sendArray([{m: "+ls"}]);
  2219.     });
  2220.     $("#new-room-btn").on("click", function(evt) {
  2221.         evt.stopPropagation();
  2222.         openModal("#new-room", "input[name=name]");
  2223.     });
  2224.  
  2225.  
  2226.     $("#play-alone-btn").on("click", function(evt) {
  2227.         evt.stopPropagation();
  2228.         var room_name = "Room" + Math.floor(Math.random() * 1000000000000);
  2229.         changeRoom(room_name, "right", {"visible": false, "chat": true, "crownsolo": false});
  2230.         setTimeout(function() {
  2231.             new Notification({id: "share", title: "Playing alone", html: 'You are playing alone in a room by yourself, but you can always invite \
  2232.                 friends by sending them the link.<br/><br/>\
  2233.                 <a href="#" onclick="window.open(\'https://www.facebook.com/sharer/sharer.php?u=\'+encodeURIComponent(location.href),\'facebook-share-dialog\',\'width=626,height=436\');return false;">Share on Facebook</a><br/><br/>\
  2234.                 <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Tweet</a>', duration: 25000});
  2235.         }, 1000);
  2236.     });
  2237.  
  2238.    
  2239.  
  2240.     var gModal;
  2241.  
  2242.     function modalHandleEsc(evt) {
  2243.         if(evt.keyCode == 27) {
  2244.             closeModal();
  2245.             evt.preventDefault();
  2246.             evt.stopPropagation();
  2247.         }
  2248.     };
  2249.    
  2250.     function openModal(selector, focus) {
  2251.         if(chat) chat.blur();
  2252.         releaseKeyboard();
  2253.         $(document).on("keydown", modalHandleEsc);
  2254.         $("#modal #modals > *").hide();
  2255.         $("#modal").fadeIn(250);
  2256.         $(selector).show();
  2257.         setTimeout(function() {
  2258.             $(selector).find(focus).focus();
  2259.         }, 100);
  2260.         gModal = selector;
  2261.     };
  2262.  
  2263.     function closeModal() {
  2264.         $(document).off("keydown", modalHandleEsc);
  2265.         $("#modal").fadeOut(100);
  2266.         $("#modal #modals > *").hide();
  2267.         captureKeyboard();
  2268.         gModal = null;
  2269.     };
  2270.  
  2271.     var modal_bg = $("#modal .bg")[0];
  2272.     $(modal_bg).on("click", function(evt) {
  2273.         if(evt.target != modal_bg) return;
  2274.         closeModal();
  2275.     });
  2276.  
  2277.     (function() {
  2278.         function submit() {
  2279.             var name = $("#new-room .text[name=name]").val();
  2280.             var settings = {
  2281.                 visible: $("#new-room .checkbox[name=visible]").is(":checked"),
  2282.                 chat: true,
  2283.                 crownsolo: false
  2284.             };
  2285.             $("#new-room .text[name=name]").val("");
  2286.             closeModal();
  2287.             changeRoom(name, "right", settings);
  2288.             setTimeout(function() {
  2289.             new Notification({id: "share", title: "Created a Room", html: 'You can invite friends to your room by sending them the link.<br/><br/>\
  2290.                 <a href="#" onclick="window.open(\'https://www.facebook.com/sharer/sharer.php?u=\'+encodeURIComponent(location.href),\'facebook-share-dialog\',\'width=626,height=436\');return false;">Share on Facebook</a><br/><br/>\
  2291.                 <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Tweet</a>', duration: 25000});
  2292.         }, 1000);
  2293.         };
  2294.         $("#new-room .submit").click(function(evt) {
  2295.             submit();
  2296.         });
  2297.         $("#new-room .text[name=name]").keypress(function(evt) {
  2298.             if(evt.keyCode == 13) {
  2299.                 submit();
  2300.             } else if(evt.keyCode == 27) {
  2301.                 closeModal();
  2302.             } else {
  2303.                 return;
  2304.             }
  2305.             evt.preventDefault();
  2306.             evt.stopPropagation();
  2307.             return false;
  2308.         });
  2309.     })();
  2310.  
  2311.  
  2312.  
  2313.    
  2314.  
  2315.  
  2316.  
  2317.  
  2318.     function changeRoom(name, direction, settings, push) {
  2319.         if(!settings) settings = {};
  2320.         if(!direction) direction = "right";
  2321.         if(typeof push == "undefined") push = true;
  2322.         var opposite = direction == "left" ? "right" : "left";
  2323.  
  2324.         if(name == "") name = "lobby";
  2325.         if(gClient.channel && gClient.channel._id === name) return;
  2326.         if(push) {
  2327.             var url = "/" + encodeURIComponent(name).replace("'", "%27");
  2328.             if(window.history && history.pushState) {
  2329.                 history.pushState({"depth": gHistoryDepth += 1, "name": name}, "Piano > " + name, url);
  2330.             } else {
  2331.                 window.location = url;
  2332.                 return;
  2333.             }
  2334.         }
  2335.        
  2336.         gClient.setChannel(name, settings);
  2337.  
  2338.         var t = 0, d = 100;
  2339.         $("#piano").addClass("ease-out").addClass("slide-" + opposite);
  2340.         setTimeout(function() {
  2341.             $("#piano").removeClass("ease-out").removeClass("slide-" + opposite).addClass("slide-" + direction);
  2342.         }, t += d);
  2343.         setTimeout(function() {
  2344.             $("#piano").addClass("ease-in").removeClass("slide-" + direction);
  2345.         }, t += d);
  2346.         setTimeout(function() {
  2347.             $("#piano").removeClass("ease-in");
  2348.         }, t += d);
  2349.     };
  2350.  
  2351.     var gHistoryDepth = 0;
  2352.     $(window).on("popstate", function(evt) {
  2353.         var depth = evt.state ? evt.state.depth : 0;
  2354.         if(depth == gHistoryDepth) return; // <-- forgot why I did that though...
  2355.        
  2356.         var direction = depth <= gHistoryDepth ? "left" : "right";
  2357.         gHistoryDepth = depth;
  2358.  
  2359.         var name = decodeURIComponent(window.location.pathname);
  2360.         if(name.substr(0, 1) == "/") name = name.substr(1);
  2361.         changeRoom(name, direction, null, false);
  2362.     });
  2363.  
  2364.  
  2365.  
  2366.  
  2367.  
  2368.  
  2369.  
  2370.  
  2371.  
  2372.  
  2373.  
  2374.  
  2375.  
  2376.  
  2377.  
  2378.  
  2379.  
  2380.  
  2381.  
  2382.  
  2383. // Rename
  2384.  
  2385. ////////////////////////////////////////////////////////////////
  2386.  
  2387. (function() {
  2388.         function submit() {
  2389.             var set = {
  2390.                 name: $("#rename input[name=name]").val(),
  2391.                 color: $("#rename input[name=color]").val()
  2392.             };
  2393.             //$("#rename .text[name=name]").val("");
  2394.             closeModal();
  2395.             gClient.sendArray([{m: "userset", set: set}]);
  2396.         };
  2397.         $("#rename .submit").click(function(evt) {
  2398.             submit();
  2399.         });
  2400.         $("#rename .text[name=name]").keypress(function(evt) {
  2401.             if(evt.keyCode == 13) {
  2402.                 submit();
  2403.             } else if(evt.keyCode == 27) {
  2404.                 closeModal();
  2405.             } else {
  2406.                 return;
  2407.             }
  2408.             evt.preventDefault();
  2409.             evt.stopPropagation();
  2410.             return false;
  2411.         });
  2412.     })();
  2413.  
  2414.  
  2415.  
  2416.  
  2417.  
  2418.  
  2419.  
  2420.  
  2421.  
  2422.  
  2423.  
  2424.  
  2425.  
  2426.  
  2427.  
  2428. // chatctor
  2429.  
  2430. ////////////////////////////////////////////////////////////////
  2431. msgBuffer = [];msgVirtualQuota = 3;mvq = setInterval(function(){if(msgVirtualQuota<3){msgVirtualQuota++}},2500);
  2432.     var chat = (function() {
  2433.         gClient.on("ch", function(msg) {
  2434.             if(msg.ch.settings.chat) {
  2435.                 chat.show();
  2436.             } else {
  2437.                 chat.hide();
  2438.             }
  2439.         });
  2440.         gClient.on("disconnect", function(msg) {
  2441.             chat.hide();
  2442.         });
  2443.         gClient.on("c", function(msg) {
  2444.             chat.clear();
  2445.             if(msg.c) {
  2446.                 for(var i = 0; i < msg.c.length; i++) {
  2447.                     chat.receive(msg.c[i]);
  2448.                 }
  2449.             }
  2450.         });
  2451.         gClient.on("a", function(msg) {
  2452.             chat.receive(msg);
  2453.         });
  2454.  
  2455.         $("#chat input").on("focus", function(evt) {
  2456.             releaseKeyboard();
  2457.             $("#chat").addClass("chatting");
  2458.             chat.scrollToBottom();
  2459.         });
  2460.         /*$("#chat input").on("blur", function(evt) {
  2461.             captureKeyboard();
  2462.             $("#chat").removeClass("chatting");
  2463.             chat.scrollToBottom();
  2464.         });*/
  2465.         $(document).mousedown(function(evt) {
  2466.             if(!$("#chat").has(evt.target).length > 0) {
  2467.                 chat.blur();
  2468.             }
  2469.         });
  2470.         document.addEventListener("touchstart", function(event) {
  2471.             for(var i in event.changedTouches) {
  2472.                 var touch = event.changedTouches[i];
  2473.                 if(!$("#chat").has(touch.target).length > 0) {
  2474.                     chat.blur();
  2475.                 }
  2476.             }
  2477.         });
  2478.         $(document).on("keydown", function(evt) {
  2479.             if($("#chat").hasClass("chatting")) {
  2480.                 if(evt.keyCode == 27) {
  2481.                     chat.blur();
  2482.                     evt.preventDefault();
  2483.                     evt.stopPropagation();
  2484.                 } else if(evt.keyCode == 13) {
  2485.                     $("#chat input").focus();
  2486.                 }
  2487.             } else if(!gModal && (evt.keyCode == 27 || evt.keyCode == 13)) {
  2488.                 $("#chat input").focus();
  2489.             }
  2490.         });
  2491.         $("#chat input").on("keydown", function(evt) {
  2492.             if(evt.keyCode == 13) {
  2493.                 var message = $(this).val();
  2494.                 if(message.length == 0) {
  2495.                     setTimeout(function() {
  2496.                         chat.blur();
  2497.                     }, 100);
  2498.                 } else if(message.length <= 512) {
  2499.                     chat.send(message);
  2500.                     $(this).val("");
  2501.                     setTimeout(function() {
  2502.                         chat.blur();
  2503.                     }, 100);
  2504.                 }
  2505.                 evt.preventDefault();
  2506.                 evt.stopPropagation();
  2507.             } else if(evt.keyCode == 27) {
  2508.                 chat.blur();
  2509.                 evt.preventDefault();
  2510.                 evt.stopPropagation();
  2511.             } else if(evt.keyCode == 9) {
  2512.                 evt.preventDefault();
  2513.                 evt.stopPropagation();
  2514.             }
  2515.         });
  2516.  
  2517.         return {
  2518.             show: function() {
  2519.                 $("#chat").fadeIn();
  2520.             },
  2521.  
  2522.             hide: function() {
  2523.                 $("#chat").fadeOut();
  2524.             },
  2525.  
  2526.             clear: function() {
  2527.                 $("#chat li").remove();
  2528.             },
  2529.  
  2530.             scrollToBottom: function() {
  2531.                 var ele = $("#chat ul").get(0);
  2532.                 ele.scrollTop = ele.scrollHeight;
  2533.             },
  2534.  
  2535.             blur: function() {
  2536.                 if($("#chat").hasClass("chatting")) {
  2537.                     $("#chat input").get(0).blur();
  2538.                     $("#chat").removeClass("chatting");
  2539.                     chat.scrollToBottom();
  2540.                     captureKeyboard();
  2541.                 }
  2542.             },
  2543.  
  2544.             send: function(message,b) {
  2545.                 if(b){
  2546.                     gClient.sendArray([{m:"a", message: message}]);
  2547.                 }else{
  2548.                 if(typeof message == 'string' && message.length<2048 && msgBuffer.length<16)
  2549.                 if(message.length<512){var msg = message;var multi = -1;while((msg+';'+msgBuffer.slice(0,multi+1).join(';')).length<512 && multi+1<msgBuffer.length){multi++};
  2550.                 if(multi==-1){msgBuffer.push(msg)}else{msgBuffer = msgBuffer.slice(multi);msgBuffer[0] = msgBuffer.slice(0,multi+1).reverse().join(';')+';'+msg}}
  2551.                 else
  2552.                 {var msg = message;while(msg.length>512){msgBuffer.push(msg.slice(0,500)+'...');msg = msg.slice(500)};msgBuffer.push(msg)}
  2553.                     if(msgVirtualQuota>0 && message == undefined){gClient.sendArray([{m:"a", message: msgBuffer[0]}]);msgBuffer.shift();msgVirtualQuota--}
  2554.                     if(msgBuffer.length>0 || message!==undefined){setTimeout(MPP.chat.send,250)};
  2555.                 };
  2556.             },
  2557.  
  2558.             receive: function(msg) {
  2559.                 if(gChatMutes.indexOf(msg.p._id) != -1) return;
  2560.  
  2561.                 var li = $('<li><span class="name"/><span class="message"/>');
  2562.  
  2563.                 li.find(".name").text(msg.p.name + ":");
  2564.                 li.find(".message").text(msg.a);
  2565.                 li.css("color", msg.p.color || "white");
  2566.  
  2567.                 $("#chat ul").append(li);
  2568.  
  2569.                 var eles = $("#chat ul li").get();
  2570.                 for(var i = 1; i <= 50 && i <= eles.length; i++) {
  2571.                     eles[eles.length - i].style.opacity = 1.0 - (i * 0.03);
  2572.                 }
  2573.                 if(eles.length > 50) {
  2574.                     eles[0].style.display = "none";
  2575.                 }
  2576.                 if(eles.length > 256) {
  2577.                     $(eles[0]).remove();
  2578.                 }
  2579.  
  2580.                 // scroll to bottom if not "chatting" or if not scrolled up
  2581.                 if(!$("#chat").hasClass("chatting")) {
  2582.                     chat.scrollToBottom();
  2583.                 } else {
  2584.                     var ele = $("#chat ul").get(0);
  2585.                     if(ele.scrollTop > ele.scrollHeight - ele.offsetHeight - 50)
  2586.                         chat.scrollToBottom();
  2587.                 }
  2588.             }
  2589.         };
  2590.     })();
  2591.    
  2592.  
  2593.  
  2594.  
  2595.  
  2596.  
  2597.  
  2598.  
  2599.  
  2600.  
  2601.  
  2602.  
  2603.  
  2604.  
  2605.  
  2606. // MIDI
  2607.  
  2608. ////////////////////////////////////////////////////////////////
  2609.  
  2610.     var MIDI_TRANSPOSE = -12;
  2611.     var MIDI_KEY_NAMES = ["a-1", "as-1", "b-1"];
  2612.     var bare_notes = "c cs d ds e f fs g gs a as b".split(" ");
  2613.     for(var oct = 0; oct < 7; oct++) {
  2614.         for(var i in bare_notes) {
  2615.             MIDI_KEY_NAMES.push(bare_notes[i] + oct);
  2616.         }
  2617.     }
  2618.     MIDI_KEY_NAMES.push("c7");
  2619.  
  2620.     var devices_json;
  2621.     function sendDevices() {
  2622.         gClient.sendArray([{"m": "devices", "list": JSON.parse(devices_json)}]);
  2623.     }
  2624.     gClient.on("connect", sendDevices);
  2625.  
  2626.     (function() {
  2627.  
  2628.         if (navigator.requestMIDIAccess) {
  2629.             navigator.requestMIDIAccess().then(
  2630.                 function(midi) {
  2631.                     console.log(midi);
  2632.                     function midimessagehandler(evt) {
  2633.                         if(!evt.target.enabled) return;
  2634.                         //console.log(evt);
  2635.                         var channel = evt.data[0] & 0xf;
  2636.                         var cmd = evt.data[0] >> 4;
  2637.                         var note_number = evt.data[1];
  2638.                         var vel = evt.data[2];
  2639.                         //console.log(channel, cmd, note_number, vel);
  2640.                         if(cmd == 8 || (cmd == 9 && vel == 0)) {
  2641.                             // NOTE_OFF
  2642.                             release(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE]);
  2643.                         } else if(cmd == 9) {
  2644.                             // NOTE_ON
  2645.                             press(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE], vel / 100);
  2646.                         } else if(cmd == 11) {
  2647.                             // CONTROL_CHANGE
  2648.                             if(!gAutoSustain) {
  2649.                                 if(note_number == 64) {
  2650.                                     if(vel > 0) {
  2651.                                         pressSustain();
  2652.                                     } else {
  2653.                                         releaseSustain();
  2654.                                     }
  2655.                                 }
  2656.                             }
  2657.                         }
  2658.                     }
  2659.  
  2660.                     function deviceInfo(dev) {
  2661.                         return {
  2662.                             type: dev.type,
  2663.                             //id: dev.id,
  2664.                             manufacturer: dev.manufacturer,
  2665.                             name: dev.name,
  2666.                             version: dev.version,
  2667.                             //connection: dev.connection,
  2668.                             //state: dev.state,
  2669.                             enabled: dev.enabled
  2670.                         };
  2671.                     }
  2672.  
  2673.                     function updateDevices() {
  2674.                         var list = [];
  2675.                         if(midi.inputs.size > 0) {
  2676.                             var inputs = midi.inputs.values();
  2677.                             for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2678.                                 var input = input_it.value;
  2679.                                 list.push(deviceInfo(input));
  2680.                             }
  2681.                         }
  2682.                         if(midi.outputs.size > 0) {
  2683.                             var outputs = midi.outputs.values();
  2684.                             for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2685.                                 var output = output_it.value;
  2686.                                 list.push(deviceInfo(output));
  2687.                             }
  2688.                         }
  2689.                         var new_json = JSON.stringify(list);
  2690.                         if(new_json !== devices_json) {
  2691.                             devices_json = new_json;
  2692.                             sendDevices();
  2693.                         }
  2694.                     }
  2695.  
  2696.                     function plug() {
  2697.                         if(midi.inputs.size > 0) {
  2698.                             var inputs = midi.inputs.values();
  2699.                             for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2700.                                 var input = input_it.value;
  2701.                                 //input.removeEventListener("midimessage", midimessagehandler);
  2702.                                 //input.addEventListener("midimessage", midimessagehandler);
  2703.                                 input.onmidimessage = midimessagehandler;
  2704.                                 if(input.enabled !== false) {
  2705.                                     input.enabled = true;
  2706.                                     localSound = false;
  2707.                                     MPP.chat.send("MIDI input detected. Local sound is off.")
  2708.                                 }
  2709.                                 console.log("input", input);
  2710.                             }
  2711.                         }
  2712.                         if(midi.outputs.size > 0) {
  2713.                             var outputs = midi.outputs.values();
  2714.                             for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2715.                                 var output = output_it.value;
  2716.                                 //output.enabled = false; // edit: don't touch
  2717.                                 console.log("output", output);
  2718.                             }
  2719.                             gMidiOutTest = function(note_name, vel, delay_ms) {
  2720.                                 var note_number = MIDI_KEY_NAMES.indexOf(note_name);
  2721.                                 if(note_number == -1) return;
  2722.                                 note_number = note_number + 9 - MIDI_TRANSPOSE;
  2723.  
  2724.                                 var outputs = midi.outputs.values();
  2725.                                 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2726.                                     var output = output_it.value;
  2727.                                     if(output.enabled) {
  2728.                                         output.send([0x90, note_number, vel], window.performance.now() + delay_ms);
  2729.                                     }
  2730.                                 }
  2731.                             }
  2732.                         }
  2733.                         showConnections(false);
  2734.                         updateDevices();
  2735.                     }
  2736.  
  2737.                     midi.addEventListener("statechange", function(evt) {
  2738.                         if(evt instanceof MIDIConnectionEvent) {
  2739.                             plug();
  2740.                         }
  2741.                     });
  2742.  
  2743.                     plug();
  2744.  
  2745.  
  2746.                     var connectionsNotification;
  2747.  
  2748.                     function showConnections(sticky) {
  2749.                         //if(document.getElementById("Notification-MIDI-Connections"))
  2750.                             //sticky = 1; // todo: instead,
  2751.                         var inputs_ul = document.createElement("ul");
  2752.                         if(midi.inputs.size > 0) {
  2753.                             var inputs = midi.inputs.values();
  2754.                             for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2755.                                 var input = input_it.value;
  2756.                                 var li = document.createElement("li");
  2757.                                 li.connectionId = input.id;
  2758.                                 li.classList.add("connection");
  2759.                                 if(input.enabled) li.classList.add("enabled");
  2760.                                 li.textContent = input.name;
  2761.                                 li.addEventListener("click", function(evt) {
  2762.                                     var inputs = midi.inputs.values();
  2763.                                     for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2764.                                         var input = input_it.value;
  2765.                                         if(input.id === evt.target.connectionId) {
  2766.                                             input.enabled = !input.enabled;
  2767.                                             evt.target.classList.toggle("enabled");
  2768.                                             console.log("click", input);
  2769.                                             updateDevices();
  2770.                                             return;
  2771.                                         }
  2772.                                     }
  2773.                                 });
  2774.                                 inputs_ul.appendChild(li);
  2775.                             }
  2776.                         } else {
  2777.                             inputs_ul.textContent = "(none)";
  2778.                         }
  2779.                         var outputs_ul = document.createElement("ul");
  2780.                         if(midi.outputs.size > 0) {
  2781.                             var outputs = midi.outputs.values();
  2782.                             for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2783.                                 var output = output_it.value;
  2784.                                 var li = document.createElement("li");
  2785.                                 li.connectionId = output.id;
  2786.                                 li.classList.add("connection");
  2787.                                 if(output.enabled) li.classList.add("enabled");
  2788.                                 li.textContent = output.name;
  2789.                                 li.addEventListener("click", function(evt) {
  2790.                                     var outputs = midi.outputs.values();
  2791.                                     for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2792.                                         var output = output_it.value;
  2793.                                         if(output.id === evt.target.connectionId) {
  2794.                                             output.enabled = !output.enabled;
  2795.                                             evt.target.classList.toggle("enabled");
  2796.                                             console.log("click", output);
  2797.                                             updateDevices();
  2798.                                             return;
  2799.                                         }
  2800.                                     }
  2801.                                 });
  2802.                                 outputs_ul.appendChild(li);
  2803.                             }
  2804.                         } else {
  2805.                             outputs_ul.textContent = "(none)";
  2806.                         }
  2807.                         var div = document.createElement("div");
  2808.                         var h1 = document.createElement("h1");
  2809.                         h1.textContent = "Inputs";
  2810.                         div.appendChild(h1);
  2811.                         div.appendChild(inputs_ul);
  2812.                         h1 = document.createElement("h1");
  2813.                         h1.textContent = "Outputs";
  2814.                         div.appendChild(h1);
  2815.                         div.appendChild(outputs_ul);
  2816.                         connectionsNotification = new Notification({"id":"MIDI-Connections", "title":"MIDI Connections","duration":sticky?"-1":"4500","html":div,"target":"#midi-btn"});
  2817.                     }
  2818.  
  2819.                     document.getElementById("midi-btn").addEventListener("click", function(evt) {
  2820.                         if(!document.getElementById("Notification-MIDI-Connections"))
  2821.                             showConnections(true);
  2822.                         else {
  2823.                             connectionsNotification.close();
  2824.                         }
  2825.                     });
  2826.                 },
  2827.                 function(err){
  2828.                     console.log(err);
  2829.                 } );
  2830.         }
  2831.     })();
  2832.  
  2833.  
  2834.  
  2835.  
  2836.  
  2837.  
  2838.  
  2839.  
  2840.  
  2841.  
  2842.  
  2843.  
  2844.  
  2845.  
  2846. // bug supply
  2847.  
  2848. ////////////////////////////////////////////////////////////////
  2849.    
  2850.     window.onerror = function(message, url, line) {
  2851.         var url = url || "(no url)";
  2852.         var line = line || "(no line)";
  2853.         // errors in socket.io
  2854.         if(url.indexOf("socket.io.js") !== -1) {
  2855.             if(message.indexOf("INVALID_STATE_ERR") !== -1) return;
  2856.             if(message.indexOf("InvalidStateError") !== -1) return;
  2857.             if(message.indexOf("DOM Exception 11") !== -1) return;
  2858.             if(message.indexOf("Property 'open' of object #<c> is not a function") !== -1) return;
  2859.             if(message.indexOf("Cannot call method 'close' of undefined") !== -1) return;
  2860.             if(message.indexOf("Cannot call method 'close' of null") !== -1) return;
  2861.             if(message.indexOf("Cannot call method 'onClose' of null") !== -1) return;
  2862.             if(message.indexOf("Cannot call method 'payload' of null") !== -1) return;
  2863.             if(message.indexOf("Unable to get value of the property 'close'") !== -1) return;
  2864.             if(message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) return;
  2865.             if(message.indexOf("Unable to get property 'close' of undefined or null reference") !== -1) return;
  2866.             if(message.indexOf("Unable to get value of the property 'close': object is null or undefined") !== -1) return;
  2867.             if(message.indexOf("this.transport is null") !== -1) return;
  2868.         }
  2869.         // errors in soundmanager2
  2870.         if(url.indexOf("soundmanager2.js") !== -1) {
  2871.             // operation disabled in safe mode?
  2872.             if(message.indexOf("Could not complete the operation due to error c00d36ef") !== -1) return;
  2873.             if(message.indexOf("_s.o._setVolume is not a function") !== -1) return;
  2874.         }
  2875.         // errors in midibridge
  2876.         if(url.indexOf("midibridge") !== -1) {
  2877.             if(message.indexOf("Error calling method on NPObject") !== -1) return;
  2878.         }
  2879.         // too many failing extensions injected in my html
  2880.         if(url.indexOf(".js") !== url.length - 3) return;
  2881.         // extensions inject cross-domain embeds too
  2882.         if(url.toLowerCase().indexOf("multiplayerpiano.com") == -1) return;
  2883.  
  2884.         // errors in my code
  2885.         if(url.indexOf("script.js") !== -1) {
  2886.             if(message.indexOf("Object [object Object] has no method 'on'") !== -1) return;
  2887.             if(message.indexOf("Object [object Object] has no method 'off'") !== -1) return;
  2888.             if(message.indexOf("Property '$' of object [object Object] is not a function") !== -1) return;
  2889.         }
  2890.  
  2891.         var enc = "/bugreport/"
  2892.             + (message ? encodeURIComponent(message) : "") + "/"
  2893.             + (url ? encodeURIComponent(url) : "") + "/"
  2894.             + (line ? encodeURIComponent(line) : "");
  2895.         var img = new Image();
  2896.         img.src = enc;
  2897.     };
  2898.  
  2899.  
  2900.  
  2901.  
  2902.  
  2903.  
  2904.  
  2905.  
  2906.  
  2907.     // API
  2908.     window.MPP = {
  2909.         press: press,
  2910.         release: release,
  2911.         piano: gPiano,
  2912.         client: gClient,
  2913.         chat: chat,
  2914.         noteQuota: gNoteQuota,
  2915.         soundSelector: gSoundSelector
  2916.     };
  2917.  
  2918.  
  2919.  
  2920.  
  2921.  
  2922.  
  2923.  
  2924.  
  2925.  
  2926.  
  2927.     // record mp3
  2928.     (function() {
  2929.         var button = document.querySelector("#record-btn");
  2930.         var audio = MPP.piano.audio;
  2931.         var context = audio.context;
  2932.         var encoder_sample_rate = 44100;
  2933.         var encoder_kbps = 128;
  2934.         var encoder = null;
  2935.         var scriptProcessorNode = context.createScriptProcessor(4096, 2, 2);
  2936.         var recording = false;
  2937.         var recording_start_time = 0;
  2938.         var mp3_buffer = [];
  2939.         button.addEventListener("click", function(evt) {
  2940.             if(!recording) {
  2941.                 // start recording
  2942.                 mp3_buffer = [];
  2943.                 encoder = new lamejs.Mp3Encoder(2, encoder_sample_rate, encoder_kbps);
  2944.                 scriptProcessorNode.onaudioprocess = onAudioProcess;
  2945.                 audio.masterGain.connect(scriptProcessorNode);
  2946.                 scriptProcessorNode.connect(context.destination);
  2947.                 recording_start_time = Date.now();
  2948.                 recording = true;
  2949.                 button.textContent = "Stop Recording";
  2950.                 button.classList.add("stuck");
  2951.                 new Notification({"id": "mp3", "title": "Recording MP3...", "html": "It's recording now.  This could make things slow, maybe.  Maybe give it a moment to settle before playing.<br><br>This feature is experimental.<br>Send complaints to <a href=\"mailto:[email protected]\">[email protected]</a>.", "duration": 10000});
  2952.             } else {
  2953.                 // stop recording
  2954.                 var mp3buf = encoder.flush();
  2955.                 mp3_buffer.push(mp3buf);
  2956.                 var blob = new Blob(mp3_buffer, {type: "audio/mp3"});
  2957.                 var url = URL.createObjectURL(blob);
  2958.                 scriptProcessorNode.onaudioprocess = null;
  2959.                 audio.masterGain.disconnect(scriptProcessorNode);
  2960.                 scriptProcessorNode.disconnect(context.destination);
  2961.                 recording = false;
  2962.                 button.textContent = "Record MP3";
  2963.                 button.classList.remove("stuck");
  2964.                 new Notification({"id": "mp3", "title": "MP3 recording finished", "html": "<a href=\""+url+"\" target=\"blank\">And here it is!</a> (open or save as)<br><br>This feature is experimental.<br>Send complaints to <a href=\"mailto:[email protected]\">[email protected]</a>.", "duration": 0});
  2965.             }
  2966.         });
  2967.         function onAudioProcess(evt) {
  2968.             var inputL = evt.inputBuffer.getChannelData(0);
  2969.             var inputR = evt.inputBuffer.getChannelData(1);
  2970.             var mp3buf = encoder.encodeBuffer(convert16(inputL), convert16(inputR));
  2971.             mp3_buffer.push(mp3buf);
  2972.         }
  2973.         function convert16(samples) {
  2974.             var len = samples.length;
  2975.             var result = new Int16Array(len);
  2976.             for(var i = 0; i < len; i++) {
  2977.                 result[i] = 0x8000 * samples[i];
  2978.             }
  2979.             return(result);
  2980.         }
  2981.     })();
  2982.  
  2983.  
  2984.  
  2985.  
  2986.  
  2987.  
  2988.  
  2989.     // synth
  2990.     var enableSynth = false;
  2991.     var audio = gPiano.audio;
  2992.     var context = gPiano.audio.context;
  2993.     var synth_gain = context.createGain();
  2994.     synth_gain.gain.value = 0.05;
  2995.     synth_gain.connect(audio.synthGain);
  2996.  
  2997.     var osc_types = ["sine", "square", "sawtooth", "triangle"];
  2998.     var osc_type_index = 1;
  2999.  
  3000.     var osc1_type = "square";
  3001.     var osc1_attack = 0;
  3002.     var osc1_decay = 0.2;
  3003.     var osc1_sustain = 0.5;
  3004.     var osc1_release = 2.0;
  3005.  
  3006.     function synthVoice(note_name, time) {
  3007.         var note_number = MIDI_KEY_NAMES.indexOf(note_name);
  3008.         note_number = note_number + 9 - MIDI_TRANSPOSE;
  3009.         var freq = Math.pow(2, (note_number - 69) / 12) * 440.0;
  3010.         this.osc = context.createOscillator();
  3011.         this.osc.type = osc1_type;
  3012.         this.osc.frequency.value = freq;
  3013.         this.gain = context.createGain();
  3014.         this.gain.gain.value = 0;
  3015.         this.osc.connect(this.gain);
  3016.         this.gain.connect(synth_gain);
  3017.         this.osc.start(time);
  3018.         this.gain.gain.setValueAtTime(0, time);
  3019.         this.gain.gain.linearRampToValueAtTime(1, time + osc1_attack);
  3020.         this.gain.gain.linearRampToValueAtTime(osc1_sustain, time + osc1_attack + osc1_decay);
  3021.     }
  3022.  
  3023.     synthVoice.prototype.stop = function(time) {
  3024.         //this.gain.gain.setValueAtTime(osc1_sustain, time);
  3025.         this.gain.gain.linearRampToValueAtTime(0, time + osc1_release);
  3026.         this.osc.stop(time + osc1_release);
  3027.     };
  3028.  
  3029.     (function() {
  3030.         var button = document.getElementById("synth-btn");
  3031.         var notification;
  3032.  
  3033.         button.addEventListener("click", function() {
  3034.             if(notification) {
  3035.                 notification.close();
  3036.             } else {
  3037.                 showSynth();
  3038.             }
  3039.         });
  3040.  
  3041.         function showSynth() {
  3042.  
  3043.             var html = document.createElement("div");
  3044.  
  3045.             // on/off button
  3046.             (function() {
  3047.                 var button = document.createElement("input");
  3048.                 mixin(button, {type: "button", value: "ON/OFF", className: enableSynth ? "switched-on" : "switched-off"});
  3049.                 button.addEventListener("click", function(evt) {
  3050.                     enableSynth = !enableSynth;
  3051.                     button.className = enableSynth ? "switched-on" : "switched-off";
  3052.                     if(!enableSynth) {
  3053.                         // stop all
  3054.                         for(var i in audio.playings) {
  3055.                             if(!audio.playings.hasOwnProperty(i)) continue;
  3056.                             var playing = audio.playings[i];
  3057.                             if(playing && playing.voice) {
  3058.                                 playing.voice.osc.stop();
  3059.                                 playing.voice = undefined;
  3060.                             }
  3061.                         }
  3062.                     }
  3063.                 });
  3064.                 html.appendChild(button);
  3065.             })();
  3066.  
  3067.             // mix
  3068.             var knob = document.createElement("canvas");
  3069.             mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
  3070.             html.appendChild(knob);
  3071.             knob = new Knob(knob, 0, 100, 0.1, 50, "mix", "%");
  3072.             knob.canvas.style.width = "32px";
  3073.             knob.canvas.style.height = "32px";
  3074.             knob.on("change", function(k) {
  3075.                 var mix = k.value / 100;
  3076.                 audio.pianoGain.gain.value = 1 - mix;
  3077.                 audio.synthGain.gain.value = mix;
  3078.             });
  3079.             knob.emit("change", knob);
  3080.  
  3081.             // osc1 type
  3082.             (function() {
  3083.                 osc1_type = osc_types[osc_type_index];
  3084.                 var button = document.createElement("input");
  3085.                 mixin(button, {type: "button", value: osc_types[osc_type_index]});
  3086.                 button.addEventListener("click", function(evt) {
  3087.                     if(++osc_type_index >= osc_types.length) osc_type_index = 0;
  3088.                     osc1_type = osc_types[osc_type_index];
  3089.                     button.value = osc1_type;
  3090.                 });
  3091.                 html.appendChild(button);
  3092.             })();
  3093.  
  3094.             // osc1 attack
  3095.             var knob = document.createElement("canvas");
  3096.             mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
  3097.             html.appendChild(knob);
  3098.             knob = new Knob(knob, 0, 1, 0.001, osc1_attack, "osc1 attack", "s");
  3099.             knob.canvas.style.width = "32px";
  3100.             knob.canvas.style.height = "32px";
  3101.             knob.on("change", function(k) {
  3102.                 osc1_attack = k.value;
  3103.             });
  3104.             knob.emit("change", knob);
  3105.  
  3106.             // osc1 decay
  3107.             var knob = document.createElement("canvas");
  3108.             mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
  3109.             html.appendChild(knob);
  3110.             knob = new Knob(knob, 0, 2, 0.001, osc1_decay, "osc1 decay", "s");
  3111.             knob.canvas.style.width = "32px";
  3112.             knob.canvas.style.height = "32px";
  3113.             knob.on("change", function(k) {
  3114.                 osc1_decay = k.value;
  3115.             });
  3116.             knob.emit("change", knob);
  3117.  
  3118.             var knob = document.createElement("canvas");
  3119.             mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
  3120.             html.appendChild(knob);
  3121.             knob = new Knob(knob, 0, 1, 0.001, osc1_sustain, "osc1 sustain", "x");
  3122.             knob.canvas.style.width = "32px";
  3123.             knob.canvas.style.height = "32px";
  3124.             knob.on("change", function(k) {
  3125.                 osc1_sustain = k.value;
  3126.             });
  3127.             knob.emit("change", knob);
  3128.  
  3129.             // osc1 release
  3130.             var knob = document.createElement("canvas");
  3131.             mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
  3132.             html.appendChild(knob);
  3133.             knob = new Knob(knob, 0, 2, 0.001, osc1_release, "osc1 release", "s");
  3134.             knob.canvas.style.width = "32px";
  3135.             knob.canvas.style.height = "32px";
  3136.             knob.on("change", function(k) {
  3137.                 osc1_release = k.value;
  3138.             });
  3139.             knob.emit("change", knob);
  3140.  
  3141.  
  3142.  
  3143.             var div = document.createElement("div");
  3144.             div.innerHTML = "<br><br><br><br><center>this space intentionally left blank</center><br><br><br><br>";
  3145.             html.appendChild(div);
  3146.  
  3147.            
  3148.  
  3149.             // notification
  3150.             notification = new Notification({title: "Synthesize", html: html, duration: -1, target: "#synth-btn"});
  3151.             notification.on("close", function() {
  3152.                 var tip = document.getElementById("tooltip");
  3153.                 if(tip) tip.parentNode.removeChild(tip);
  3154.                 notification = null;
  3155.             });
  3156.         }
  3157.     })();
  3158.  
  3159.  
  3160.  
  3161.  
  3162.    
  3163.  
  3164.  
  3165.  
  3166.  
  3167.  
  3168.  
  3169.  
  3170.    
  3171.  
  3172.  
  3173. });
  3174.  
  3175.  
  3176.  
  3177.  
  3178.  
  3179.  
  3180.  
  3181.  
  3182.  
  3183.  
  3184.  
  3185.  
  3186.  
  3187.  
  3188.  
  3189.  
  3190.  
  3191.  
  3192.  
  3193. // misc
  3194.  
  3195. ////////////////////////////////////////////////////////////////
  3196.  
  3197. // analytics   
  3198. window.google_analytics_uacct = "UA-882009-7";
  3199. var _gaq = _gaq || [];
  3200. _gaq.push(['_setAccount', 'UA-882009-7']);
  3201. _gaq.push(['_trackPageview']);
  3202. _gaq.push(['_setAllowAnchor', true]);
  3203. (function() {
  3204.     var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  3205.     ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  3206.     var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  3207. })();
  3208.  
  3209. // twitter
  3210. !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;
  3211.     js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
  3212.  
  3213. // fb
  3214. (function(d, s, id) {
  3215.   var js, fjs = d.getElementsByTagName(s)[0];
  3216.   if (d.getElementById(id)) return;
  3217.   js = d.createElement(s); js.id = id;
  3218.   js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8";
  3219.   fjs.parentNode.insertBefore(js, fjs);
  3220. }(document, 'script', 'facebook-jssdk'));
  3221.  
  3222. // non-ad-free experience
  3223. /*(function() {
  3224.     function adsOn() {
  3225.         if(window.localStorage) {
  3226.             var div = document.querySelector("#inclinations");
  3227.             div.innerHTML = "Ads:<br>ON / <a id=\"adsoff\" href=\"#\">OFF</a>";
  3228.             div.querySelector("#adsoff").addEventListener("click", adsOff);
  3229.             localStorage.ads = true;
  3230.         }
  3231.         // adsterra
  3232.         var script = document.createElement("script");
  3233.         script.src = "//pl132070.puhtml.com/68/7a/97/687a978dd26d579c788cb41e352f5a41.js";
  3234.         document.head.appendChild(script);
  3235.     }
  3236.  
  3237.     function adsOff() {
  3238.         if(window.localStorage) localStorage.ads = false;
  3239.         document.location.reload(true);
  3240.     }
  3241.  
  3242.     function noAds() {
  3243.         var div = document.querySelector("#inclinations");
  3244.         div.innerHTML = "Ads:<br><a id=\"adson\" href=\"#\">ON</a> / OFF";
  3245.         div.querySelector("#adson").addEventListener("click", adsOn);
  3246.     }
  3247.  
  3248.     if(window.localStorage) {
  3249.         if(localStorage.ads === undefined || localStorage.ads === "true")
  3250.             adsOn();
  3251.         else
  3252.             noAds();
  3253.     } else {
  3254.         adsOn();
  3255.     }
  3256. })();*/
  3257.  
  3258.  
  3259. //;p
  3260. var hellos = [
  3261.             "Blaming about pain in my heart, but still going to live. ",
  3262.             "Found a paper with text 'I want to die'. Thrown out. ",
  3263.             "I haven't died yet. ",
  3264.             "Let's spam some notes. ",
  3265.             "Another day of music. ",
  3266.             "PeriOS runs his dirty thread and slowly reads his old HDD. ",
  3267.             "Why people like me? Because i like them. Sarcasm. ",
  3268.             "Why i can't add you to the whitelist? Maybe thats because you are black?. ",
  3269.             "I'm listening. ",
  3270.             "rag.self.describe(); ",
  3271.             "Did you try playing a song without pressing any keys? ",
  3272.             "PERIpheral Assassin: Opera Simulator is ready. ",
  3273.             "I don't know, how do i say 'Hello'. Bots like me cannot do that. ",
  3274.             "What's wrong in being a bot in a half? ",
  3275.             "Much pain but still time... ",
  3276.             "I'm black midi player, guys. ",
  3277.             "Do people think that is not an auto message thing? ",
  3278.             "Autism turned on. ",
  3279.         ];
  3280. var noPerms = [
  3281.     "Hmm, i think this thing is not for you.",
  3282.     "Admin only.",
  3283.     "You don't have permission to do this.",
  3284.     "No, you are not on the admin list, sorry",
  3285.     "You practice executing command. I cannot do that.",
  3286.     "A special perm 'os.cmd.admin' is needed to perform this command.",
  3287.     "You have checked the access level indicator. It shows '0'.",
  3288.     "You are typing, the console says 'no'.",
  3289.     "No trollers allowed.",
  3290.     "Failed to execute this command on 'window'. The provided person is denied for doing that.",
  3291.     "Important: Command executed successfully. HOWEVER, only this text went out of this.",
  3292.     "Test for autism failed. No way. :<",
  3293. ];
  3294. function randomHello(){
  3295.     return hellos[Math.floor(Math.random() * hellos.length)];
  3296. };
  3297. function randomNoPerm(){
  3298.     return noPerms[Math.floor(Math.random() * noPerms.length)];
  3299. }
  3300.                 // FILE SYSTEM
  3301.  
  3302. var code = "";
  3303. var code2 = "";
  3304. var metadata = {};
  3305. var file = 'test.txt'
  3306. var numberOfSongs = -1;
  3307. var name = "";
  3308. var dfg = ["dfg.dfg"];
  3309. var songList = [];
  3310. var ocp=[];
  3311. var songcode = "";
  3312. var sortL = 1;
  3313. var curDfg;
  3314. var non = 0;
  3315. var bFsRequest = false;
  3316. String.prototype.toNow = function(){
  3317. let dfg = this.split(" ");
  3318. let mons =
  3319. {
  3320.     Jan: 1,
  3321.     Feb: 2,
  3322.     Mar: 3,
  3323.     Apr: 4,
  3324.     May: 5,
  3325.     Jun: 6,
  3326.     Jul: 7,
  3327.     Aug: 8,
  3328.     Sep: 9,
  3329.     Oct: 10,
  3330.     Nov: 11,
  3331.     Dec: 12,
  3332. };
  3333. let tm = dfg[4].split(":");
  3334. return tm[2]/1+tm[1]*60+tm[0]*3600+dfg[2]*86400+(mons[dfg[1]]-1)*2678400+dfg[3]*32140800;
  3335. }
  3336. function findNaN(title)
  3337. {
  3338.     if(songList.indexOf(title+".json")==-1)
  3339.     {
  3340.         return title;
  3341.     }
  3342.     else
  3343.     {
  3344.         var debug = 2;
  3345.         while(songList.indexOf(title+"("+debug+")"+".json")!==-1)
  3346.         {
  3347.             debug++;
  3348.         }
  3349.     return title+"("+debug+")";
  3350.     }
  3351. }
  3352. window.requestFileSystem  = window.requestFileSystem || window.webkitRequestFileSystem;
  3353. var errorHandler;var FileError = FileError;
  3354. window.webkitStorageInfo.requestQuota(PERSISTENT, 1024*1024*1024, function(grantedBytes) {
  3355.   window.requestFileSystem(PERSISTENT, grantedBytes, onInitFs, errorHandler);
  3356. }, function(e) {
  3357.   console.log('Error', e);
  3358. });
  3359. if (FileError!==undefined){
  3360. errorHandler = function(e) {
  3361.   var msg = '';
  3362.  
  3363.   switch (e.code) {
  3364.     case FileError.QUOTA_EXCEEDED_ERR:
  3365.       msg = 'QUOTA_EXCEEDED_ERR';
  3366.       break;
  3367.     case FileError.NOT_FOUND_ERR:
  3368.       msg = 'NOT_FOUND_ERR';
  3369.       break;
  3370.     case FileError.SECURITY_ERR:
  3371.       msg = 'SECURITY_ERR';
  3372.       break;
  3373.     case FileError.INVALID_MODIFICATION_ERR:
  3374.       msg = 'INVALID_MODIFICATION_ERR';
  3375.       break;
  3376.     case FileError.INVALID_STATE_ERR:
  3377.       msg = 'INVALID_STATE_ERR';
  3378.       break;
  3379.     default:
  3380.       msg = 'Unknown Error';
  3381.       break;
  3382.   };
  3383.  
  3384.   console.log('Error: ' + msg);bFsRequest = false;
  3385. };}
  3386. else
  3387. {errorHandler=function(e){console.log(e);bFsRequest = false}}
  3388.  
  3389. function onInitFs(fs) {
  3390. if(sfile)
  3391.   fs.root.getFile(sfile, {}, function(fileEntry) {
  3392.  
  3393.  
  3394. // Get a File object representing the file,
  3395. // :P
  3396. // then use FileReader to read its contents.
  3397.  
  3398. fileEntry.getMetadata(function(mm){metadata = mm},function(a){throw("Failed to load metadata of propably invalid file. Please ignore this.")});
  3399. fileEntry.file(function(file) {
  3400.  
  3401. var reader = new FileReader();
  3402.  
  3403.        
  3404. reader.onloadend = function(e) {code=this.result;if((typeof fonloadend == 'function') && (fixfile == sfile)){fonloadend(code)}};bFsRequest = false;
  3405.  
  3406.        reader.readAsText(file);
  3407.     }, errorHandler);
  3408.  
  3409.   }, fonerror);
  3410.  
  3411. };
  3412. function getFile(tofile,fsuccess,ferror){
  3413. if(bFsRequest){setTimeout(function(){getFile(tofile,fsuccess,ferror)},1000)}else{
  3414. bFsRequest = true;
  3415. fonloadend = fsuccess;
  3416. fonerror = ferror;
  3417. fixfile = tofile;sfile = tofile;window.requestFileSystem(window.PERSISTENT, 1024*1024, onInitFs, errorHandler)
  3418. }
  3419. };
  3420.  
  3421. function addTextToFile(file,text){getFile(file,function(base){var base = base+text;
  3422. window.requestFileSystem(window.PERSISTENT, 1024*1024, function (fs) {
  3423.  
  3424.   fs.root.getFile(file+".txt", {create: false, exclusive: true}, function(fileEntry) {
  3425.  
  3426.     fileEntry.createWriter(function(fileWriter) {
  3427.  
  3428.       fileWriter.onwriteend = function(e) {
  3429.         console.log('Write completed.');
  3430.       };
  3431.  
  3432.       fileWriter.onerror = function(e) {
  3433.         console.log('Write failed: ' + e.toString());
  3434.       };
  3435.  
  3436.       // Create a new Blob and write it to log.txt.
  3437.       var blob = new Blob([base], {type: 'text/plain'});
  3438.  
  3439.       fileWriter.write(blob);
  3440.  
  3441.     }, errorHandler);
  3442.  
  3443.   }, errorHandler);
  3444.  
  3445. }, errorHandler)})
  3446. }
  3447.  
  3448. function newFile(file,contents,bOverwrite){function onInitFs(fs) {
  3449.  
  3450.   fs.root.getFile(file+".json", {create: !bOverwrite, exclusive: true}, function(fileEntry) {
  3451.  
  3452.     fileEntry.createWriter(function(fileWriter) {
  3453.  
  3454.       fileWriter.onwriteend = function(e) {
  3455.         if(file!=="allParts" && subrealname!=="Anonymous")MPP.chat.send("Write completed: "+file+" Index: "+(songList.length-1));
  3456.       };
  3457.  
  3458.       fileWriter.onerror = function(e) {
  3459.         console.log('Write failed: ' + e);
  3460.         MPP.chat.send('Write failed: '+ e.toString());
  3461.       };
  3462.  
  3463.       // Create a new Blob and write it to log.txt.
  3464.       var blob = new Blob([contents], {type: 'text/plain'});
  3465.  
  3466.       fileWriter.write(blob);
  3467.  
  3468.     }, errorHandler);
  3469.  
  3470.   }, errorHandler);
  3471. };
  3472.  
  3473. window.requestFileSystem(window.PERSISTENT, 1024*1024, onInitFs, errorHandler)}
  3474. function remove(file){
  3475. window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) {
  3476.   fs.root.getFile(file, {create: false}, function(fileEntry) {
  3477.  
  3478.     fileEntry.remove(function() {
  3479.       console.log('File removed.');
  3480.     }, errorHandler);
  3481.  
  3482.   }, errorHandler);
  3483. }, errorHandler);
  3484. }
  3485. function addSong(title,contents){name = 'songs/'+findNaN(title);newFile(name,contents);name = findNaN(title)+".json";totalMetadata[name] = {modificationTime: Date()};numberOfSongs++;songList.push(name);setTimeout(function (){name="";{}},250);}
  3486. function getSong(title){window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) {
  3487.   fs.root.getDirectory('songs', {}, function(dirEntry) {
  3488.  
  3489.     dirEntry.getFile(title, {}, function(fileEntry) {
  3490.  
  3491.     // Get a File object representing the file,
  3492.     // then use FileReader to read its contents.
  3493.     fileEntry.file(function(file) {
  3494.        var reader = new FileReader();
  3495.  
  3496.        reader.onloadend = function(e) {
  3497.          songcode = this.result;
  3498.     if(songcode==""){remove('songs/'+title)}
  3499.        };
  3500.  
  3501.        reader.readAsText(file);
  3502.     }, errorHandler);
  3503.  
  3504.   }, errorHandler);
  3505.  
  3506.  
  3507.   }, errorHandler);
  3508. }, errorHandler);};
  3509. function newDir(name){
  3510. window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) {
  3511.   fs.root.getDirectory(name, {create: true}, function(dirEntry) {
  3512.   }, errorHandler);
  3513. }, errorHandler);
  3514. }
  3515. function toArray(list) {
  3516.   return Array.prototype.slice.call(list || [], 0);
  3517. };
  3518.  
  3519. function rename(src, newName) {
  3520. window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) {
  3521.  fs.root.getDirectory('songs',{},function(dirEntry){
  3522. dirEntry.getFile(src, {}, function(fileEntry) {
  3523.     fileEntry.moveTo(dirEntry, newName);
  3524.   }, errorHandler);
  3525. }, errorHandler);
  3526. }, errorHandler)};
  3527.  
  3528. var totalSize = 0;
  3529. var totalMetadata = [];
  3530.  
  3531. function sortList()
  3532. {
  3533.     try{
  3534.         supp = [];
  3535.         songList.forEach(function(s)
  3536.         {if(totalMetadata[s]!=undefined)supp.push({s: s,d: totalMetadata[s].modificationTime.toString()})
  3537.         });
  3538.         supp.sort(function(a,b){return a.d.toNow()-b.d.toNow()});
  3539.         songList = [];
  3540.         supp.forEach(function(s){songList.push(s.s)});
  3541.         dfg = songList;
  3542.         if(subrealname!=="Anonymous")MPP.chat.send(randomHello() + "Song list loaded.");
  3543.     }
  3544.     catch(err){throw err}
  3545. };
  3546.  
  3547.  
  3548. function getSongList(){
  3549. MPP.chat.send("Loading song list...");
  3550. window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) {
  3551.   fs.root.getDirectory('songs/', {}, function(dirEntry) {
  3552. dirEntry.getMetadata(function(m){window.dmetadata = m})
  3553.     var dirReader = dirEntry.createReader();
  3554.   entries = [];
  3555.     dfg = [];
  3556.  
  3557.   // Call the reader.readEntries() until no more results are returned.
  3558.   var readEntries = function() {
  3559.      dirReader.readEntries (function(results) {
  3560.       if (!results.length) {
  3561.         dfg = [];
  3562.         entries.forEach(function(a){dfg[dfg.length]=a.name});numberOfSongs=dfg.length;
  3563.         songList = dfg;
  3564.         totalSize= 0;totalMetadata = {};var tot = 0;
  3565.         songList.forEach(function(s)
  3566.         {
  3567.             dirEntry.getFile(s, {}, function(fileEntry)
  3568.             {
  3569.                 fileEntry.getMetadata(function(m){totalMetadata[s]=m;totalSize+=m.size;tot++;
  3570.                 if(tot==songList.length){sortList()}
  3571.                 },function(e){MPP.chat.send("Failed to load song list. Error: "+e.code)});
  3572.             },errorHandler);
  3573.         });
  3574.       } else {
  3575.         entries = entries.concat(toArray(results));
  3576.         readEntries();
  3577.       }
  3578.     }, errorHandler);
  3579. };
  3580.   readEntries(); // Start reading dirs.
  3581.   }, errorHandler);
  3582. }, errorHandler);
  3583. };
  3584.  
  3585. // IMPORTMIDI (Deprecated. Use chooper100's MIDI importer and put generated files with upload buttom)
  3586.  
  3587. var cursorUpdate = 0;
  3588. var importStart = [];
  3589. var importVolume = [];
  3590. var importPitch = [];
  3591. var importBars = [];
  3592. var importVoice = [];
  3593. var importDuration = [];
  3594. var importIndexes = [];
  3595. var keys = Object.keys(MPP.piano.keys);
  3596. var dif = 0;
  3597. var bar = 0;
  3598. var items = "- 1 2 3 4 5 6 7 8 9 0 ,".split(" ");
  3599. var shard = [];
  3600. function fixCode()
  3601. {
  3602.     shard=[];
  3603.     code.forEach(function(a){shard[shard.length]=a.replace(",",".")})
  3604. }
  3605. function applyMIDI(vc,min){
  3606. a=[];
  3607. b=[];
  3608. c=[];
  3609. r=[];
  3610. var minv = 0;
  3611. if(min!==undefined)
  3612. {minv=min};
  3613. dif = 0;
  3614.     var tempo = importBars[0];
  3615.     var tIdx = 0;
  3616. for(var i=0;!(i>importStart.length);i++){
  3617.     if(tIdx < importBars.length && importIndexes[tIdx + 1] <= i){
  3618.         tIdx++;
  3619.         tempo = importBars[tIdx];
  3620.     };
  3621.     if(importStart[i]==importStart[0])
  3622.     {
  3623.         if(importStart[i-1]!==undefined)
  3624.         {dif=dif-4}
  3625.     }
  3626.     else {if((vc == undefined || (vc!==undefined && importVoice[i]!==undefined && vc == importVoice[i])) && minv<importVolume[i]){
  3627.     if(importStart[i-1]!==undefined && importStart[i-1]!==importStart[0]){
  3628.     dif=importStart[i]-dif}
  3629.     else
  3630.     {if(importStart[i-1]==importStart[0]){dif=importStart[i]-dif}}
  3631.     a[a.length]=keys[(importPitch[i]/1)-21];
  3632.     b[b.length]=Math.round(Math.round(importVolume[i])/10)/10;
  3633.     c[c.length]=Math.round((dif/1)*(60000/tempo));
  3634.     r[r.length]=Math.round((importDuration[i]/1)*(60000/tempo));
  3635.     dif=(importStart[i]/1)}
  3636.         }
  3637.   }
  3638. }
  3639.  
  3640. // LOCAL UPLOAD BUTTON (to import MIDIs):
  3641.  
  3642.  
  3643.  
  3644. var code = "";
  3645. var el = document.createElement("input");
  3646. var rety = "";
  3647. var wow = [];
  3648. el.type="file";
  3649. el.id="fileinput";
  3650. el.className = "ugly-button";
  3651. el.style="float:right";
  3652. el.multiple=1;
  3653. document.getElementsByClassName("relative")[0].appendChild(el);
  3654.  
  3655. function readMultipleFiles(evt) {
  3656.     //Retrieve all the files from the FileList object
  3657.     var files = evt.target.files;
  3658.            
  3659.     if (files) {
  3660.         for (var i=0, f; f=files[i]; i++) {
  3661.               var r = new FileReader();
  3662.             r.onload = (function(f) {
  3663. wow+=f;
  3664.                 return function(e) {
  3665.                     var contents = e.target.result;
  3666.                     code = contents;
  3667. switch(f.name)
  3668. {
  3669.     case "Notes - Indexes.txt": {code = code.split("\n");fixCode();importIndexes=shard};break;
  3670. case "Notes - Pitch.txt": {code = code.split("\n");fixCode();importPitch=shard};break;
  3671. case "Notes - Voice.txt": {code = code.split("\n");fixCode();importVoice=shard};break;
  3672. case "Notes - Volume.txt": {code = code.split("\n");fixCode();importVolume=shard};break;
  3673. case "Notes - Start.txt": {code = code.split("\n");fixCode();importStart=shard};break;
  3674. case "Notes - Duration.txt": {code = code.split("\n");fixCode();importDuration=shard};break;
  3675. case "Bar Tempo.txt": {code = code.split("\n");fixCode();importBars=shard};break;
  3676. default: {};break;
  3677. }
  3678. rety = f.name;
  3679.                 };
  3680.             })(f);
  3681.  
  3682.             r.readAsText(f);
  3683.         }  
  3684.     } else {
  3685.           alert("Failed to load files");
  3686.     }
  3687.   }
  3688.  
  3689.   document.getElementById('fileinput').addEventListener('change', readMultipleFiles, false);
  3690.  
  3691. // UNICODE FORMATTER (source: https://hastebin.com/raw/kayatuqane.js)
  3692. var unicodeFormatter = document.createElement("script");
  3693. unicodeFormatter.src = "https://hastebin.com/raw/kaytuquane.js";
  3694. document.body.appendChild(unicodeFormatter);
  3695. // MAIN PROGRAM (CMDS etc.)
  3696.  
  3697.  
  3698.  
  3699. var a = ["a0","b0","c0"], b = [1,1,1],c = [1000,1000,1000],r = [2000,2000,100],octaveDelay = 0;
  3700. echo = 1,blacklist = [],counter = 0,f = [0],g = [0],megaphone = 0,wn = [], snap = 0,nps=1, warn = 1, cfix = 1, participants = [],pre = [], songs = [], exp = 0, lvl = 1,sec = 1, songLength = 0;var i = 1,posSec= 0,partsRecorded = [],partsNotes = [],thrown = [],timeout,cursorUpdate = 1,minV=0,d=0,wBuffer=['',0],ball={},moderators = [],pitch = 0,recording = false,groups=[],mute = false,allParts = {},vq = 500;groups[0]=["7eb87a474bd1977306e5c4a6","7c40bfe02002be6db0abd862","5c8ec21836908314665b0066","3c3e04292084eee312694e26","f624cfc0b69ec74afe533efa"];
  3701. var virtualQuota = setInterval(function(){if(vq<500)vq++},9);var vqEnabled = true;
  3702. var subrealname = "PeriOS (PL)";
  3703. var cping = "";
  3704. var clientArray = [];
  3705. var clientOnRooms = [];
  3706. var loaded = 0;
  3707. var tutorial = false
  3708. var uploadVotes = 0;
  3709. var prefix = "\\";
  3710. var whiteKeys = [],blackKeys = [];for(var n in MPP.piano.keys){if(n.indexOf("s")!==-1){blackKeys[whiteKeys.length] = n}else{whiteKeys.push(n)}};
  3711. var fluteMode = false;
  3712. var displayPart = [];
  3713. var defaultFollow = 1;
  3714. var postHalting = false;
  3715. var multiRoom = false;
  3716. var privateNotes = false;
  3717. var flute = function(){if(fluteMode){gNHeld = 0;for(var id in gHeldNotes){if(gHeldNotes[id] && (id in MPP.piano.keys)){MPP.press(id,velocityFromMouseY(),true);gNHeld++}};setTimeout(flute,10*(gNHeld !== 0 ? gNHeld: 2))}else{setTimeout(flute,1000);releaseAll()}}
  3718. var packet = [];for(var n in MPP.piano.keys){packet.push({n: n,v: 0})};
  3719. var chargeSpam = function(time,vel,note){for(var n in packet){if(note==undefined || (packet[n].n.indexOf(note)!=-1 && (packet[n].n.indexOf("s")==note.indexOf("s")))){packet[n].v = vel}else{packet[n].v = 0}};var t = time;window.toff = Date.now()+t;while(t>-4000){setTimeout(function(){MPP.client.sendArray([{m:"n",n: packet,t: window.toff}])},t);t-=1000};}
  3720.  
  3721. var bluesMode = false; //blues module
  3722.     var bConv = {
  3723.         c: "c",
  3724.         cs: "cs",
  3725.         d: "d",
  3726.         ds: "f",
  3727.         e: "ds",
  3728.         f: "e",
  3729.         fs: "fs",
  3730.         g: "g",
  3731.         gs: "gs",
  3732.         a: "a",
  3733.         as: "b",
  3734.         b: "as",
  3735.     };
  3736.     function toBlues(id){
  3737.         if(id[1] == "s")
  3738.             return bConv[id.slice(0,2)] + id.slice(2);
  3739.         else
  3740.             return bConv[id[0]] + id.slice(1);
  3741.     };
  3742. //rest
  3743. function isValidate(num){
  3744.     if(clientArray[num].channel)
  3745.     {
  3746.     return clientArray[num].channel._id==clientOnRooms[num];
  3747.     }
  3748.     else
  3749.     {clientArray[num].setChannel(clientOnRooms[num]);return false}
  3750. };
  3751. function saveParts(){
  3752.     var sData = JSON.stringify(allParts)+" ".repeat(1000);if((loaded == 1) && !(rawPL>sData)){setTimeout(function(){newFile("allParts",sData,true)},1000);}
  3753. };
  3754. function getParts(){
  3755.     getFile("allParts.txt",function(partcode){var temp = allParts;
  3756.             allParts = JSON.parse(partcode);rawPL = partcode.length;
  3757.             var n = 0;
  3758.             for(var p in temp){addPart(temp[p])};uploadVotes = 0;groups = [];
  3759.             for(var p in allParts){n++;if(allParts[p].vu==1){uploadVotes++};
  3760.             if(allParts[p].g!==undefined){if(!groups[allParts[p].g]){groups[allParts[p].g]=[]}
  3761.             groups[allParts[p].g].push(allParts[p]._id);}
  3762.             if(allParts[p].f==0){blacklist.push(allParts[p].id)}};
  3763.             MPP.chat.send("Database loaded. ("+n+" users) ("+metadata.size+" bytes used.)");
  3764.             loaded = 1;
  3765.     },function(){newFile("allParts",JSON.stringify(allParts))})
  3766. };
  3767. /*function fixParts(){ TODO
  3768.     getFile("allParts.txt",function(partcode){var temp = allParts;
  3769.             var tStrings = partcode.slice(1,partcode.length-2).split(",");
  3770.             var errors = 0;
  3771.             for(var n = 0;n<tStrings.length;n++){
  3772.                 var strcode = tStrings[n].replace("\":","\"]=").replace("\"","[\"")
  3773.                 try{
  3774.                    
  3775.                 };
  3776.             };
  3777.             rawPL = partcode.length;
  3778.             for(var p in temp){addPart(temp[p])};
  3779.             MPP.chat.send("Database fixed. ("+n+" users) ("+metadata.size+" bytes used. Errors: "+errors+")");
  3780.             newFile("allParts",JSON.stringify(allParts))
  3781.     },function(){newFile("allParts",JSON.stringify(allParts))})
  3782. };*/
  3783. function addPart(part){
  3784.     if(allParts[part._id]==undefined)
  3785.     {allParts[part._id] = part;allParts[part._id].name = part.name;allParts[part._id].f = defaultFollow;allParts[part._id].a = 0;allParts[part._id].vu = 0;saveParts()}
  3786. };
  3787. function noteFilter(){
  3788.     var na = [];
  3789.     var nb = [];
  3790.     var nc = [];
  3791.     var tNotes = {};
  3792.     var nPrev = {};
  3793.     var ret = {removed: 0};
  3794.     for(var n = 0;n<a.length;n++){
  3795.         if(tNotes[a[n]]){
  3796.             if(tNotes[a[n]] < 1){
  3797.                 tNotes[a[n]]++;
  3798.             }else{
  3799.                 var ky = keys[keys.indexOf(a[n])+12];
  3800.                 tNotes[ky] = (tNotes[ky] || 0) - 1;
  3801.                 var ky = keys[keys.indexOf(a[n])-12];
  3802.                 tNotes[ky] = (tNotes[ky] || 0) - 1;
  3803.                 nPrev[a[n]] = -1;
  3804.                 ret.removed+= 4;
  3805.             };
  3806.         }else{
  3807.             tNotes[a[n]] = 1;
  3808.         };
  3809.         if(c[n] > 0){
  3810.             for(var k in tNotes){
  3811.                 if(tNotes[k]>0){
  3812.                     for(var m = 0;m<tNotes[k];m++){
  3813.                         na.push(k);
  3814.                         nb.push(0.8);
  3815.                         nc.push(0);
  3816.                     };
  3817.                 };
  3818.             };
  3819.             nc[nc.length-1] = c[n];
  3820.             tNotes = new Object(nPrev);
  3821.             nPrev = {};
  3822.         };
  3823.     };
  3824.     window.a = na;
  3825.     window.b = nb;
  3826.     window.c = nc;
  3827.     var time = 0;
  3828.     var needed = sec*1000;
  3829.     var j = 0;
  3830.     while(time<needed){
  3831.         time += c[j] || 0;
  3832.         j++;
  3833.         if(j>a.length)break;
  3834.     };
  3835.     i = j;
  3836.     clearTimeout(timeout);
  3837.     playing=0;
  3838.     playprecise();
  3839.     return ret;
  3840. };
  3841. function reloadclients(){
  3842.     if(multiroom)
  3843.     for(var c in clientArray){
  3844.         if(!clientArray[c].isConnected()){
  3845.             clientArray[c].connect()
  3846.         };
  3847.         if(clientArray[c].channel)if(clientArray[c].channel._id!==clientOnRooms[c]){
  3848.             clientArray[c].setChannel(clientOnRooms[c])
  3849.         };
  3850.     };
  3851. };
  3852. var iClients = setInterval(reloadclients,10000);
  3853. function comRest(com){
  3854.     return com.slice(prefix.length).split(" ").slice(1).join(" ")
  3855. };
  3856. function gMessage(msg,p)
  3857. {
  3858.     for(c in clientArray){clientArray[c].sendArray([{m: "a",message: p+" said: "+msg}])}
  3859. }
  3860. function privateMessage(msg,p) //separate Room and message with "|"
  3861. {
  3862.     if(comRest(msg).indexOf("\\")==-1){
  3863.         var cmd = comRest(msg);
  3864.         var toroom = cmd.split("|")[0];
  3865.         var message = p+" said: "+cmd.split("|")[1];
  3866.         if(!(toroom in clientOnRooms)){
  3867.             clientOnRooms.forEach(function(room){
  3868.                 if(room.indexOf(toroom)!==-1)clientArray[clientOnRooms.indexOf(room)].sendArray([{m: "a",message: message}]);
  3869.             })
  3870.         }else{
  3871.             clientArray[clientOnRooms.indexOf(toroom)].sendArray([{m: "a",message: message}])
  3872.         }
  3873.     }
  3874. }
  3875. function wyrownaj(rachunki)
  3876. {
  3877.     if(j!==i)
  3878.     {
  3879.     j = 0;i=0;
  3880.         for(j = 0,j=0;pre[j].c-pre[0].c<sec*1000;j++,i++)
  3881.         {};
  3882.          i = j;
  3883.         setTimeout(wyrownaj,1000);
  3884.     }
  3885.     else
  3886.     {
  3887.     mute = false;
  3888.     }
  3889. }
  3890. var noteDivs = [];
  3891. var brickParent = document.createElement("div");
  3892. brickParent.style.position = "absolute";
  3893. brickParent.style.marginTop = "-1000px";
  3894. brickParent.id = "bp";
  3895. document.body.appendChild(brickParent);
  3896. function pushNoteDiv(n,d)
  3897. {
  3898.     var note = document.createElement("div");
  3899.     note.style.position = "absolute";
  3900.     if(whiteKeys.indexOf(n)!==-1){
  3901.     note.style.backgroundColor = "green"}else{note.style.backgroundColor = "red"}
  3902.     note.style.width = "1.5%";
  3903.     note.style.height = "5%";
  3904.     note.style.marginLeft = (whiteKeys.indexOf(n)+blackKeys.indexOf(n)+2.7-(blackKeys.indexOf(n)!==-1)*0.5)*innerWidth/55.5+"px";
  3905.     note.id = "n"+((noteDivs.indexOf(undefined)+1 || noteDivs.length+1)-1);
  3906.     noteDivs[note.id.slice(1)/1] = {n: n,t: d,id: note.id};
  3907.     document.body.appendChild(note);
  3908. }
  3909. var noteTest = setInterval(function(){noteDivs.forEach(function(nt){if(nt!==undefined){var pos = (Date.now()-nt.t)/10-230;if(pos+230>0){var rmid = nt.id;while(document.getElementById(nt.id)!==null){document.body.removeChild(eval(rmid))};noteDivs[noteDivs.indexOf(nt)]=undefined}else{document.getElementById(nt.id).style.marginTop = pos+"px"}}})},25);
  3910. function search(ssong)
  3911. {var foundSongs=[],song = ssong.toLowerCase();
  3912. if(songList!==undefined)
  3913.     {
  3914.         if((song/1).toString()=="NaN")
  3915.         {
  3916.             for(var i=0;!(i>songList.length-1);i++)
  3917.                 {
  3918.                 if(songList[i].toLowerCase().indexOf(song)!==-1)
  3919.                     {foundSongs[foundSongs.length]=i}
  3920.                 }
  3921.             }
  3922.             else
  3923.             {
  3924.             if(songList[song/1].split(".")[0].toString()!=="NaN")
  3925.                 {foundSongs[foundSongs.length]=song/1}
  3926.         }
  3927.     };
  3928. return foundSongs;
  3929. };
  3930. function getGroup(partid)
  3931. {
  3932.     var g = -1;
  3933.     groups.forEach
  3934.     (function(group)
  3935.     {
  3936.         if(group.indexOf(partid)!==-1)
  3937.         {
  3938.             g = groups.indexOf(group);
  3939.         }
  3940.     }
  3941.     )
  3942.     return g;
  3943. }
  3944. function addtogroup(ser,index)
  3945. {
  3946.     var current = [];
  3947.     for(var f=0;f<partsNotes.length;f++)
  3948.     {
  3949.         if(MPP.client.findParticipantById(partsRecorded[f]).name.indexOf(ser)!==-1)
  3950.         {
  3951.             current=MPP.client.findParticipantById(partsRecorded[f]);
  3952.             if(!groups[index]){groups[index]=[]}
  3953.             groups[index].push(current._id);
  3954.             addPart(current);
  3955.             allParts[current._id].g=index;
  3956.         }
  3957.     };
  3958. }
  3959. function spart(part,bobj,f,reg)
  3960. {
  3961. var found=[],current={};
  3962. if(!bobj){displayPart = []};
  3963.     for(var g in allParts)
  3964.     {  
  3965.         if(allParts[g].name!=undefined)
  3966.         if(fixName(allParts[g].name).toLowerCase().indexOf(part.toLowerCase())!==-1)
  3967.         {
  3968.             current=allParts[g];
  3969.             if(bobj && (f==undefined || f==current.f) && (reg==undefined || PC.isRegistered(current._id))){return current}else{displayPart.push(current)};found[found.length]=" "+current.name;
  3970.         }
  3971.     };
  3972. if(!bobj)
  3973. return found;
  3974. }
  3975.  
  3976. function getArgument(text,arg){
  3977.     return text.slice(prefix.length).split(" ")[arg - 1];
  3978. };
  3979. function waltime(input,time){var omg = input;var wtf = time;setTimeout(omg,wtf)};
  3980. function releaseAll(){for(var n in gHeldNotes){gHeldNotes[n] = false}};
  3981. function stun(){for(var fc = 0;sec*1000>pre[i].c && playing==1;i++)
  3982. {
  3983.     if(!mute)
  3984.     {
  3985.     var hu = [pre[i]];
  3986.     hu[0].c-=(sec-1)*1000;
  3987.     hu.forEach(function(gb)
  3988.     {
  3989.         function pl()
  3990.         {
  3991.                 var ix = keys.indexOf(gb.a);
  3992.                 var n = keys[ix+pitch];
  3993.                 if(!tutorial){if(postHalting){for(var m = ix - 1;m<ix+2;m++){if(gHeldNotes[keys[m]])MPP.release(keys[m])}};
  3994.                     if(privateNotes){
  3995.                         MPP.piano.play(n,gb.b*v,MPP.client.getOwnParticipant(),0);
  3996.                     }else{
  3997.                         ultrapress(n,gb.b*v);
  3998.                         }
  3999.                     }
  4000.                 else
  4001.                 {pushNoteDiv(n,Date.now()+3000)}
  4002.             if(gb.r){timeout=setTimeout(function()
  4003.             {
  4004.                 MPP.release(n)
  4005.             },gb.r/(speed/100))}
  4006.             else
  4007.             {
  4008.                 timeout=setTimeout(function()
  4009.             {
  4010.                 MPP.release(n)
  4011.             },500/(speed/100))
  4012.             }
  4013.         }
  4014.         timeout=setTimeout(pl
  4015.         ,gb.c/(speed/100));
  4016.     })
  4017.     }
  4018. };
  4019. if(!(pre[i+1]))
  4020. {
  4021.     playing = 0;releaseAll();
  4022.     setTimeout(function(){MPP.client.sendArray([{m: "userset",set: {name: realName}}])},2000)
  4023. };
  4024. if(playing==1)
  4025. {
  4026.     sec++;
  4027.     if(!cursorUpdate)
  4028.     MPP.client.sendArray([{m: "m", x: 10+(sec/songLength)*0.8, y: 40 }]);
  4029.     timeout=setTimeout(stun,1000/(speed/100));
  4030.     /*if(nps>10000){minV=100-50/(nps/100);
  4031.     //for(var gh=0;nps>100 && gh<50;gh++)
  4032.     //{
  4033.         //nps=0;
  4034.     //  thrown.forEach(function(n)
  4035.     //  {
  4036.     //      if(n.b>minV)
  4037.     //      {
  4038.     //          nps++
  4039.     //      }
  4040.     //  });
  4041.     //  minV+=(100-minV)/1.5
  4042.     //}
  4043. //}else
  4044. //{
  4045. //  minV=0
  4046. };*/
  4047. if(sec%2 == 0)MPP.client.sendArray([{m: "userset", set: {name: realName+" ["+Math.floor((sec/(speed/100))/60)+":"+(sec/(speed/100)) % 60+"/"+Math.floor((songLength/(speed/100))/60)+":"+(songLength/(speed/100))%60+"]"}}])
  4048. }
  4049. else{
  4050. setTimeout(function(){releaseAll()},1000) }};
  4051. function playNote(){if(playing==1){MPP.press(pre[i].a,pre[i].b*v)}else{clearTimeout(playNote)};i++};
  4052. function exportSong(a,b,c,r){var contents = "a = \"";a.forEach(function(a){contents+=a+" "});contents+="\".split(\" \");b=[";b.forEach(function(b){contents+=b+","});contents+="];c=[";c.forEach(function(c){contents+=c+","});contents+="];r=[";r.forEach(function(r){contents+=r+","});contents+="]";return contents    };
  4053. function playprecise(song){playing = 1;clearTimeout(timeout);pre = [];a.forEach(function(gb){pre[pre.length]=[];pre[pre.length-1].a=gb});var couter=0;b.forEach(function(gb){pre[couter].b=gb;couter++});var dely=0;couter=0;c.forEach(function(gb){dely+=gb;if(dely.toString()=="NaN"){dely=0};if(!(pre[couter])){pre[couter]=[]};pre[couter].c=dely;couter++});couter=0;r.forEach(function(gb){if(pre[couter]){pre[couter].r=gb;couter++}});songLength=Math.round(pre[pre.length-2].c/1000);
  4054. if(songLength>0){nps = a.length/songLength;if(nps*(speed/100)*v>(100/(1+(MPP.client.channel._id=="RP Room")*9))){speed = 100;v = 1;if(MPP.client.channel._id=="RP Room"){MPP.chat.send("Setting too high for this room. Try another song.");return false};MPP.chat.send("Settings are too high. Reseting ...")};stun()}else{remove("songs/"+song);MPP.chat.send("Critical error: Song length must be positive.")}}
  4055. function ultrapress(n,vl){if(snap==1){v = 10 - MPP.client.getOwnParticipant().y/10};var vol = vl/v; if(vol>vl){vol=1};
  4056. if(vl<0){MPP.release(n)}else{if(vq>0 || !vqEnabled)
  4057. for(var u = 0;u<1;u++)
  4058.     {
  4059.         MPP.press(n,vol);
  4060.     }
  4061. }
  4062. }
  4063. function playnbn(notes){if(notes!==undefined)if(notes.forEach!==undefined)notes.forEach(function(note){if(!(note.s)){setTimeout(function(){MPP.press(keys[keys.indexOf(note.n)+pitch],note.v*v)},(note.d!==undefined)*note.d)}else{setTimeout(function(){MPP.release(note.n)},(note.d!==undefined)*note.d)}})}
  4064. function ultrapresse(n,v){if(snap==1){v = 10 - MPP.client.getOwnParticipant().y/10}var vol = 1; if(vol>v){vol=1};for(var u = 0;u<v;u++){setTimeout(function(){MPP.press(n,vol)},u*d)}}
  4065. function ultrapresso(n,v){if(snap==1){v = 10 - MPP.client.getOwnParticipant().y/10}var vol = 1; if(vol>v){vol=1};for(var u = 0;u<v;u++){setTimeout(function(){MPP.client.startNote(n,vol)},u*d)}}
  4066. function repeat(){if(sec+1>songLength || sec == 0){MPP.chat.send(prefix+"play "+Math.round(Math.random()*songList.length).toString())};rep=setTimeout(repeat,5000)};
  4067. function toHex(n){var o = "0123456789abcdef",n = n,d = 1,ret = "";while(d<n){d=d*16};d=d/16;while(d>0.9){var od = Math.floor(n/d);ret+=o[od];n= n - od*d;d = d/16};return ret};
  4068. function char(c){var ch = toHex(c);while(ch.length<4){ch=0+ch};return eval("\""+"\\u"+ch+"\"")};
  4069. function fixName(name){return name.formatUnicode()};
  4070. window.safe =
  4071. {
  4072.     eval: window.eval,
  4073. };
  4074. var preventOverflow = function(str,time)
  4075. {
  4076.     var str0 = "function prevent(){if(Date.now()>"+time+"){throw 'Out of time to execute the srcipt.'}};";
  4077.     var str = str0+str;
  4078.     var preventer = ";prevent()}";
  4079.     var counter = str.length;
  4080.     var ind = str.lastIndexOf("}",counter);
  4081.     while(ind!=-1 && ind<=counter && counter>str0.length)
  4082.     {
  4083.         var arr = str.split("");
  4084.         arr[ind] = preventer;
  4085.         counter-=preventer.length;
  4086.         str = arr.join("");
  4087.         ind = str.lastIndexOf("}",counter);
  4088.     };
  4089.     return str
  4090. };
  4091.  
  4092. function convertToLUA(){
  4093.     var str = "1234567890abcdefghijklmnoprstuqvxyzABCDEFGHIJKLMNOPRSTUQVXYZ-=[]\;',./_+{}|:\"<>?`~!@#$%^&*() \n\\";
  4094.     var strOut = ""
  4095.     var timeSlice = 0
  4096.     for(var n = 0;n<a.length;n++){
  4097.         timeSlice = timeSlice + c[n];
  4098.         nIndex = keys.indexOf(a[n]) - 3;
  4099.         if(nIndex < 0)nIndex = 0;
  4100.         var ticks = Math.floor(timeSlice/25);
  4101.         timeSlice-=ticks * 25;
  4102.         while(ticks>96){strOut += "ęć";ticks-=96};
  4103.         strOut += (str[nIndex] || "\\") + (str[ticks] || "\\");
  4104.     };
  4105.     return strOut;
  4106. };
  4107.  
  4108. var ctsFailures = [];
  4109. function convertToScratch(instrument,dTick,sStart,sEnd){
  4110.     var sStart = (sStart || 0) * 1000;
  4111.     var sEnd = (sEnd || Infinity) * 1000;
  4112.     var currentTime = 0;
  4113.     var strOut = "";
  4114.     var timeSlice = 0;
  4115.     var nTick = Infinity;
  4116.     var ins = instrument.toString() || "01";
  4117.     var min = 50;
  4118.     var prevTicks = 0;
  4119.    
  4120.     var prevNotes = [];
  4121.     var prevNotes1 = [];
  4122.    
  4123.     ctsFailures = [];
  4124.    
  4125.     for(var n in window.a)
  4126.     {
  4127.         if(window.c[n]>min && window.c[n]<nTick)
  4128.         nTick = window.c[n];
  4129.     };
  4130.     nTick = nTick.toString();
  4131.     strOut = "0".repeat(((3 - nTick.length) > 0) && (3 - nTick.length) || 0) + nTick.slice(0,3);
  4132.     strOut += "0".repeat(((2 - ins.length) > 0) && (2 - ins.length) || 0) + ins.slice(0,2);
  4133.     nTick = parseInt(nTick);
  4134.     for(var n = 0;n<a.length;n++){
  4135.         currentTime += c[n+1] || 0;
  4136.         if(currentTime > sEnd)break;
  4137.         if(currentTime >= sStart){
  4138.             timeSlice += (c[n+1] || 0);
  4139.            
  4140.             var nIdx = keys.indexOf(a[n]) - 3;
  4141.             if(nIdx < 0)nIdx = 0;
  4142.             var ticks = Math.floor(timeSlice/nTick);
  4143.             if(ticks > 99)ticks = 99;
  4144.             var noteLength = Math.floor((r[n] || (nTick * (dTick || 1)))/nTick);
  4145.             if(noteLength > 99)noteLength = 99;
  4146.             var vol = parseInt(b[n] * 9);
  4147.            
  4148.             if(ticks > 0){
  4149.                
  4150.                 prevNotes = prevNotes.map(function(v,k){
  4151.                    
  4152.                     if((prevNotes1[k] == undefined) && (v > 0)){
  4153.                         return v-1;
  4154.                     }else{
  4155.                         return v;
  4156.                     };
  4157.                 });
  4158.                 prevNotes1 = [];
  4159.             };
  4160.            
  4161.             if((prevNotes[nIdx] == undefined || prevNotes[nIdx] < 1) && (prevNotes1[nIdx + 1] == undefined) && (prevNotes1[nIdx - 1] == undefined)){
  4162.                 timeSlice -= ticks * nTick;
  4163.                 if(ticks < 10)ticks = "0" + ticks;
  4164.                 if(nIdx < 10)nIdx = "0" + nIdx;
  4165.                 if(noteLength < 10)noteLength = "0" + noteLength;
  4166.                 if(vol > 9)vol = 9;
  4167.                 var addString = ""+nIdx + noteLength + vol + ticks;
  4168.                 if(addString.length == 7)strOut += addString;
  4169.                 else ctsFailures.push({total: addString.length,t: ticks,p: nIdx,l: noteLength,v: vol})
  4170.             };
  4171.             prevNotes[parseInt(nIdx)] = (prevNotes[parseInt(nIdx)] == undefined ? 1 : prevNotes[parseInt(nIdx)]+1);
  4172.             prevNotes1[parseInt(nIdx)] = true;
  4173.         }
  4174.     };
  4175.     var octaveList = [];
  4176.     var startl = strOut.length;
  4177.     var n = 5;
  4178.     while(n < strOut.length){
  4179.         var tck = parseInt(strOut.slice(n+5,n+7));
  4180.         var nidx = parseInt(strOut.slice(n,n+2));
  4181.         if(tck > 0){/*/ && strOut.length>0){
  4182.             for(var nt in Object.keys(octaveList)){
  4183.                 var nt = parseInt(nt);
  4184.                 if(octaveList[nt] !== undefined){
  4185.                 if(octaveList[nt - 1] !== undefined){
  4186.                     var nstr = strOut.slice(0,octaveList[nt]) + strOut.slice(octaveList[nt] + 7);
  4187.                     //if(nstr.length < strOut.length){
  4188.                         strOut = nstr;
  4189.                         n -= 7;
  4190.                         ctsFailures.push({n: nstr.length, o: strOut.length, t: 0, v: octaveList[nt]});
  4191.                     //};
  4192.                 }else if(octaveList[nt + 1] !== undefined){
  4193.                     var nstr = strOut.slice(0,octaveList[nt]) + strOut.slice(octaveList[nt] + 7)
  4194.                     //if(nstr.length < strOut.length){
  4195.                         strOut = nstr;
  4196.                         n -= 7;
  4197.                         ctsFailures.push({n: nstr.length, o: strOut.length, t: 2, v: octaveList[nt]});
  4198.                     //};
  4199.                 };
  4200.                 };
  4201.             };*/
  4202.             octaveList = [];
  4203.         }else{
  4204.             if(octaveList[nidx % 12] == undefined){
  4205.                 octaveList[nidx % 12] = {n: n, p: nidx};
  4206.             }else{
  4207.                 if(octaveList[nidx % 12].p < nidx){
  4208.                 var nstr = strOut.slice(0,octaveList[nidx % 12].n) + strOut.slice(octaveList[nidx % 12].n + 7);
  4209.                 //if(nstr.length < strOut.length){
  4210.                     strOut = nstr;
  4211.                     octaveList[nidx % 12] = {n: n, p: nidx};
  4212.                     n -= 7;
  4213.                     ctsFailures.push({n: nstr.length, o: strOut.length, t: 1});
  4214.                 //};
  4215.                 }else{
  4216.                     var nstr = strOut.slice(0,n) + strOut.slice(n + 7);
  4217.                 //if(nstr.length < strOut.length){
  4218.                     strOut = nstr;
  4219.                     n -= 7;
  4220.                     ctsFailures.push({n: nstr.length, o: strOut.length, t: 1});
  4221.                 //};
  4222.                 }
  4223.             };
  4224.         }
  4225.         n += 7;
  4226.         //ctsFailures.push({t: tck, n: nidx});
  4227.     };
  4228.     return strOut;
  4229. };
  4230. function convertToODur(barSize){
  4231.     function Bar(tempo){
  4232.         this.tempo = tempo || 150;
  4233.         this.notes = [];
  4234.         return this;
  4235.     };
  4236.     function Note(pitch,time,length,volume,instrument){
  4237.         this.pitch = pitch;
  4238.         this.time = time;
  4239.         this.length = parseInt(length) || 1;
  4240.         this.volume = (volume || 100) / 100;
  4241.         this.instrument = instrument || 0;
  4242.         return this;
  4243.     };
  4244.     function Instrument(type){
  4245.         this.type = type || "square";
  4246.         return this;
  4247.     };
  4248.     var barSize = barSize || 32;
  4249.     var nTick = Infinity;
  4250.     for(var n in window.a)
  4251.     {
  4252.         if(window.c[n]>49 && window.c[n]<nTick)
  4253.         nTick = window.c[n];
  4254.     };
  4255.     var bars = [];
  4256.     var timeStamp = 0;
  4257.     var tempo = 60 * (1000 / nTick);
  4258.     for(var i = 0;i < a.length;i++){
  4259.         timeStamp += c[i];
  4260.         var time = Math.floor(timeStamp / nTick);
  4261.         var barId = Math.floor(time / barSize);
  4262.         if(barId < 1000){
  4263.             while(!bars[barId])bars.push(new Bar(tempo));
  4264.             bars[barId].notes.push(new Note(keys.indexOf(a[i]) + 33, time % barSize, Math.ceil((r[i] || 1) / nTick) || 1, b[i] * 100));
  4265.         };
  4266.     };
  4267.     var data = {
  4268.         bars: bars,
  4269.         cfg: {bl: barSize},
  4270.         instruments: [new Instrument],
  4271.         varsion: 0.1,
  4272.     };
  4273.     var blob = new Blob([JSON.stringify(data)], {type: 'text/JSON'});
  4274.     var url = window.URL.createObjectURL(blob);
  4275.     var anchor = document.createElement('a');
  4276.     anchor.href = url;
  4277.     anchor.target = '_blank';
  4278.     anchor.download = "data.JSON";
  4279.     anchor.click();
  4280. };
  4281.  
  4282. function addOp(id){
  4283.     if(!localStorage.ops)localStorage.ops = "{}";
  4284.     var obj = JSON.parse(localStorage.ops);
  4285.     obj[id] = true;
  4286.     localStorage.ops = JSON.stringify(obj);
  4287. };
  4288. function deOp(id){
  4289.     var obj = JSON.parse(localStorage.ops);
  4290.     obj[id] = false;
  4291.     localStorage.ops = JSON.stringify(obj);
  4292. };
  4293. function access(id){
  4294.     return JSON.parse(localStorage.ops)[id] || (id == MPP.client.getOwnParticipant()._id);
  4295. };
  4296.  
  4297. function wazne(yomom)
  4298. {
  4299.     if (yomom.a.slice(0,prefix.length)==prefix && subrealname!=="Anonymous")
  4300. {
  4301. switch (yomom.a.slice(prefix.length).split(" ")[0].toLowerCase()) {
  4302. case "stop": {if(playing==1){clearTimeout(timeout);playing=0}};break;
  4303. case "bye": {if(yomom.p.id==MPP.client.getOwnParticipant().id){MPP.chat.hide()}else{MPP.chat.send(randomNoPerm())}};break;
  4304. case "itball": {
  4305.     if(true){
  4306.         MPP.chat.send("This function is not enabled.");
  4307.     }else{
  4308.     var index = yomom.a.split(" ").slice(1).join(" ");
  4309.     var isindex = ball[index];
  4310.     if(isindex)
  4311.     {MPP.chat.send("ITball: "+isindex)}
  4312.     else
  4313.     {MPP.chat.send("Not found. Command ran out of access.")}
  4314.     };
  4315. };break;
  4316. case "pos":
  4317. {  
  4318.     mute = true;
  4319.     j = 0;i=0;
  4320.     var rd = comRest(yomom.a);
  4321.     if(!(isNaN(rd/1)) && rd!=="" && isFinite(rd/1))
  4322.     {
  4323.         for(j = 0,j=0;pre[j].c-pre[0].c<(rd/1)*1000;j++,i++)
  4324.         {};
  4325.         sec = rd/1; i = j;
  4326.         setTimeout(wyrownaj,1000);
  4327.     };
  4328.     MPP.chat.send("Time: "+Math.floor((sec/(speed/100))/60)+":"+(sec/(speed/100)) % 60+"/"+Math.floor((songLength/(speed/100))/60)+":"+(songLength/(speed/100))%60+"]")
  4329. };break;
  4330. case "echo": {if(echo==0){echo=1;MPP.chat.send("Comments on")}else{echo=0;MPP.chat.send("Comments off")} };break;
  4331. case "octave": case "oct": {
  4332.     octaveMode=parseInt(getArgument(yomom.a,2) || (!octaveMode + 0));
  4333.     if(octaveMode>6)octaveMode = 6;
  4334.     if(octaveMode<0)octaveMode = 0;
  4335.     var out = "Octave mode off.";
  4336.     if(octaveMode == 1)
  4337.         out = "Octave mode on.";
  4338.     else if(octaveMode > 1)
  4339.         out = "Octaves played: "+octaveMode;
  4340.     MPP.chat.send(out);
  4341.     };break;
  4342. case "octavedelay": {var arg = getArgument(yomom.a,2);if(arg!=="")octaveDelay = parseInt(arg);MPP.chat.send("Octave delay: "+octaveDelay)};break;
  4343. case "megaphone": {if(megaphone==1){megaphone=0}else{megaphone=1} };break;
  4344. case "gamemode":case "gm": {MPP.chat.send('Your gamemode has been updated.')};break;
  4345. case "reload":{if(access(yomom.p._id)){getSongList()}else{MPP.chat.send(randomNoPerm())}};break;
  4346. case "vf": {var arg = parseFloat(getArgument(yomom.a,2));if(arg && arg<=1 && arg>=0){volumeFriction = arg;MPP.chat.send("Volume friction: "+volumeFriction)}};break;
  4347. case "msg":{if(yomom.a.indexOf("|")!=-1){privateMessage(yomom.a,yomom.p.name)}else{this.sendArray([{m: "a", message: "Please use \"|\" to separate room name and message."}])}};break;
  4348. case "rooms":{MPP.client.sendArray([{m: "+ls"}]);MPP.chat.send("Waiting for response...")};break;
  4349. case "ping":{MPP.chat.send("Play a note to check ping.");cping = yomom.p.id};break;
  4350. case "tutorial":{};break;
  4351. case "save":{if(access(yomom.p._id)){var s = yomom.a;for(var p = 0;p < partsNotes.length;p++){if(partsNotes[p].p == s){partsNotes[p].lastNote=0}}} else {MPP.chat.send(randomNoPerm())}};break;
  4352. case "prefix":{var lprefix = comRest(yomom.a) || "";if(lprefix==""){MPP.chat.send("Prefix: "+prefix)}else{if(yomom.p.id==MPP.client.getOwnParticipant().id){prefix = lprefix;MPP.chat.send("Prefix set to: "+prefix)}else{MPP.chat.send(randomNoPerm())}}};break;
  4353. case "songs":{MPP.chat.send("Song in database: "+dfg.length)};break;
  4354. case "list":case "l":{var n = comRest(yomom.a)/1;if(n==""){n=1};if(!(isNaN(n))){if(n*10>dfg.length+9){MPP.chat.send("Not found.")}else{MPP.chat.send("Display page "+n+" of "+Math.ceil(dfg.length/10));var ls = [];for(var k = 0;k<10;k++){if(dfg.slice(n*10-10,n*10)[k] !== undefined)ls.push((n*10-10+k)+". "+dfg.slice(n*10-10,n*10)[k])};MPP.chat.send(ls.join(" #@# "))}}else{MPP.chat.send("Invalid page number")}};break;
  4355. case "addtogroup":{if(access(yomom.p._id)){var toadd = comRest(yomom.a).split(" ");toadd[toadd.length-1]=undefined;toadd = getArgument(yomom.a,2);addtogroup(comRest(yomom.a),getArgument(yomom.a,yomom.a.split(" ").length))}};break;
  4356. case "delay":case "d": {var rd = comRest(yomom.a);if(!(isNaN(rd/1)) && rd!=="" && isFinite(rd/1)){d = rd/1};MPP.chat.send('Note echo delay: '+d)};break;
  4357. case "rplay": {var rp = comRest(yomom.a);if(rp/1<dfg.length){MPP.chat.send("Trying to read various file: "+dfg[rp/1]);try{play(dfg[rp/1])}catch(err){MPP.chat.send("Play error: "+err)}}};break;
  4358. case "pitch":case "t": {var arg = comRest(yomom.a);if(!(isNaN(arg/1)) && Math.abs(arg/1)<96){pitch = arg/1};MPP.chat.send('Pitch: '+pitch)};break;
  4359. case "userset": {if(access(yomom.p._id)){subrealname = comRest(yomom.a);MPP.client.sendArray([{m: "userset",set: {name: comRest(yomom.a)}}])}else{MPP.chat.send(randomNoPerm())}};break;
  4360. case "addmod":
  4361. {
  4362.     var part=comRest(yomom.a);
  4363.     var found="";
  4364.     var current={};
  4365.     for(var f=0;f<partsRecorded.length;f++)
  4366.     {
  4367.         if(partsNotes[f].p.indexOf(part)!==-1)
  4368.         {
  4369.             current=MPP.client.findParticipantById(partsRecorded[f]);
  4370.             if(moderators.indexOf(current.id)==-1)
  4371.             {
  4372.                 moderators[moderators.length]=current.id
  4373.             }
  4374.         }
  4375.     }
  4376. };
  4377. break;
  4378. case "localsound": {localSound = !localSound; MPP.chat.send("Local sound: " + (localSound ? "enabled" : "disabled"))};break;
  4379. case "search":case "s": {var s = comRest(yomom.a);var found = search(s);if(found!=="" && found.length>1){MPP.chat.send("Found indexes: "+found.slice(0,100))}else{MPP.chat.send("Not found.")}};break;
  4380. case "remove":case "rm": {if(access(yomom.p._id)){remove("songs/"+songList[comRest(yomom.a)])}else{MPP.chat.send(randomNoPerm())} };break;
  4381. case "about": {MPP.chat.send("Peripheral_Assassin's independent recording and playing music bot. Version: "+version) };break;
  4382. case "big": {
  4383.     var str = comRest(yomom.a);
  4384.     var str1 = "", str2 = "", str3 = "";
  4385.     for(var i = 0;i < str.length;i++){
  4386.         var l = lBlocks[str[i]];
  4387.         if(l)
  4388.         for(var j = 0;j < l.length;j += 3){
  4389.             str1 += l[j];
  4390.             str2 += l[j + 1];
  4391.             str3 += l[j + 2];
  4392.         };
  4393.     };
  4394.     MPP.chat.send(str1,true);
  4395.     MPP.chat.send(str2,true);
  4396.     MPP.chat.send(str3,true);
  4397. };break;
  4398. case "motd": {MPP.chat.send(motd)};break;
  4399. case "multiroom": {if(access(yomom.p._id)){multiRoom = !multiRoom;MPP.chat.send("Multiroom mode turned "+(multiRoom && "on" || "off")+".")}else{MPP.chat.send(randomNoPerm())}};break;
  4400. case "voteupload": {if(allParts[yomom.p._id].vu!==1){allParts[yomom.p._id].vu = 1;uploadVotes++;MPP.chat.send("You have voted for uploading my code. Total votes: "+uploadVotes)}else{MPP.chat.send("You already voted!")}};break;
  4401. case "random":case "r": {var toplay = Math.round(Math.random()*dfg.length);i=0;sec=0;play(songList[toplay]);MPP.chat.send("Reading file. Index: "+toplay+" Song: "+songList[toplay])};break;
  4402. case "globalsendchat":case "gsc":{if(comRest(yomom.a).indexOf("\\")==-1)gMessage(comRest(yomom.a),yomom.p.name)};break;
  4403. case "setmotd": {if(access(yomom.p._id)){motd = comRest(yomom.a)}else{MPP.chat.send(randomNoPerm())}};break;
  4404. case "rename": {
  4405.     if(access(yomom.p._id)){
  4406.         var index = parseInt(getArgument(yomom.a,2));
  4407.         var newName = comRest(yomom.a).split(" ").slice(1).join(" ");
  4408.         rename(songList[index],newName);
  4409.         songList[index] = newName;
  4410.     }else{
  4411.         MPP.chat.send(randomNoPerm());
  4412.     }
  4413.         };break;
  4414. case "startrecord": {if(!recording){recording = true;wn = [];MPP.chat.send("Started recording notes. Type \\stoprecord to stop.") }else{MPP.chat.send("Already recording. Type \\stoprecord to stop.")}};break;
  4415. case "stoprecord": {if(recording){recording = false;MPP.chat.send("Stopped recording. Type \\playrecord to play recorded notes. ("+wn.length+" packets received.)");counter = 0}};break;
  4416. case "playrecord": {if(!playing){playing = 1;offset = wn[0].t;wnI = 0;function playwn(){if(playing==1){playnbn(wn[wnI].n);wnI++;setTimeout(playwn,wn[wnI+1].t-wn[wnI].t)}};playwn()}else{MPP.chat.send("Already playing. Please try later.")}};break;
  4417. case "play":case "p":
  4418. {
  4419. var arg=comRest(yomom.a);
  4420. if(arg!=="")
  4421.     {
  4422.     name = "";
  4423.     var song = arg.toLowerCase();
  4424.     songs=songList;
  4425.     var found=-1;
  4426.     if(songList!==undefined)
  4427.         {
  4428.         if((song/1).toString()=="NaN")
  4429.             {
  4430.             for(var i=0;!(i>songList.length-1);i++)
  4431.                 {
  4432.                 if(songList[i].toLowerCase().slice(songList[i].split(".").length+1).indexOf(song)!==-1)
  4433.                     {found=i}
  4434.                 }
  4435.             }
  4436.             else
  4437.             {
  4438.             if(songList[song/1] !== undefined)
  4439.                 {found=song/1}
  4440.             }
  4441.         };
  4442.     if(found!==-1)
  4443.     {
  4444.     if(playing==1)
  4445.         {
  4446.         playing=0;
  4447.         clearTimeout(timeout)
  4448.         };
  4449.     MPP.chat.send("Reading file. Index: "+found+" Song: "+songList[found]);
  4450.     i=0;
  4451.     sec=0;
  4452.     play(songList[found])
  4453.     }
  4454.     else
  4455.     {
  4456.     MPP.chat.send("Not found.")
  4457.     }  
  4458. }
  4459. else
  4460. {
  4461. playprecise()
  4462. }
  4463. };break;
  4464. case "console": {if(access(yomom.p._id)){var err;try{var out = eval(comRest(yomom.a));MPP.chat.send("Console: "+((typeof out == "object") ? JSON.stringify(out) : out))} catch(err){MPP.chat.send(err.toString())}}else{MPP.chat.send(randomNoPerm())} };break;
  4465. case "speed":case "tempo": {var rd = comRest(yomom.a);if(!(isNaN(rd/1)) && rd!=="" && isFinite(rd/1))if(rd>10){if(nps*rd/100*v<(100/(1+MPP.client.channel._id!=="RP Room"*9))){speed = rd }else{ speed = ((100/(1+MPP.client.channel._id!=="RP Room"*9))/(nps*v))*100}}else{speed=10};MPP.chat.send("Tempo: "+speed+"%")};break;
  4466. case "dbsave":{if(access(yomom.p._id)){saveParts()}else{MPP.chat.send(randomNoPerm())}};break;
  4467. case "dbload":{if(access(yomom.p._id)){getParts()}else{MPP.chat.send(randomNoPerm())}};break;
  4468. case "posthalting": case "ph":{postHalting = !postHalting;MPP.chat.send("Posthalting "+((postHalting) && "enabled" || "disabled"))};break;
  4469. case "suggestions":{};break;
  4470. case "bin":{
  4471.     var code1 = secretChar;
  4472.     var code2 = secretChar;
  4473.     var isBin = true;
  4474.     var txt1 = comRest(yomom.a);
  4475.     var txt2 = txt1.split(" ");
  4476.     for(var i = 0;i < txt2.length;i++){
  4477.         var pr = parseInt(txt2[i],2);
  4478.         if(!isNaN(pr)){
  4479.             var cha = pr.toString(16);
  4480.             code2 += eval("'\\u" + "0".repeat(4 - cha.length) + cha + "'");
  4481.         }else{
  4482.             isBin = false;
  4483.             break;
  4484.         };
  4485.     };
  4486.     if(isBin){
  4487.         MPP.chat.send(code2);
  4488.     }else{
  4489.         for(var i = 0;i < txt1.length;i++){
  4490.             code1 += txt1.charCodeAt(i).toString(2) + " ";
  4491.         };
  4492.         MPP.chat.send(code1);
  4493.     }
  4494. };break;
  4495. case "hex":{
  4496.     var code1 = secretChar + "0x";
  4497.     var code2 = secretChar;
  4498.     var txt1 = comRest(yomom.a);
  4499.     var txt2 = comRest(yomom.a);
  4500.     var isHex = txt1.slice(0,2) == "0x";
  4501.     if(isHex){
  4502.         for(var i = 2;i < txt2.length;i += 4){
  4503.             var pr = txt2.slice(i,i + 4);
  4504.             if(!isNaN(pr)){
  4505.                 code2 += eval("'\\u"+pr+"'");
  4506.             };
  4507.         };
  4508.         MPP.chat.send(code2);
  4509.         console.log(code2);
  4510.     }else{
  4511.         for(var i = 0;i < txt1.length;i++){
  4512.             var cha = txt1.charCodeAt(i).toString(16);
  4513.             code1 += "0".repeat(4 - cha.length) + cha;
  4514.         };
  4515.         MPP.chat.send(code1);
  4516.         console.log(code1);
  4517.     };
  4518. };break;
  4519. case "lenny":{MPP.chat.send("( ͡° ͜ʖ ͡°)")};break;
  4520. case "op":{if(yomom.p.id==MPP.client.getOwnParticipant().id){var p = spart(getArgument(yomom.a,2),true);if(p){addOp(p._id);MPP.chat.send("Opped "+p.name+"; id: "+p._id)}else{MPP.chat.send("User not found.")}}else{MPP.chat.send(randomNoPerm())}};break;
  4521. case "deop":{if(yomom.p.id==MPP.client.getOwnParticipant().id){var p = spart(getArgument(yomom.a,2),true);if(p){deOp(p._id);MPP.chat.send("Deopped "+p.name+"; id: "+p._id)}else{MPP.chat.send("User not found.")}}else{MPP.chat.send(randomNoPerm())}};break;
  4522. case "takecrown":{if(access(yomom.p._id))MPP.client.sendArray([{m: "chown",p: yomom.p.id}])};break;
  4523. case "vq":{vqEnabled = !vqEnabled;MPP.chat.send("Virtual quota "+(vqEnabled && "enabled." || "disabled."))};break;
  4524. case "restart":case "rr": {v = 1;octaveMode = 0;speed = 100;pitch = 0;gAutoSustain = false;octaveDelay = 100;MPP.chat.send("Config reset.")};break;
  4525. case "meow": {MPP.chat.send("I am only a bot. I cannot say 'MEOW'")};break;
  4526. case "autorecord":case "autorec":{MPP.chat.send("Auto recording: " + ((rcEnabled = !rcEnabled) ? "enabled" : "disabled") ); localStorage.rcEnabled = rcEnabled};break;
  4527. case "maxv":{for(var n in window.pre){window.pre[n].b = 1};MPP.chat.send("Volume maximized.")};break;
  4528. case "gms":{var rooms = [];var n = clientArray.length;var nc = 0;var nr = 0;for(var cl in clientArray){if(clientArray[cl].isConnected())nc++;if(clientArray[cl].channel)if(clientArray[cl].channel._id==clientOnRooms[cl]){nr++;rooms.push(clientOnRooms[cl])}};MPP.chat.send("Multi room system state: "+nc+" connected, "+nr+" are on valid rooms. Rooms: "+rooms)};break;
  4529. case "sustain": {gAutoSustain=!gAutoSustain;MPP.chat.send("Sustain: "+gAutoSustain)};break;
  4530. case "magnet": {cursorUpdate=0;var part = MPP.client.findParticipantById(yomom.p._id);MPP.client.sendArray([{m: "m", x: part.x, y: part.y }])};break;
  4531. case "unfollow": {var part=fixName(comRest(yomom.a));var found=[];var current=spart(part !== "" ? part: yomom.p.name.split("\u034f").join(""),true,1);
  4532. if(current !==undefined)
  4533. {found[found.length]=" "+current.name;allParts[current._id].f = 0;
  4534. blacklist[blacklist.length]=current.id;}
  4535. if(found.join().toString()!==""){MPP.chat.send("Unfollowing "+found+" ...")}else{MPP.chat.send("User not found.")}};break;
  4536. case "follow":
  4537. {
  4538.     var part=fixName(comRest(yomom.a)),found=[],current={};
  4539.     var current = spart(part !== "" ? part: yomom.p.name.split("\u034f").join(""),true,0);if(current!==undefined){
  4540.     if(blacklist.indexOf(current._id)!==-1)
  4541.             {
  4542.                 found[found.length]=" "+current.name;
  4543.                 blacklist[blacklist.indexOf(current.id)]=undefined;
  4544.             }
  4545.     allParts[current._id].f = 1;
  4546.     if(current.name!=="")
  4547.     {
  4548.         MPP.chat.send("Following "+found+" ...")
  4549.     }
  4550.     else
  4551.     {
  4552.     MPP.chat.send("User not found or user is already followed.")
  4553.     }
  4554.     }
  4555. };break;
  4556. case "whois": {var part=fixName(comRest(yomom.a));var n = 1;if(isNaN(part/1)){spart(part)}else{n=part/1};if(displayPart[0]!==undefined && n<displayPart.length+1){MPP.chat.send('Display participant '+n+' of '+displayPart.length);current = displayPart[n-1];MPP.chat.send("Name: "+current.name+" id: "+current.id+" color: "+current.color+ "( " + (new Color(current.color)).getName() + " ) _id: "+current._id+" x: "+current.x+" y: "+current.y)}else{MPP.chat.send("Not found.")}};break;
  4557. case "whoami": {var current=yomom.p;MPP.chat.send("Name: "+current.name+" id: "+current.id+" color: "+current.color+ "( " + (new Color(current.color)).getName() + " ) _id: "+current._id+" x: "+current.x+" y: "+current.y)};break;
  4558. case "help":case "?": {
  4559. if (getArgument(yomom.a,2)==undefined){
  4560. if(access(yomom.p._id))MPP.chat.send("Admin only: \\console, \\remove <song>, \\userset <name>, \\dbsave, \\dbload, \\save, \\rename, \\setmotd <motd>, \\remove, \\reload, \\takecrown".split("\\").join(prefix))
  4561. ;
  4562. setTimeout(function(){MPP.chat.send("Auto playing & NoTurns: \\play [song], \\sustain, \\volume [volume], \\stop, \\octave, \\octavedelay, \\speed [tempo], \\pitch, \\delay [note delay], \\pos [seconds], \\posthalting, \\list(l) \\vf \\restart(rr) \\autorec".split("\\").join(prefix)) },1000);
  4563. setTimeout(function(){MPP.chat.send("Recording & hopping: \\startrecord, \\stoprecord, \\playrecord, \\follow [player], \\unfollow [player], \\ITball, \\megaphone".split("\\").join(prefix))
  4564. MPP.chat.send("Other: \\help [command], \\motd, \\about, \\voteupload, \\press <notes> <volume>, \\echo, \\megaphone, \\whois [player], \\ping, \\magnet, \\gms, \\multiroom, \\globalsendchat, \\msg, \\big".split("\\").join(prefix))},2000);
  4565. }
  4566. else
  4567. {
  4568. switch((getArgument(yomom.a,2) || "").toLowerCase())
  4569. {
  4570. case "play": {MPP.chat.send(".\\play <song_number> Plays song.")}
  4571. ;break;
  4572. case "speed": {MPP.chat.send(".\\speed <speed> Sets the song playing speed. Optimal speed is 100.")}
  4573. ;break;
  4574. case "stop": {MPP.chat.send(".\\stop Just stops playing song or recording")};break;
  4575. case "console": {MPP.chat.send(".\\console puts the commands into console")};break;
  4576. case "itball": {MPP.chat.send(".\\ITball is a smart answering ball with auto-learning system")};break;
  4577. case "megaphone": {MPP.chat.send(".\\megaphone Turns on or off repeating notes pressed on keyboard.")};break;
  4578. case "record":{MPP.chat.send(".\\startrecord Starts, \\stoprecord stops recording the keyboard notes.")};break;
  4579. case "about": {MPP.chat.send("Peripheral_Assassin's independent recording and playing music bot. Type \\help to see a list of commands")};break;
  4580. case "echo": {MPP.chat.send(".\\echo Turns on or off notifications about players.")};break;
  4581. case "motd": {MPP.chat.send(".\\motd shows message of the day.")};break;
  4582. case "sustain": {MPP.chat.send("Turns auto sustain on and off")};break;
  4583. case "maxv":{};break;
  4584. case "suggestions": {};break;
  4585. case "suggest": {};break; //TODO
  4586. case "gms": {MPP.chat.send("Checks information about bots that are connected to other rooms.")};break;
  4587. case "voteupload":{MPP.chat.send(".\\voteupload allows you to vote for uploading my code.")};break;
  4588. case "pitch": {MPP.chat.send("Use \\pitch to set note offset while playing song or following notes.")};break;
  4589. case "delay": {MPP.chat.send("Type \\delay <ms> to set time between playing multiple notes.")};break;
  4590. case "follow": {MPP.chat.send("Type \\follow <nickname> to let me some information about you (notes)")};break;
  4591. case "unfollow": {MPP.chat.send("Type \\unfollow <nickname> to do not let me some information about you (notes)")};break;
  4592. case "volume": {MPP.chat.send(".\\volume <volume> Sets the song playing volume. Optimal volume is 1.")};break;
  4593.  
  4594. case "press": {MPP.chat.send(".\\press <notes> <volume> Plays simple note on the keyboard. Examples: cs5 1,a4 5,b3 10,g6 69.")};break;
  4595. default: {MPP.chat.send("No help for: "+JSON.stringify(getArgument(yomom.a,2)))};break;
  4596. }
  4597. }
  4598. }
  4599. ;break;
  4600.  
  4601. case "volume":case "v": {var rd = comRest(yomom.a);if(rd!=="" && !(isNaN(rd/1)) && isFinite(rd/1)){if( rd=="mouseY"){snap = 1}else{
  4602.             if(nps){
  4603.                 if(nps*(speed/100)*rd<(100/(1+MPP.client.channel._id!=="RP Room"*9))){v = getArgument(yomom.a,2);snap = 0}else{v = (100/(1+MPP.client.channel._id!=="RP Room"*9))/(nps*(speed/100))}
  4604.             }else{
  4605.                 v = getArgument(yomom.a,2);
  4606.             }
  4607.         }
  4608.     };MPP.chat.send("Volume: "+v)};break;
  4609. case "press": {
  4610.     if(getArgument(yomom.a,3)==""){
  4611.         MPP.chat.send("Too few arguments. Use: \\press <note> <volume>")
  4612.     }else{
  4613.         tv = cv;
  4614.         var len = yomom.a.split(" ").length;
  4615.         vo = parseInt(getArgument(yomom.a,len));
  4616.         cv = vo;
  4617.         if(vo<=100 && typeof vo == "number"){
  4618.             var n = yomom.a.split(" ").slice(1,len - 1);
  4619.             for(var u = 0;u<n.length+1;u++){
  4620.                 MPP.press(n[u],1)
  4621.             };
  4622.             cv = tv
  4623.         }else{MPP.chat.send('Too high volume. Type a number from 0 to 100')} } };break;
  4624.  
  4625. default: {MPP.chat.send("Unknown command. Type \\help for help")};break
  4626. }
  4627. }
  4628. else
  4629.     {
  4630.     if(false && (wBuffer[2]>Date.now()-40000)&&(yomom.a.indexOf("\'")==-1)&&(wBuffer[1].indexOf("\"")==-1)&&(yomom.a.indexOf("\"")==-1)&&(wBuffer[1].indexOf("\'")==-1)&&(yomom.a.indexOf("\\")==-1)&&(yomom.a.toLowerCase().indexOf("itball")==-1)&&(wBuffer[1].indexOf("\\")==-1))
  4631.         {
  4632.             if(!(ball[yomom.a]) && !(yomom.a in ball)){ball[wBuffer[1]]=yomom.a;
  4633.             addTextToFile("ITball","\nball[\'"+JSON.stringify(wBuffer[1])+"\']=\'"+JSON.stringify(yomom.a)+"\';")};
  4634.             var n = 0,w1 = yomom.a.split(" "),w2 = wBuffer[1].split(" ");          
  4635.             while((w1.length>n)&&(w2.length>n))
  4636.             {
  4637.                 if(!(ball[w1[n]])){ball[w1[n]]=w2[n];
  4638.                 addTextToFile("ITball","\nball[\'"+w2[n]+"\']=\'"+w1[n]+"\';")};
  4639.                 n++;
  4640.             }
  4641.         }
  4642.     if(false &&(yomom.a.indexOf("\\")==-1)&&(yomom.a.toLowerCase().indexOf("itball")==-1))
  4643.     {wBuffer[1]=yomom.a;
  4644.     wBuffer[2]=Date.now();};
  4645.     }
  4646. if(blacklist.indexOf(yomom.p.id)==-1)
  4647. {
  4648.     if(partsRecorded.indexOf(yomom.p._id)==-1)
  4649.     {
  4650.         partsRecorded[partsRecorded.length]=yomom.p._id;
  4651.         partsNotes[partsNotes.length]=
  4652.         {
  4653.             lastNote:Date.now(),
  4654.             p:yomom.p.name,
  4655.             a:[],b:[],c:[],r:[]
  4656.         }
  4657.     };
  4658. };
  4659.     for(var l = 0;l<partsRecorded.length-1;l++)
  4660.     {
  4661.         if(MPP.client.findParticipantById(partsRecorded[l])==MPP.client.offlineParticipant && partsRecorded[l].indexOf("GROUP")==-1)
  4662.         {
  4663.             partsRecorded[l] = undefined;
  4664.             partsNotes[l] = undefined;
  4665.         };
  4666.         partsRecorded = partsRecorded.filter(function(a){return a});
  4667.         partsNotes = partsNotes.filter(function(a){return a});
  4668.     };
  4669. addPart(yomom.p);
  4670. };
  4671.  
  4672. flute();
  4673.  
  4674. function vnum(n){
  4675.     return (typeof n == "number") && (Math.abs(n)<200)
  4676. };
  4677. var oMouse = {//mouse position troll
  4678.     x: 0,
  4679.     y: 0,
  4680.     nx: 0,
  4681.     ny: 0,
  4682.     t: 0,
  4683.     idle: 0,
  4684.     moveSpeed: 1,
  4685.     fIUpdate: function(){
  4686.         if(oMouse.t>0 && subrealname!=="Anonymous"){
  4687.             if(oMouse.x==0 && oMouse.y==0)console.log("wyzerowal");
  4688.             oMouse.x -= (oMouse.x - oMouse.nx) * (1/oMouse.t);
  4689.             oMouse.y -= (oMouse.y - oMouse.ny) * (1/oMouse.t);
  4690.             oMouse.t--;
  4691.             if(vnum(oMouse.x) && vnum(oMouse.y))
  4692.             {
  4693.                 MPP.client.sendArray([{m: "m",x: oMouse.x,y: oMouse.y}]);
  4694.                 MPP.client.participantUpdate({m: "m",x: oMouse.x,y: oMouse.y,id: MPP.client.getOwnParticipant().id});
  4695.             }
  4696.             /*else
  4697.             {
  4698.                 console.log(oMouse.x+" "+oMouse.y);
  4699.                 oMouse.x = 0;
  4700.                 oMouse.y = 0;
  4701.                 oMouse.nx = 0;
  4702.                 oMouse.ny = 0;
  4703.                 oMouse.t = 0;
  4704.             };
  4705.             */
  4706.         };
  4707.     },
  4708.     fUpdate: function(m){
  4709.         if(vnum(m.x) && vnum(m.y)){
  4710.         oMouse.nx = 100-m.x;
  4711.         oMouse.ny = 100-m.y;
  4712.         var a = oMouse.x-oMouse.nx;
  4713.         var b = oMouse.y-oMouse.ny;
  4714.         oMouse.t = Math.ceil(Math.sqrt((a*a)+(b*b))/oMouse.moveSpeed);
  4715.         if(oMouse.t<1)oMouse.t = 0;
  4716.         //console.log(oMouse.t);
  4717.         }
  4718.     },
  4719. };
  4720. //clearInterval(oMouse.fIUpdate);
  4721. //oMouse.iUpdate = setInterval(oMouse.fIUpdate,50);
  4722. //MPP.client._events.m.pop();
  4723. //MPP.client.on("m",oMouse.fUpdate);
  4724.  
  4725.  
  4726. var partCheck = setInterval(function(){
  4727.     partsRecorded.forEach(function(id){
  4728.         rec({p: id,n: [],t: 0})
  4729.     })
  4730. },10000);
  4731. MPP.client.on("participant removed", function(jomom) {if(echo==1)MPP.chat.send(jomom.name+" left the room")});
  4732. MPP.client.on("participant added", function(jomom) {addPart(jomom);
  4733. if(echo==1){MPP.chat.send("Welcome, "+jomom.name+". Type \\help to get my list of commands!")};if(allParts[jomom._id].f==0){blacklist.push(jomom.id)}});
  4734. MPP.client.on("participant update", function(jomom){addPart(jomom);allParts[jomom._id].name = jomom.name})
  4735. var rcEnabled = (localStorage.rcEnabled !== undefined ? localStorage.rcEnabled : true);
  4736. function rec(jomom) {if(!rcEnabled)return false;
  4737. if(jomom.p == cping){MPP.chat.send("Ping: "+(Date.now()-jomom.t)+" ms.");cping = ""};
  4738. //if(allParts[jomom.p.id] && allParts[jomom.p.id].f == 0)return;
  4739. if(true)//blacklist.indexOf(jomom.p)==-1)
  4740. {
  4741.     if(partsRecorded.indexOf(jomom.p)==-1)
  4742.     {
  4743.         partsRecorded[partsRecorded.length]=jomom.p;
  4744.         partsNotes[partsNotes.length]=
  4745.         {
  4746.             lastNote:jomom.t,
  4747.             lastPlayed: Date.now(),
  4748.             p:MPP.client.findParticipantById(jomom.p).name,
  4749.             a:[],
  4750.             b:[],
  4751.             c:[],
  4752.             r:[]
  4753.         }
  4754.     };
  4755.     var delay = 0, delaykill = [0,0,0],lastd = 0,played = false, id = partsRecorded.indexOf(jomom.p), counterb = jomom.t-partsNotes[id].lastNote;
  4756.     if(counterb>10000 || (Date.now()-(partsNotes[id].lastPlayed || Date.now()) > 20000))
  4757.     {
  4758.         var wait=0;
  4759.         partsNotes.forEach(function(pn)
  4760.             {
  4761.             if(((jomom.t-pn.lastNote)>10000) || ((pn.lastPlayed || 0) + 20000 < Date.now()))
  4762.                 {
  4763.                     if(pn.a.length>250)
  4764.                     {
  4765.                             pn.c[0]=0;
  4766.                             var songname = "";
  4767.                             if(partsRecorded[partsNotes.indexOf(pn)].indexOf("GROUP")!==-1)
  4768.                             {songname = partsRecorded[partsNotes.indexOf(pn)]}
  4769.                             else
  4770.                             {songname = fixName(MPP.client.findParticipantById(partsRecorded[partsNotes.indexOf(pn)]).name)}
  4771.                             addSong("MPP - recording("+songname+")",exportSong(pn.a,pn.b,pn.c,pn.r));
  4772.                     }
  4773.                         pn.a=[];
  4774.                         pn.b=[];
  4775.                         pn.c=[];
  4776.                         pn.r=[]
  4777.                 }
  4778.             }
  4779.     )};
  4780.     var gr = getGroup(jomom.p)
  4781.     if(gr!==-1)
  4782.     {
  4783.         var renew = jomom;
  4784.         renew.p="GROUP"+gr;
  4785.         return rec(renew)
  4786.     }
  4787.     for(var d = 0;d<jomom.n.length;d++)
  4788.     {
  4789.         if(jomom.n[d].s==undefined)
  4790.         {
  4791.             played = true;
  4792.             partsNotes[id].b[partsNotes[id].b.length]=jomom.n[d].v;
  4793.             partsNotes[id].a[partsNotes[id].a.length]=jomom.n[d].n;
  4794.             if(d>0)
  4795.             {
  4796.                 if(jomom.n[d].d!==undefined)
  4797.                 {  
  4798.                     lastd = jomom.n[d].d;
  4799.                     delaykill[delaykill.length]=jomom.n[d].d
  4800.                 }
  4801.                 else
  4802.                 {
  4803.                     delaykill[delaykill.length]=delaykill[delaykill.length-1]
  4804.                 }
  4805.             }
  4806.             delay=delaykill[delaykill.length-1]-delaykill[delaykill.length-2];
  4807.             partsNotes[id].c[partsNotes[id].c.length]=Math.round(counterb+delay);
  4808.             counterb=0;
  4809.             if(partsNotes[id].b[partsNotes[id].b.length-1]==undefined)
  4810.             {
  4811.                 partsNotes[id].b[partsNotes[id].b.length-1]=1
  4812.             };
  4813.         }
  4814.         else  //defining length of notes (r)
  4815.         {
  4816.             var time = 0;
  4817.             for(var l = partsNotes[id].a.length-1;l>0;l--)
  4818.             {
  4819.                 time+=partsNotes[id].c[l];
  4820.                 if(partsNotes[id].a[l] == jomom.n[d].n)
  4821.                 {
  4822.                     partsNotes[id].r[l] = Math.round(time);
  4823.                     l = 0;
  4824.                 }
  4825.             }
  4826.         }
  4827.         if(played)
  4828.         {
  4829.             partsNotes[id].lastNote=jomom.t+lastd;
  4830.             partsNotes[id].lastPlayed = Date.now();
  4831.         }
  4832.     };
  4833. };addPart(jomom.p)
  4834. if(recording)
  4835. wn.push(jomom)
  4836. };
  4837.  
  4838.  
  4839.  
  4840. var tClient = 0;
  4841.  
  4842. motd = "This is the default message of the day.";
  4843.  
  4844. MPP.client.on("n", rec);
  4845. MPP.client.on("n", function(jomom) {if(megaphone==1 && blacklist.indexOf(jomom.p._id)==-1){if(MPP.client.noteBuffer.length>50 && warn == 1){MPP.chat.send("Note spam detected. Stopping...");megaphone=0}else{var delay = 0;var d = 0;playnbn(jomom.n,d,delay)}} })
  4846. MPP.client.on("a", function(yomom) {wazne(yomom)});
  4847. MPP.client.on("ls",function(ls){
  4848.     if(multiRoom){
  4849. var rooms = "";for(var i in ls.u) {if(!ls.u.hasOwnProperty(i)) continue;if(clientOnRooms.indexOf(ls.u[i]._id)==-1){clientOnRooms.push(ls.u[i]._id);
  4850. clientArray.push(new Client("ws:multiplayerpiano.com:443"));
  4851. clientArray[clientArray.length-1].on("a",function(msg){
  4852. if(this.channel._id!==MPP.client.channel._id){var pmsg = msg;pmsg.p.name="["+this.channel._id+"] "+pmsg.p.name;MPP.chat.receive(pmsg)
  4853. if(msg.a[0]==prefix)
  4854. switch(msg.a.slice(1).split(" ")[0].toLowerCase()){
  4855. case "msg":{if(msg.a.indexOf("|")!=-1){privateMessage(msg.a,msg.p.name)}else{this.sendArray([{m: "a", message: "Please use "|" to separate room and message."}])}};break;
  4856. case "globalsendchat":case "gsc":{if(comRest(msg.a).indexOf("\\")==-1)gMessage(comRest(msg.a),msg.p.name)};break;
  4857. case "motd":{this.sendArray([{m: "a", message: motd}])};break
  4858. case "help":{this.sendArray([{m: "a", message: "Local commands: \\msg <room>| <message>, \\globalsendchat, \\gsc <message>, \\help. See room "+MPP.client.channel._id+" to get full list of commands.".split("\\").join(prefix)}])};break;
  4859. }
  4860. }
  4861. })
  4862. clientArray[clientArray.length-1].on("participant added", function(jomom) {addPart(jomom);allParts[jomom._id].id = jomom.id;if(allParts[jomom._id].f==0 && blacklist.indexOf(jomom.id)==-1){blacklist.push(jomom._id)}});
  4863. clientArray[clientArray.length-1].on("participant update", function(jomom){addPart(jomom);allParts[jomom._id].name = fixName(jomom.name)});
  4864. clientArray[clientArray.length-1].chatsend = function(msg){this.sendArray([{m: "a", message: msg}]);
  4865. }}
  4866. for(var c in clientArray){if(clientArray[c].isConnected()){clientArray[c].stop()};};
  4867. for(var c in clientArray){clientArray[c].setChannel(clientOnRooms[c]);clientArray[c].start();};
  4868. MPP.client.sendArray([{m: "-ls"}]);
  4869. clearTimeout(tClient);
  4870. tClient = setTimeout(function(){var connected = 0,number = clientArray.length,valid = 0;for(var c in clientArray){if(clientArray[c].isConnected())connected++;if(clientArray[c].channel)if(clientArray[c].channel._id==clientOnRooms[c])valid++};MPP.chat.send("Reloading clients: "+number+" loaded, "+connected+" connected, "+valid+" valid.")},10000)
  4871. }
  4872.     };
  4873. });
  4874.  
  4875. multiroom = false;
  4876.  
  4877. var speed = 100;var playing = 0;var v = 1;var i = -1;function st() {if (i<a.length && playing == 1){if(cfix==1 && c[i]>10000){c[i]=10000}setTimeout(function () {if(v>1){ultrapress(a[i],b[i]*v)}else{MPP.press(a[i],b[i]*v)};i++;st();{}},c[i]/speed*100)}else {playing = 0}};
  4878. function play(song){getSong(song);setTimeout(function(){if(songcode.length>50 || songcode!=="")
  4879. {i=0;sec=0;setTimeout(function(){eval(songcode);playprecise(song)},1000)}
  4880. else{remove("songs/"+song);MPP.chat.send("Critical error, file lost.");getSongList()}},500)
  4881.  
  4882. }
  4883. echo=0;
  4884. var wait = 100;var dmg = 0;var rdmg = 0;
  4885. function chordWow(){rdmg++;if(repeatkeys[rdmg]==undefined){rdmg=0;dmg++};if(dmg>6){dmg=0};MPP.press(repeatkeys[rdmg]+dmg);setTimeout(chordWow,wait)};
  4886. function clearChat(ch){var cha = ch || "\u3164";MPP.client.sendArray([{m: "userset",set: {name: cha.repeat(32)}}]);for(var y = 0;y<4;y++){MPP.chat.send((cha.repeat(15) + "\u3164").repeat(32))}};
  4887. code="";
  4888. //file="ITball.txt";getFile();setTimeout(function(){eval(code)},2000);
  4889. function putNote(n,v,d){}
  4890. // pitch faller test: var test  = setInterval(function(){pitch = Math.round(MPP.client.getOwnParticipant().x/2-25)},100);
  4891. setTimeout(function(){getParts()},4000);
  4892. setTimeout(function(){for(var p in MPP.client.ppl){if(allParts[MPP.client.ppl[p]._id].f==0){blacklist.push(MPP.client.ppl[p].id)}}},10000);
  4893. flute();
  4894.  
  4895. var foundBN = [];var best = {i: -1,n: "",s: -Infinity};
  4896. function searchByNote(notes){foundBN = [];best = {i: -1,n: "",s: -Infinity};
  4897. window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) {
  4898.   fs.root.getDirectory('songs', {}, function(dirEntry) {
  4899. dfg.forEach(function(f){
  4900.     dirEntry.getFile(f, {}, function(fileEntry) {
  4901.  
  4902.     // Get a File object representing the file,
  4903.     // then use FileReader to read its contents.
  4904.     fileEntry.file(function(file) {
  4905.        var reader = new FileReader();
  4906.  
  4907.        reader.onloadend = function(e) {
  4908.          eval(this.result);
  4909. var index = 0;var score = 0;
  4910. for(var n = 0;n<a.length-1;n++){if(keys.indexOf(notes[index+1])==-1){score+=100;break};var r = keys.indexOf(notes[index])-keys.indexOf(notes[index+1]);if(r==keys.indexOf(a[n])-keys.indexOf(a[n+1])){score+=20;index++}else{score-=1};};
  4911. foundBN.push({i: songList.indexOf(f),n: f,s: score});
  4912. if(foundBN.length==songList.length){foundBN = foundBN.sort(function(a,b){var bs =(a.s>b.s && a || b);if(bs.s>best.s){best = bs};return a.s>b.s});MPP.chat.send("Index: "+best.i+"; Song: "+best.n+"; Score: "+best.s)};
  4913.        };
  4914.        reader.readAsText(file);
  4915.     }, errorHandler);
  4916.  
  4917.   }, errorHandler);
  4918.  
  4919. });
  4920.  
  4921. }, errorHandler);
  4922. }, errorHandler);};
  4923.  
  4924. function loadingAnimation(nickLength){
  4925.     window.strla = "___________LOADING_PLEASE_WAIT";
  4926.     window.laidx = 0;
  4927.     window.lanl = nickLength;
  4928.     window.laintr = setInterval(function(){
  4929.         if(laidx < strla.length - lanl){
  4930.             subrealname = strla.slice(laidx,laidx + lanl + 1);
  4931.             laidx++;
  4932.         }else{
  4933.             subrealname = "\u29BF_RECORDER_\u29BF";
  4934.             setTimeout(function(){subrealname = "PeriOS (PL)"},10000);
  4935.             clearInterval(laintr);
  4936.         };
  4937.     },2000);
  4938. };
  4939.  
  4940. loadingAnimation(11);
  4941.  
  4942. getSongList();
  4943.  
  4944. var secretChar = "\u034f";
  4945.  
  4946. var supernamechanger = setInterval(function(){var n = subrealname;for(var i = subrealname.length/2;i>0;i--){var sl = Math.ceil(Math.random()*subrealname.length);n = n.slice(0,sl)+secretChar+n.slice(sl)};realName = n;if(playing==0)MPP.client.sendArray([{m: "userset", set: {name: realName}}])},2500);
  4947.  
  4948. var PC = {prefix: "&"},broenabled = false;
  4949. var tbroadcast = setInterval(function(){if(broenabled)MPP.chat.send("[OS] "+broadcasts[Math.floor(Math.random()*(broadcasts.length))].join(""))},300000);
  4950. window.broadcasts =
  4951. [
  4952.     ["Type ",PC.prefix,"help to get pianocraft game commands!"],
  4953.     ["Type ",prefix,"help to get my bot commands!"],
  4954.     ["Get newest version of my bot here: http://pastebin.com/3HCZVRe4"],
  4955.     ["Added new command: "+prefix+"gms ! Use this to read information about my bots connected to other rooms."],
  4956.     ["If you don't want to record your songs, just type ",prefix,"unfollow to disable it."]
  4957. ];
  4958. String.prototype.formatUnicode = function(){
  4959. let replaced = this;
  4960. let chs = {
  4961.     f: ["F"],
  4962.     r: ["R"],
  4963.     o: ["O"],
  4964.     m: ["M"],
  4965. };
  4966. let reps = {};
  4967. for(var c in chs){
  4968.     for (var trep in chs[c]){
  4969.         reps[chs[c][trep]] = c;
  4970.     };
  4971. };
  4972. //console.log(reps)
  4973. for(var rep in reps){
  4974.     replaced = replaced.replace(eval("/"+rep+"/g"),reps[rep])
  4975. };
  4976. return replaced;
  4977. };
  4978.  
  4979. var lBlocks = {
  4980.     A: ["\u2597","\u2599","\u258c\u2005","\u2596","\u259f","\u2005\u2590"],
  4981.     B: [],
  4982. };
  4983.  
  4984. function getFairTimeOffset(){ //atlas module, i hope it'll be working
  4985.     var quota = 1;            //nope
  4986.     var offset = 0;
  4987.     for(var i = 0;i < c.length;i++){
  4988.         quota += c[i] / 10;
  4989.         if(quota < 1){
  4990.             offset += 10;
  4991.         }else{
  4992.             quota--;
  4993.         }
  4994.     };
  4995.     return offset; //in ms
  4996. };
  4997. var dataB = {
  4998.     s: 0,
  4999.     e: 0,
  5000.     t: 0,
  5001.     idx: 0,
  5002.     it: 0,
  5003. };
  5004. function extractPack(){
  5005.     var now = Date.now();
  5006.     var pack = {m: "n",n: [],t: now};
  5007.     for(var i = 0;i < 88;i++){
  5008.         var id = dataB.idx;
  5009.         if(id == undefined)break;
  5010.         pack.n.push({n: a[id], v: b[id], d: (dataB.s - now)});
  5011.         dataB.s += c[id];
  5012.         dataB.idx++;
  5013.     };
  5014.     return pack;
  5015. };
  5016. function playWithOffset(ms){
  5017.     if(ms){
  5018.     dataB.s = Date.now() + ms;
  5019.     dataB.e = dataB.s + songLength * 1000;
  5020.     dataB.idx = 0;
  5021.     dataB.it = setInterval(function(){
  5022.         MPP.client.sendArray([extractPack()]);
  5023.         if(dataB.idx >= a.length - 1)clearInterval(dataB.it);
  5024.     },880);
  5025.     return dataB.it;
  5026.     };
  5027. };
  5028. /*
  5029. var can = document.createElement("canvas");
  5030. var ctx = can.getContext("2d");
  5031. var longest = 0;
  5032. var dchar = "_";
  5033. var its = 16*16*16*16 - 1;
  5034. for(var i = 0;i < its;i++){
  5035.     try{
  5036.     var hid = toHex(i);
  5037.     var char = eval("'\\u" + "0".repeat(4 - hid.length) + hid + "'");
  5038.     var len = ctx.measureText(eval("'\\u" + "0".repeat(4 - hid.length) + hid + "'"));
  5039.     if(len.width > longest){
  5040.         longest = len.width;
  5041.         dchar = char;
  5042.     };
  5043.     }catch(err){console.log(i)}
  5044. };
  5045. */
  5046. function spamSong(){if(dataB.s >= dataB.e){var ms = getFairTimeOffset();return playWithOffset(ms)}};
  5047. var display = 1;
  5048. var spamPacks = []
  5049. function G(n,d){return {n: n,v: 1,d: d || 0}};
  5050. function H(d){var arr = [];for(var k in MPP.piano.keys){arr.push(new G(k,d))};return arr};
  5051. function overPlay(d,dt){MPP.client.sendArray([{m: "n",n: new SNotes(d), t: dt || Date.now()}])};
  5052. function testPack(delay){var arr = [];var d = 0;for(var k in MPP.piano.keys){arr.push(new G(k,d));d += delay};return arr};
  5053. function S(s,d){spamPacks = [];display = d;
  5054. for(var i = 0;i < s;i++){
  5055.     spamPacks.push(new H(i * 860));
  5056.     };spamPacks.forEach(function(a,b){setTimeout(function(){MPP.client.sendArray([{m:"n",n:a,t:Date.now()}])},(s - b) * 860)});
  5057. setTimeout(function(){var sp = new H(0);for(var i = 0;i < 5;i++){MPP.client.sendArray([{m:"n",n:sp, t:Date.now()}])}},s * 860)};
Advertisement
Add Comment
Please, Sign In to add comment