Advertisement
Guest User

Untitled

a guest
May 7th, 2017
642
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 87.45 KB | None | 0 0
  1. // ==UserScript==
  2. // @name MPP autoban script
  3. // @namespace http://www.multiplayerpiano.com/script.js
  4. // @version 0.1
  5. // @description Replacement for MPP's script.js, with autobanning functionality
  6. // @author github.com/lekro
  7. // @match http://www.multiplayerpiano.com/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11.  
  12. window.MPP;
  13.  
  14. window.gPianoMutes = [];
  15.  
  16. window.gChatMutes = [];
  17.  
  18. $(function() {
  19.  
  20. var test_mode = (window.location.hash && window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i));
  21.  
  22. var gSeeOwnCursor = (window.location.hash && window.location.hash.match(/^(?:#.+)*#seeowncursor(?:#.+)*$/i));
  23.  
  24. var gMidiOutTest = (window.location.hash && window.location.hash.match(/^(?:#.+)*#midiout(?:#.+)*$/i)); // todo this is no longer needed
  25.  
  26. if (!Array.prototype.indexOf) {
  27. Array.prototype.indexOf = function(elt /*, from*/) {
  28. var len = this.length >>> 0;
  29. var from = Number(arguments[1]) || 0;
  30. from = (from < 0) ? Math.ceil(from) : Math.floor(from);
  31. if (from < 0) from += len;
  32. for (; from < len; from++) {
  33. if (from in this && this[from] === elt) return from;
  34. }
  35. return -1;
  36. };
  37. }
  38.  
  39. window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame
  40. || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
  41. || function (cb) { setTimeout(cb, 1000 / 30); };
  42.  
  43. var gSoundPath = "/mp3/";
  44. var gSoundExt = ".wav.mp3";
  45.  
  46. // Yoshify.
  47. if ((window.location.hash && window.location.hash.match(/^(?:#.+)*#Piano_Great_and_Soft(?:#.+)*$/i))) {
  48. gSoundPath = "https://dl.dropboxusercontent.com/u/216104606/GreatAndSoftPiano/";
  49. gSoundExt = ".mp3";
  50. }
  51.  
  52. if ((window.location.hash && window.location.hash.match(/^(?:#.+)*#Piano_Loud_and_Proud(?:#.+)*$/i))) {
  53. gSoundPath = "https://dl.dropboxusercontent.com/u/216104606/LoudAndProudPiano/";
  54. gSoundExt = ".mp3";
  55. }
  56.  
  57. // electrashave
  58. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#NewPiano(?:#.+)*$/i))) {
  59. gSoundPath = "https://dl.dropboxusercontent.com/u/258840068/CustomSounds/NewPiano/";
  60. gSoundExt = ".mp3";
  61. }
  62.  
  63. // Ethan Walsh
  64. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#HDPiano(?:#.+)*$/i))) {
  65. gSoundPath = "https://dl.dropboxusercontent.com/u/258840068/CustomSounds/HDPiano/";
  66. gSoundExt = ".wav";
  67. }
  68. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#Harpischord(?:#.+)*$/i))) {
  69. gSoundPath = "https://dl.dropboxusercontent.com/u/24213061/Harpischord/";
  70. gSoundExt = ".wav";
  71. }
  72. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#ClearPiano(?:#.+)*$/i))) {
  73. gSoundPath = "https://dl.dropboxusercontent.com/u/24213061/ClearPiano/";
  74. gSoundExt = ".wav";
  75. }
  76.  
  77. // Alexander Holmfjeld
  78. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#Klaver(?:#.+)*$/i))) {
  79. gSoundPath = "https://dl.dropboxusercontent.com/u/70730519/Klaver/";
  80. gSoundExt = ".wav";
  81. }
  82.  
  83. var DEFAULT_VELOCITY = 0.5;
  84. var TIMING_TARGET = 1000;
  85.  
  86.  
  87. // Utility
  88.  
  89. ////////////////////////////////////////////////////////////////
  90.  
  91.  
  92.  
  93. var Rect = function(x, y, w, h) {
  94. this.x = x;
  95. this.y = y;
  96. this.w = w;
  97. this.h = h;
  98. this.x2 = x + w;
  99. this.y2 = y + h;
  100. };
  101. Rect.prototype.contains = function(x, y) {
  102. return (x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2);
  103. };
  104.  
  105.  
  106.  
  107. // performing translation
  108.  
  109. ////////////////////////////////////////////////////////////////
  110.  
  111. var Translation = (function() {
  112. var strings = {
  113. "people are playing": {
  114. "pt": "pessoas estão jogando",
  115. "es": "personas están jugando",
  116. "ru": "человек играет",
  117. "fr": "personnes jouent",
  118. "ja": "人が遊んでいる",
  119. "de": "Leute spielen",
  120. "zh": "人被打",
  121. "nl": "mensen spelen",
  122. "pl": "osób grają",
  123. "hu": "ember játszik"
  124. },
  125. "New Room...": {
  126. "pt": "Nova Sala ...",
  127. "es": "Nueva sala de...",
  128. "ru": "Новый номер...",
  129. "ja": "新しい部屋",
  130. "zh": "新房间",
  131. "nl": "nieuwe Kamer",
  132. "hu": "új szoba"
  133. },
  134. "room name": {
  135. "pt": "nome da sala",
  136. "es": "sala de nombre",
  137. "ru": "название комнаты",
  138. "fr": "nom de la chambre",
  139. "ja": "ルーム名",
  140. "de": "Raumnamen",
  141. "zh": "房间名称",
  142. "nl": "kamernaam",
  143. "pl": "nazwa pokój",
  144. "hu": "szoba neve"
  145. },
  146. "Visible (open to everyone)": {
  147. "pt": "Visível (aberto a todos)",
  148. "es": "Visible (abierto a todo el mundo)",
  149. "ru": "Visible (открытый для всех)",
  150. "fr": "Visible (ouvert à tous)",
  151. "ja": "目に見える(誰にでも開いている)",
  152. "de": "Sichtbar (offen für alle)",
  153. "zh": "可见(向所有人开放)",
  154. "nl": "Zichtbaar (open voor iedereen)",
  155. "pl": "Widoczne (otwarte dla wszystkich)",
  156. "hu": "Látható (nyitott mindenki számára)"
  157. },
  158. "Enable Chat": {
  159. "pt": "Ativar bate-papo",
  160. "es": "Habilitar chat",
  161. "ru": "Включить чат",
  162. "fr": "Activer discuter",
  163. "ja": "チャットを有効にする",
  164. "de": "aktivieren Sie chatten",
  165. "zh": "启用聊天",
  166. "nl": "Chat inschakelen",
  167. "pl": "Włącz czat",
  168. "hu": "a csevegést"
  169. },
  170. "Play Alone": {
  171. "pt": "Jogar Sozinho",
  172. "es": "Jugar Solo",
  173. "ru": "Играть в одиночку",
  174. "fr": "Jouez Seul",
  175. "ja": "一人でプレイ",
  176. "de": "Alleine Spielen",
  177. "zh": "独自玩耍",
  178. "nl": "Speel Alleen",
  179. "pl": "Zagraj sam",
  180. "hu": "Játssz egyedül"
  181. }
  182. // todo: it, tr, th, sv, ar, fi, nb, da, sv, he, cs, ko, ro, vi, id, nb, el, sk, bg, lt, sl, hr
  183. // todo: Connecting, Offline mode, input placeholder, Notifications
  184. };
  185.  
  186. var setLanguage = function(lang) {
  187. language = lang
  188. };
  189.  
  190. var getLanguage = function() {
  191. if(window.navigator && navigator.language && navigator.language.length >= 2) {
  192. return navigator.language.substr(0, 2).toLowerCase();
  193. } else {
  194. return "en";
  195. }
  196. };
  197.  
  198. var get = function(text, lang) {
  199. if(typeof lang === "undefined") lang = language;
  200. var row = strings[text];
  201. if(row == undefined) return text;
  202. var string = row[lang];
  203. if(string == undefined) return text;
  204. return string;
  205. };
  206.  
  207. var perform = function(lang) {
  208. if(typeof lang === "undefined") lang = language;
  209. $(".translate").each(function(i, ele) {
  210. var th = $(this);
  211. if(ele.tagName && ele.tagName.toLowerCase() == "input") {
  212. if(typeof ele.placeholder != "undefined") {
  213. th.attr("placeholder", get(th.attr("placeholder"), lang))
  214. }
  215. } else {
  216. th.text(get(th.text(), lang));
  217. }
  218. });
  219. };
  220.  
  221. var language = getLanguage();
  222.  
  223. return {
  224. setLanguage: setLanguage,
  225. getLanguage: getLanguage,
  226. get: get,
  227. perform: perform
  228. };
  229. })();
  230.  
  231. Translation.perform();
  232.  
  233.  
  234.  
  235. // AudioEngine classes
  236.  
  237. ////////////////////////////////////////////////////////////////
  238.  
  239. var AudioEngine = function() {
  240. };
  241.  
  242. AudioEngine.prototype.init = function(cb) {
  243. this.volume = 0.6;
  244. this.sounds = {};
  245. return this;
  246. };
  247.  
  248. AudioEngine.prototype.load = function(id, url, cb) {
  249. };
  250.  
  251. AudioEngine.prototype.play = function() {
  252. };
  253.  
  254. AudioEngine.prototype.stop = function() {
  255. };
  256.  
  257. AudioEngine.prototype.setVolume = function(vol) {
  258. this.volume = vol;
  259. };
  260.  
  261.  
  262. AudioEngineWeb = function() {
  263. this.threshold = 1000;
  264. this.worker = new Worker("/workerTimer.js");
  265. var self = this;
  266. this.worker.onmessage = function(event)
  267. {
  268. if(event.data.args)
  269. if(event.data.args.action==0)
  270. {
  271. self.actualPlay(event.data.args.id, event.data.args.vol, event.data.args.time, event.data.args.part_id);
  272. }
  273. else
  274. {
  275. self.actualStop(event.data.args.id, event.data.args.time, event.data.args.part_id);
  276. }
  277. }
  278. };
  279.  
  280. AudioEngineWeb.prototype = new AudioEngine();
  281.  
  282. AudioEngineWeb.prototype.init = function(cb) {
  283. AudioEngine.prototype.init.call(this);
  284.  
  285. this.context = new AudioContext();
  286.  
  287. this.masterGain = this.context.createGain();
  288. this.masterGain.connect(this.context.destination);
  289. this.masterGain.gain.value = this.volume;
  290.  
  291. this.limiterNode = this.context.createDynamicsCompressor();
  292. this.limiterNode.threshold.value = -10;
  293. this.limiterNode.knee.value = 0;
  294. this.limiterNode.ratio.value = 20;
  295. this.limiterNode.attack.value = 0;
  296. this.limiterNode.release.value = 0.1;
  297. this.limiterNode.connect(this.masterGain);
  298.  
  299. // for synth mix
  300. this.pianoGain = this.context.createGain();
  301. this.pianoGain.gain.value = 0.5;
  302. this.pianoGain.connect(this.limiterNode);
  303. this.synthGain = this.context.createGain();
  304. this.synthGain.gain.value = 0.5;
  305. this.synthGain.connect(this.limiterNode);
  306.  
  307. this.playings = {};
  308.  
  309. if(cb) setTimeout(cb, 0);
  310. return this;
  311. };
  312.  
  313. AudioEngineWeb.prototype.load = function(id, url, cb) {
  314. var audio = this;
  315. var req = new XMLHttpRequest();
  316. req.open("GET", url);
  317. req.responseType = "arraybuffer";
  318. req.addEventListener("readystatechange", function(evt) {
  319. if(req.readyState !== 4) return;
  320. try {
  321. audio.context.decodeAudioData(req.response, function(buffer) {
  322. audio.sounds[id] = buffer;
  323. if(cb) cb();
  324. });
  325. } catch(e) {
  326. /*throw new Error(e.message
  327. + " / id: " + id
  328. + " / url: " + url
  329. + " / status: " + req.status
  330. + " / ArrayBuffer: " + (req.response instanceof ArrayBuffer)
  331. + " / byteLength: " + (req.response && req.response.byteLength ? req.response.byteLength : "undefined"));*/
  332. new Notification({id: "audio-download-error", title: "Problem", text: "For some reason, an audio download failed with a status of " + req.status + ". ",
  333. target: "#piano", duration: 10000});
  334. }
  335. });
  336. req.send();
  337. };
  338.  
  339. AudioEngineWeb.prototype.actualPlay = function(id, vol, time, part_id) { //the old play(), but with time insted of delay_ms.
  340. if(!this.sounds.hasOwnProperty(id)) return;
  341. var source = this.context.createBufferSource();
  342. source.buffer = this.sounds[id];
  343. var gain = this.context.createGain();
  344. gain.gain.value = vol;
  345. source.connect(gain);
  346. gain.connect(this.pianoGain);
  347. source.start(time);
  348. // Patch from ste-art remedies stuttering under heavy load
  349. if(this.playings[id]) {
  350. var playing = this.playings[id];
  351. playing.gain.gain.setValueAtTime(playing.gain.gain.value, time);
  352. playing.gain.gain.linearRampToValueAtTime(0.0, time + 0.2);
  353. playing.source.stop(time + 0.21);
  354. if(enableSynth && playing.voice) {
  355. playing.voice.stop(time);
  356. }
  357. }
  358. this.playings[id] = {"source": source, "gain": gain, "part_id": part_id};
  359.  
  360. if(enableSynth) {
  361. this.playings[id].voice = new synthVoice(id, time);
  362. }
  363. }
  364.  
  365. AudioEngineWeb.prototype.play = function(id, vol, delay_ms, part_id)
  366. {
  367. if(!this.sounds.hasOwnProperty(id)) return;
  368. var time = this.context.currentTime + (delay_ms / 1000); //calculate time on note receive.
  369. var delay = delay_ms - this.threshold;
  370. if(delay<=0) this.actualPlay(id, vol, time, part_id);
  371. else {
  372. 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.
  373. }
  374. }
  375.  
  376. AudioEngineWeb.prototype.actualStop = function(id, time, part_id) {
  377. if(this.playings.hasOwnProperty(id) && this.playings[id] && this.playings[id].part_id === part_id) {
  378. var gain = this.playings[id].gain.gain;
  379. gain.setValueAtTime(gain.value, time);
  380. gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16);
  381. gain.linearRampToValueAtTime(0.0, time + 0.4);
  382. this.playings[id].source.stop(time + 0.41);
  383.  
  384.  
  385. if(this.playings[id].voice) {
  386. this.playings[id].voice.stop(time);
  387. }
  388.  
  389. this.playings[id] = null;
  390. }
  391. };
  392.  
  393. AudioEngineWeb.prototype.stop = function(id, delay_ms, part_id) {
  394. var time = this.context.currentTime + (delay_ms / 1000);
  395. var delay = delay_ms - this.threshold;
  396. if(delay<=0) this.actualStop(id, time, part_id);
  397. else {
  398. this.worker.postMessage({delay:delay,args:{action:1/*stop*/, id:id, time:time, part_id:part_id}});
  399. }
  400. };
  401.  
  402. AudioEngineWeb.prototype.setVolume = function(vol) {
  403. AudioEngine.prototype.setVolume.call(this, vol);
  404. this.masterGain.gain.value = this.volume;
  405. };
  406.  
  407.  
  408. // VolumeSlider inst
  409.  
  410. ////////////////////////////////////////////////////////////////
  411.  
  412. var VolumeSlider = function(ele, cb) {
  413. this.rootElement = ele;
  414. this.cb = cb;
  415. var range = document.createElement("input");
  416. try {
  417. range.type = "range";
  418. } catch(e) {
  419. // hello, IE9
  420. }
  421. if(range.min !== undefined) {
  422. this.range = range;
  423. this.rootElement.appendChild(range);
  424. range.className = "volume-slider";
  425. range.min = "0.0";
  426. range.max = "1.0";
  427. range.step = "0.01";
  428. $(range).on("change", function(evt) {
  429. cb(range.value);
  430. });
  431. } else {
  432. if(window.console) console.log("warn: no slider");
  433. // todo
  434. }
  435. };
  436.  
  437. VolumeSlider.prototype.set = function(v) {
  438. if(this.range !== undefined) {
  439. this.range.value = v;
  440. } else {
  441. // todo
  442. }
  443. };
  444.  
  445.  
  446. // Renderer classes
  447.  
  448. ////////////////////////////////////////////////////////////////
  449.  
  450. var Renderer = function() {
  451. };
  452.  
  453. Renderer.prototype.init = function(piano) {
  454. this.piano = piano;
  455. this.resize();
  456. return this;
  457. };
  458.  
  459. Renderer.prototype.resize = function(width, height) {
  460. if(typeof width == "undefined") width = $(this.piano.rootElement).width();
  461. if(typeof height == "undefined") height = Math.floor(width * 0.2);
  462. $(this.piano.rootElement).css({"height": height + "px", marginTop: Math.floor($(window).height() / 2 - height / 2) + "px"});
  463. this.width = width;
  464. this.height = height;
  465. };
  466.  
  467. Renderer.prototype.visualize = function(key, color) {
  468. };
  469.  
  470.  
  471.  
  472.  
  473. var DOMRenderer = function() {
  474. Renderer.call(this);
  475. };
  476.  
  477. DOMRenderer.prototype = new Renderer();
  478.  
  479. DOMRenderer.prototype.init = function(piano) {
  480. // create keys in dom
  481. for(var i in piano.keys) {
  482. if(!piano.keys.hasOwnProperty(i)) continue;
  483. var key = piano.keys[i];
  484. var ele = document.createElement("div");
  485. key.domElement = ele;
  486. piano.rootElement.appendChild(ele);
  487. // "key sharp cs cs2"
  488. ele.note = key.note;
  489. ele.id = key.note;
  490. ele.className = "key " + (key.sharp ? "sharp " : " ") + key.baseNote + " " + key.note + " loading";
  491. var table = $('<table width="100%" height="100%" style="pointer-events:none"></table>');
  492. var td = $('<td valign="bottom"></td>');
  493. table.append(td);
  494. td.valign = "bottom";
  495. $(ele).append(table);
  496. }
  497. // add event listeners
  498. var mouse_down = false;
  499. $(piano.rootElement).mousedown(function(event) {
  500. // todo: IE10 doesn't support the pointer-events css rule on the "blips"
  501. var ele = event.target;
  502. if($(ele).hasClass("key") && piano.keys.hasOwnProperty(ele.note)) {
  503. var key = piano.keys[ele.note];
  504. press(key.note);
  505. mouse_down = true;
  506. event.stopPropagation();
  507. };
  508. //event.preventDefault();
  509. });
  510. piano.rootElement.addEventListener("touchstart", function(event) {
  511. for(var i in event.changedTouches) {
  512. var ele = event.changedTouches[i].target;
  513. if($(ele).hasClass("key") && piano.keys.hasOwnProperty(ele.note)) {
  514. var key = piano.keys[ele.note];
  515. press(key.note);
  516. mouse_down = true;
  517. event.stopPropagation();
  518. }
  519. }
  520. //event.preventDefault();
  521. }, false);
  522. $(window).mouseup(function(event) {
  523. mouse_down = false;
  524. });
  525. /*$(piano.rootElement).mouseover(function(event) {
  526. if(!mouse_down) return;
  527. var ele = event.target;
  528. if($(ele).hasClass("key") && piano.keys.hasOwnProperty(ele.note)) {
  529. var key = piano.keys[ele.note];
  530. press(key.note);
  531. }
  532. });*/
  533.  
  534. Renderer.prototype.init.call(this, piano);
  535. return this;
  536. };
  537.  
  538. DOMRenderer.prototype.resize = function(width, height) {
  539. Renderer.prototype.resize.call(this, width, height);
  540. };
  541.  
  542. DOMRenderer.prototype.visualize = function(key, color) {
  543. var k = $(key.domElement);
  544. k.addClass("play");
  545. setTimeout(function(){
  546. k.removeClass("play");
  547. }, 100);
  548. // "blips"
  549. var d = $('<div style="width:100%;height:10%;margin:0;padding:0">&nbsp;</div>');
  550. d.css("background", color);
  551. k.find("td").append(d);
  552. d.fadeOut(1000, function(){
  553. d.remove();
  554. });
  555. };
  556.  
  557.  
  558.  
  559.  
  560. var CanvasRenderer = function() {
  561. Renderer.call(this);
  562. };
  563.  
  564. CanvasRenderer.prototype = new Renderer();
  565.  
  566. CanvasRenderer.prototype.init = function(piano) {
  567. this.canvas = document.createElement("canvas");
  568. this.ctx = this.canvas.getContext("2d");
  569. piano.rootElement.appendChild(this.canvas);
  570.  
  571. Renderer.prototype.init.call(this, piano); // calls resize()
  572.  
  573. // create render loop
  574. var self = this;
  575. var render = function() {
  576. self.redraw();
  577. requestAnimationFrame(render);
  578. };
  579. requestAnimationFrame(render);
  580.  
  581. // add event listeners
  582. var mouse_down = false;
  583. var last_key = null;
  584. $(piano.rootElement).mousedown(function(event) {
  585. mouse_down = true;
  586. //event.stopPropagation();
  587. event.preventDefault();
  588.  
  589. var pos = CanvasRenderer.translateMouseEvent(event);
  590. var hit = self.getHit(pos.x, pos.y);
  591. if(hit) {
  592. press(hit.key.note, hit.v);
  593. last_key = hit.key;
  594. }
  595. });
  596. piano.rootElement.addEventListener("touchstart", function(event) {
  597. mouse_down = true;
  598. //event.stopPropagation();
  599. event.preventDefault();
  600. for(var i in event.changedTouches) {
  601. var pos = CanvasRenderer.translateMouseEvent(event.changedTouches[i]);
  602. var hit = self.getHit(pos.x, pos.y);
  603. if(hit) {
  604. press(hit.key.note, hit.v);
  605. last_key = hit.key;
  606. }
  607. }
  608. }, false);
  609. $(window).mouseup(function(event) {
  610. if(last_key) {
  611. release(last_key.note);
  612. }
  613. mouse_down = false;
  614. last_key = null;
  615. });
  616. /*$(piano.rootElement).mousemove(function(event) {
  617. if(!mouse_down) return;
  618. var pos = CanvasRenderer.translateMouseEvent(event);
  619. var hit = self.getHit(pos.x, pos.y);
  620. if(hit && hit.key != last_key) {
  621. press(hit.key.note, hit.v);
  622. last_key = hit.key;
  623. }
  624. });*/
  625.  
  626. return this;
  627. };
  628.  
  629. CanvasRenderer.prototype.resize = function(width, height) {
  630. Renderer.prototype.resize.call(this, width, height);
  631. if(this.width < 52 * 2) this.width = 52 * 2;
  632. if(this.height < this.width * 0.2) this.height = Math.floor(this.width * 0.2);
  633. this.canvas.width = this.width;
  634. this.canvas.height = this.height;
  635.  
  636. // calculate key sizes
  637. this.whiteKeyWidth = Math.floor(this.width / 52);
  638. this.whiteKeyHeight = Math.floor(this.height * 0.9);
  639. this.blackKeyWidth = Math.floor(this.whiteKeyWidth * 0.75);
  640. this.blackKeyHeight = Math.floor(this.height * 0.5);
  641.  
  642. this.blackKeyOffset = Math.floor(this.whiteKeyWidth - (this.blackKeyWidth / 2));
  643. this.keyMovement = Math.floor(this.whiteKeyHeight * 0.015);
  644.  
  645. this.whiteBlipWidth = Math.floor(this.whiteKeyWidth * 0.7);
  646. this.whiteBlipHeight = Math.floor(this.whiteBlipWidth * 0.8);
  647. this.whiteBlipX = Math.floor((this.whiteKeyWidth - this.whiteBlipWidth) / 2);
  648. this.whiteBlipY = Math.floor(this.whiteKeyHeight - this.whiteBlipHeight * 1.2);
  649. this.blackBlipWidth = Math.floor(this.blackKeyWidth * 0.7);
  650. this.blackBlipHeight = Math.floor(this.blackBlipWidth * 0.8);
  651. this.blackBlipY = Math.floor(this.blackKeyHeight - this.blackBlipHeight * 1.2);
  652. this.blackBlipX = Math.floor((this.blackKeyWidth - this.blackBlipWidth) / 2);
  653.  
  654. // prerender white key
  655. this.whiteKeyRender = document.createElement("canvas");
  656. this.whiteKeyRender.width = this.whiteKeyWidth;
  657. this.whiteKeyRender.height = this.height + 10;
  658. var ctx = this.whiteKeyRender.getContext("2d");
  659. if(ctx.createLinearGradient) {
  660. var gradient = ctx.createLinearGradient(0, 0, 0, this.whiteKeyHeight);
  661. gradient.addColorStop(0, "#eee");
  662. gradient.addColorStop(0.75, "#fff");
  663. gradient.addColorStop(1, "#dad4d4");
  664. ctx.fillStyle = gradient;
  665. } else {
  666. ctx.fillStyle = "#fff";
  667. }
  668. ctx.strokeStyle = "#000";
  669. ctx.lineJoin = "round";
  670. ctx.lineCap = "round";
  671. ctx.lineWidth = 10;
  672. ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
  673. ctx.lineWidth = 4;
  674. ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
  675.  
  676. // prerender black key
  677. this.blackKeyRender = document.createElement("canvas");
  678. this.blackKeyRender.width = this.blackKeyWidth + 10;
  679. this.blackKeyRender.height = this.blackKeyHeight + 10;
  680. var ctx = this.blackKeyRender.getContext("2d");
  681. if(ctx.createLinearGradient) {
  682. var gradient = ctx.createLinearGradient(0, 0, 0, this.blackKeyHeight);
  683. gradient.addColorStop(0, "#000");
  684. gradient.addColorStop(1, "#444");
  685. ctx.fillStyle = gradient;
  686. } else {
  687. ctx.fillStyle = "#000";
  688. }
  689. ctx.strokeStyle = "#222";
  690. ctx.lineJoin = "round";
  691. ctx.lineCap = "round";
  692. ctx.lineWidth = 8;
  693. ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
  694. ctx.lineWidth = 4;
  695. ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
  696.  
  697. // prerender shadows
  698. this.shadowRender = [];
  699. var y = -this.canvas.height * 2;
  700. for(var j = 0; j < 2; j++) {
  701. var canvas = document.createElement("canvas");
  702. this.shadowRender[j] = canvas;
  703. canvas.width = this.canvas.width;
  704. canvas.height = this.canvas.height;
  705. var ctx = canvas.getContext("2d");
  706. var sharp = j ? true : false;
  707. ctx.lineJoin = "round";
  708. ctx.lineCap = "round";
  709. ctx.lineWidth = 1;
  710. ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
  711. ctx.shadowBlur = this.keyMovement * 3;
  712. ctx.shadowOffsetY = -y + this.keyMovement;
  713. if(sharp) {
  714. ctx.shadowOffsetX = this.keyMovement;
  715. } else {
  716. ctx.shadowOffsetX = 0;
  717. ctx.shadowOffsetY = -y + this.keyMovement;
  718. }
  719. for(var i in this.piano.keys) {
  720. if(!this.piano.keys.hasOwnProperty(i)) continue;
  721. var key = this.piano.keys[i];
  722. if(key.sharp != sharp) continue;
  723.  
  724. if(key.sharp) {
  725. ctx.fillRect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
  726. y + ctx.lineWidth / 2,
  727. this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
  728. } else {
  729. ctx.fillRect(this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
  730. y + ctx.lineWidth / 2,
  731. this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
  732. }
  733. }
  734. }
  735.  
  736. // update key rects
  737. for(var i in this.piano.keys) {
  738. if(!this.piano.keys.hasOwnProperty(i)) continue;
  739. var key = this.piano.keys[i];
  740. if(key.sharp) {
  741. key.rect = new Rect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial, 0,
  742. this.blackKeyWidth, this.blackKeyHeight);
  743. } else {
  744. key.rect = new Rect(this.whiteKeyWidth * key.spatial, 0,
  745. this.whiteKeyWidth, this.whiteKeyHeight);
  746. }
  747. }
  748. };
  749.  
  750. CanvasRenderer.prototype.visualize = function(key, color) {
  751. key.timePlayed = Date.now();
  752. key.blips.push({"time": key.timePlayed, "color": color});
  753. };
  754.  
  755. CanvasRenderer.prototype.redraw = function() {
  756. var now = Date.now();
  757. var timeLoadedEnd = now - 1000;
  758. var timePlayedEnd = now - 100;
  759. var timeBlipEnd = now - 1000;
  760.  
  761. this.ctx.save();
  762. this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  763. // draw all keys
  764. for(var j = 0; j < 2; j++) {
  765. this.ctx.globalAlpha = 1.0;
  766. this.ctx.drawImage(this.shadowRender[j], 0, 0);
  767. var sharp = j ? true : false;
  768. for(var i in this.piano.keys) {
  769. if(!this.piano.keys.hasOwnProperty(i)) continue;
  770. var key = this.piano.keys[i];
  771. if(key.sharp != sharp) continue;
  772.  
  773. if(!key.loaded) {
  774. this.ctx.globalAlpha = 0.2;
  775. } else if(key.timeLoaded > timeLoadedEnd) {
  776. this.ctx.globalAlpha = ((now - key.timeLoaded) / 1000) * 0.8 + 0.2;
  777. } else {
  778. this.ctx.globalAlpha = 1.0;
  779. }
  780. var y = 0;
  781. if(key.timePlayed > timePlayedEnd) {
  782. y = Math.floor(this.keyMovement - (((now - key.timePlayed) / 100) * this.keyMovement));
  783. }
  784. var x = Math.floor(key.sharp ? this.blackKeyOffset + this.whiteKeyWidth * key.spatial
  785. : this.whiteKeyWidth * key.spatial);
  786. var image = key.sharp ? this.blackKeyRender : this.whiteKeyRender;
  787. this.ctx.drawImage(image, x, y);
  788.  
  789. // render blips
  790. if(key.blips.length) {
  791. var alpha = this.ctx.globalAlpha;
  792. var w, h;
  793. if(key.sharp) {
  794. x += this.blackBlipX;
  795. y = this.blackBlipY;
  796. w = this.blackBlipWidth;
  797. h = this.blackBlipHeight;
  798. } else {
  799. x += this.whiteBlipX;
  800. y = this.whiteBlipY;
  801. w = this.whiteBlipWidth;
  802. h = this.whiteBlipHeight;
  803. }
  804. for(var b = 0; b < key.blips.length; b++) {
  805. var blip = key.blips[b];
  806. if(blip.time > timeBlipEnd) {
  807. this.ctx.fillStyle = blip.color;
  808. this.ctx.globalAlpha = alpha - ((now - blip.time) / 1000);
  809. this.ctx.fillRect(x, y, w, h);
  810. } else {
  811. key.blips.splice(b, 1);
  812. --b;
  813. }
  814. y -= Math.floor(h * 1.1);
  815. }
  816. }
  817. }
  818. }
  819. this.ctx.restore();
  820. };
  821.  
  822. CanvasRenderer.prototype.getHit = function(x, y) {
  823. for(var j = 0; j < 2; j++) {
  824. var sharp = j ? false : true; // black keys first
  825. for(var i in this.piano.keys) {
  826. if(!this.piano.keys.hasOwnProperty(i)) continue;
  827. var key = this.piano.keys[i];
  828. if(key.sharp != sharp) continue;
  829. if(key.rect.contains(x, y)) {
  830. var v = y / (key.sharp ? this.blackKeyHeight : this.whiteKeyHeight);
  831. v += 0.25;
  832. v *= DEFAULT_VELOCITY;
  833. if(v > 1.0) v = 1.0;
  834. return {"key": key, "v": v};
  835. }
  836. }
  837. }
  838. return null;
  839. };
  840.  
  841.  
  842. CanvasRenderer.isSupported = function() {
  843. var canvas = document.createElement("canvas");
  844. return !!(canvas.getContext && canvas.getContext("2d"));
  845. };
  846.  
  847. CanvasRenderer.translateMouseEvent = function(evt) {
  848. var element = evt.target;
  849. var offx = 0;
  850. var offy = 0;
  851. do {
  852. if(!element) break; // wtf, wtf?
  853. offx += element.offsetLeft;
  854. offy += element.offsetTop;
  855. } while(element = element.offsetParent);
  856. return {
  857. x: evt.pageX - offx,
  858. y: evt.pageY - offy
  859. }
  860. };
  861.  
  862.  
  863. // Pianoctor
  864.  
  865. ////////////////////////////////////////////////////////////////
  866.  
  867. var PianoKey = function(note, octave) {
  868. this.note = note + octave;
  869. this.baseNote = note;
  870. this.octave = octave;
  871. this.sharp = note.indexOf("s") != -1;
  872. this.loaded = false;
  873. this.timeLoaded = 0;
  874. this.domElement = null;
  875. this.timePlayed = 0;
  876. this.blips = [];
  877. };
  878.  
  879. var Piano = function(rootElement) {
  880.  
  881. var piano = this;
  882. piano.rootElement = rootElement;
  883. piano.keys = {};
  884.  
  885. var white_spatial = 0;
  886. var black_spatial = 0;
  887. var black_it = 0;
  888. var black_lut = [2, 1, 2, 1, 1];
  889. var addKey = function(note, octave) {
  890. var key = new PianoKey(note, octave);
  891. piano.keys[key.note] = key;
  892. if(key.sharp) {
  893. key.spatial = black_spatial;
  894. black_spatial += black_lut[black_it % 5];
  895. ++black_it;
  896. } else {
  897. key.spatial = white_spatial;
  898. ++white_spatial;
  899. }
  900. }
  901. if(test_mode) {
  902. addKey("c", 2);
  903. } else {
  904. addKey("a", -1);
  905. addKey("as", -1);
  906. addKey("b", -1);
  907. var notes = "c cs d ds e f fs g gs a as b".split(" ");
  908. for(var oct = 0; oct < 7; oct++) {
  909. for(var i in notes) {
  910. addKey(notes[i], oct);
  911. }
  912. }
  913. addKey("c", 7);
  914. }
  915.  
  916.  
  917. var render_engine = CanvasRenderer.isSupported() ? CanvasRenderer : DOMRenderer;
  918. this.renderer = new render_engine().init(this);
  919.  
  920. window.addEventListener("resize", function() {
  921. piano.renderer.resize();
  922. });
  923.  
  924.  
  925. window.AudioContext = window.AudioContext || window.webkitAudioContext || undefined;
  926. var audio_engine = AudioEngineWeb;
  927.  
  928. this.audio = new audio_engine().init(function() {
  929. for(var i in piano.keys) {
  930. if(!piano.keys.hasOwnProperty(i)) continue;
  931. (function() {
  932. var key = piano.keys[i];
  933. piano.audio.load(key.note, gSoundPath + key.note + gSoundExt, function() {
  934. key.loaded = true;
  935. key.timeLoaded = Date.now();
  936. if(key.domElement) // todo: move this to renderer somehow
  937. $(key.domElement).removeClass("loading");
  938. });
  939. })();
  940. }
  941. });
  942. };
  943.  
  944. Piano.prototype.play = function(note, vol, participant, delay_ms) {
  945. if(!this.keys.hasOwnProperty(note)) return;
  946. var key = this.keys[note];
  947. if(key.loaded) this.audio.play(key.note, vol, delay_ms, participant.id);
  948. if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, vol * 100, delay_ms);
  949. var self = this;
  950. var jq_namediv = $(typeof participant == "undefined" ? null : participant.nameDiv);
  951. if(jq_namediv) {
  952. setTimeout(function() {
  953. self.renderer.visualize(key, typeof participant == "undefined" ? "yellow" : (participant.color || "#777"));
  954. jq_namediv.addClass("play");
  955. setTimeout(function() {
  956. jq_namediv.removeClass("play");
  957. }, 30);
  958. }, delay_ms);
  959. }
  960. };
  961.  
  962. Piano.prototype.stop = function(note, participant, delay_ms) {
  963. if(!this.keys.hasOwnProperty(note)) return;
  964. var key = this.keys[note];
  965. if(key.loaded) this.audio.stop(key.note, delay_ms, participant.id);
  966. if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, 0, delay_ms);
  967. };
  968.  
  969. var gPiano = new Piano(document.getElementById("piano"));
  970.  
  971.  
  972.  
  973.  
  974.  
  975.  
  976.  
  977. var gAutoSustain = false; //!(window.location.hash && window.location.hash.match(/^(?:#.+)*#sustain(?:#.+)*$/));
  978. var gSustain = false;
  979.  
  980. var gHeldNotes = {};
  981. var gSustainedNotes = {};
  982.  
  983.  
  984. function press(id, vol) {
  985. if(!gClient.preventsPlaying() && gNoteQuota.spend(1)) {
  986. gHeldNotes[id] = true;
  987. gSustainedNotes[id] = true;
  988. gPiano.play(id, vol !== undefined ? vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), 0);
  989. gClient.startNote(id, vol);
  990. }
  991. }
  992.  
  993. function release(id) {
  994. if(gHeldNotes[id]) {
  995. gHeldNotes[id] = false;
  996. if((gAutoSustain || gSustain) && !enableSynth) {
  997. gSustainedNotes[id] = true;
  998. } else {
  999. if(gNoteQuota.spend(1)) {
  1000. gPiano.stop(id, gClient.getOwnParticipant(), 0);
  1001. gClient.stopNote(id);
  1002. gSustainedNotes[id] = false;
  1003. }
  1004. }
  1005. }
  1006. }
  1007.  
  1008. function pressSustain() {
  1009. gSustain = true;
  1010. }
  1011.  
  1012. function releaseSustain() {
  1013. gSustain = false;
  1014. if(!gAutoSustain) {
  1015. for(var id in gSustainedNotes) {
  1016. if(gSustainedNotes.hasOwnProperty(id) && gSustainedNotes[id] && !gHeldNotes[id]) {
  1017. gSustainedNotes[id] = false;
  1018. if(gNoteQuota.spend(1)) {
  1019. gPiano.stop(id, gClient.getOwnParticipant(), 0);
  1020. gClient.stopNote(id);
  1021. }
  1022. }
  1023. }
  1024. }
  1025. }
  1026.  
  1027.  
  1028.  
  1029.  
  1030. // internet science
  1031.  
  1032. ////////////////////////////////////////////////////////////////
  1033.  
  1034. var channel_id = decodeURIComponent(window.location.pathname);
  1035. if(channel_id.substr(0, 1) == "/") channel_id = channel_id.substr(1);
  1036. if(channel_id == "") channel_id = "lobby";
  1037.  
  1038. var wssport = window.location.hostname == "www.multiplayerpiano.com" ? 443 : 8080;
  1039. var gClient = new Client("ws://" + window.location.hostname + ":" + wssport);
  1040. gClient.setChannel(channel_id);
  1041. gClient.start();
  1042.  
  1043. var autoban_function = function(person) {
  1044.  
  1045.  
  1046. // The autobanning is here!!!!!
  1047.  
  1048. // Load name blacklist
  1049. var blocked_names_json = localStorage.getItem("blocked_names");
  1050. var blocked_names;
  1051. if (blocked_names_json === null || blocked_names_json.length === 0) {
  1052. blocked_names = new Array();
  1053. } else {
  1054. blocked_names = JSON.parse(blocked_names_json);
  1055. }
  1056.  
  1057. // Load uuid blacklist
  1058. var blocked_uuids_json = localStorage.getItem("blocked_uuids");
  1059. var blocked_uuids;
  1060. if (blocked_uuids_json === null || blocked_uuids_json.length === 0) {
  1061. blocked_uuids = new Array();
  1062. } else {
  1063. blocked_uuids = JSON.parse(blocked_uuids_json);
  1064. }
  1065.  
  1066. matching_blocks = new Set();
  1067. for (var i = 0; i < blocked_names.length; i++) {
  1068. if (person.name.includes(blocked_names[i])) {
  1069. matching_blocks.add(blocked_names[i]);
  1070. }
  1071.  
  1072. }
  1073. for (var i = 0; i < blocked_uuids.length; i++) {
  1074. if (person._id === blocked_uuids[i]) {
  1075. matching_blocks.add("uuid blacklist");
  1076. }
  1077.  
  1078. }
  1079. if (matching_blocks.size !== 0) {
  1080. //chat.send(person.name + " " + person._id + " - match on "+Array.from(matching_blocks)+"");
  1081. gClient.sendArray([{m: "kickban", _id: person._id, ms: 600000}]);
  1082. }
  1083. };
  1084.  
  1085.  
  1086. // Setting status
  1087. (function() {
  1088. gClient.on("status", function(status) {
  1089. $("#status").text(status);
  1090. });
  1091. gClient.on("count", function(count) {
  1092. if(count > 0) {
  1093. $("#status").html('<span class="number">'+count+'</span> '+(count==1? 'user' : 'users')+' in room');
  1094. document.title = "Piano (" + count + ")";
  1095. } else {
  1096. document.title = "[offline]";
  1097. }
  1098. });
  1099. })();
  1100.  
  1101. // MODIFICATION: NAME DATABASE
  1102. // Store names for uuids.
  1103. // Data structure for holding names: map of uuids->arrays.
  1104. // First element of each inside array will be the uuid concerned.
  1105. // We will actually keep this loaded, and only save when necessary
  1106.  
  1107. // original script now
  1108. // Handle changes to participants
  1109. (function() {
  1110. gClient.on("participant added", function(part) {
  1111.  
  1112. part.displayX = 150;
  1113. part.displayY = 50;
  1114.  
  1115. // add nameDiv
  1116. var div = document.createElement("div");
  1117. div.className = "name";
  1118. div.participantId = part.id;
  1119. div.textContent = part.name || "";
  1120. div.style.backgroundColor = part.color || "#777";
  1121. if(gClient.participantId === part.id) {
  1122. $(div).addClass("me");
  1123. }
  1124. if(gClient.channel && gClient.channel.crown && gClient.channel.crown.participantId === part.id) {
  1125. $(div).addClass("owner");
  1126. }
  1127. if(gPianoMutes.indexOf(part._id) !== -1) {
  1128. $(part.nameDiv).addClass("muted-notes");
  1129. }
  1130. if(gChatMutes.indexOf(part._id) !== -1) {
  1131. $(part.nameDiv).addClass("muted-chat");
  1132. }
  1133. div.style.display = "none";
  1134. part.nameDiv = $("#names")[0].appendChild(div);
  1135. $(part.nameDiv).fadeIn(2000);
  1136.  
  1137. // sort names
  1138. var arr = $("#names .name");
  1139. arr.sort(function(a, b) {
  1140. a = a.style.backgroundColor; // todo: sort based on user id instead
  1141. b = b.style.backgroundColor;
  1142. if (a > b) return 1;
  1143. else if (a < b) return -1;
  1144. else return 0;
  1145. });
  1146. $("#names").html(arr);
  1147.  
  1148. // add cursorDiv
  1149. if(gClient.participantId !== part.id || gSeeOwnCursor) {
  1150. var div = document.createElement("div");
  1151. div.className = "cursor";
  1152. div.style.display = "none";
  1153. part.cursorDiv = $("#cursors")[0].appendChild(div);
  1154. $(part.cursorDiv).fadeIn(2000);
  1155.  
  1156. var div = document.createElement("div");
  1157. div.className = "name";
  1158. div.style.backgroundColor = part.color || "#777"
  1159. div.textContent = part.name || "";
  1160. part.cursorDiv.appendChild(div);
  1161.  
  1162. } else {
  1163. part.cursorDiv = undefined;
  1164. }
  1165.  
  1166.  
  1167. });
  1168. gClient.on("participant removed", function(part) {
  1169. // remove nameDiv
  1170. console.log("user management: "+part._id+" ("+part.name+") isn't in your room now.");
  1171. var nd = $(part.nameDiv);
  1172. var cd = $(part.cursorDiv);
  1173. cd.fadeOut(2000);
  1174. nd.fadeOut(2000, function() {
  1175. nd.remove();
  1176. cd.remove();
  1177. part.nameDiv = undefined;
  1178. part.cursorDiv = undefined;
  1179. });
  1180. });
  1181. gClient.on("participant update", function(part) {
  1182. var name = part.name || "";
  1183. var color = part.color || "#777";
  1184. part.nameDiv.style.backgroundColor = color;
  1185. part.nameDiv.textContent = name;
  1186. $(part.cursorDiv)
  1187. .find(".name")
  1188. .text(name)
  1189. .css("background-color", color);
  1190. });
  1191. gClient.on("ch", function(msg) {
  1192. for(var id in gClient.ppl) {
  1193. if(gClient.ppl.hasOwnProperty(id)) {
  1194. var part = gClient.ppl[id];
  1195. if(part.id === gClient.participantId) {
  1196. $(part.nameDiv).addClass("me");
  1197. } else {
  1198. $(part.nameDiv).removeClass("me");
  1199. }
  1200. if(msg.ch.crown && msg.ch.crown.participantId === part.id) {
  1201. $(part.nameDiv).addClass("owner");
  1202. $(part.cursorDiv).addClass("owner");
  1203. } else {
  1204. $(part.nameDiv).removeClass("owner");
  1205. $(part.cursorDiv).removeClass("owner");
  1206. }
  1207. if(gPianoMutes.indexOf(part._id) !== -1) {
  1208. $(part.nameDiv).addClass("muted-notes");
  1209. } else {
  1210. $(part.nameDiv).removeClass("muted-notes");
  1211. }
  1212. if(gChatMutes.indexOf(part._id) !== -1) {
  1213. $(part.nameDiv).addClass("muted-chat");
  1214. } else {
  1215. $(part.nameDiv).removeClass("muted-chat");
  1216. }
  1217. }
  1218. }
  1219. });
  1220. })();
  1221.  
  1222.  
  1223. // Handle changes to crown
  1224. (function() {
  1225. var jqcrown = $('<div id="crown"></div>').appendTo(document.body).hide();
  1226. var jqcountdown = $('<span></span>').appendTo(jqcrown);
  1227. var countdown_interval;
  1228. jqcrown.click(function() {
  1229. gClient.sendArray([{m: "chown", id: gClient.participantId}]);
  1230. });
  1231. gClient.on("ch", function(msg) {
  1232. if(msg.ch.crown) {
  1233. var crown = msg.ch.crown;
  1234. if(!crown.participantId || !gClient.ppl[crown.participantId]) {
  1235. var land_time = crown.time + 2000 - gClient.serverTimeOffset;
  1236. var avail_time = crown.time + 15000 - gClient.serverTimeOffset;
  1237. jqcountdown.text("");
  1238. jqcrown.show();
  1239. if(land_time - Date.now() <= 0) {
  1240. jqcrown.css({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"});
  1241. } else {
  1242. jqcrown.css({"left": crown.startPos.x + "%", "top": crown.startPos.y + "%"});
  1243. jqcrown.addClass("spin");
  1244. jqcrown.animate({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"}, 2000, "linear", function() {
  1245. jqcrown.removeClass("spin");
  1246. });
  1247. }
  1248. clearInterval(countdown_interval);
  1249. countdown_interval = setInterval(function() {
  1250. var time = Date.now();
  1251. if(time >= land_time) {
  1252. var ms = avail_time - time;
  1253. if(ms > 0) {
  1254. jqcountdown.text(Math.ceil(ms / 1000) + "s");
  1255. } else {
  1256. jqcountdown.text("");
  1257. clearInterval(countdown_interval);
  1258. }
  1259. }
  1260. }, 1000);
  1261. } else {
  1262. jqcrown.hide();
  1263. }
  1264. } else {
  1265. jqcrown.hide();
  1266. }
  1267. });
  1268. gClient.on("disconnect", function() {
  1269. jqcrown.fadeOut(2000);
  1270. });
  1271. })();
  1272.  
  1273.  
  1274. // Playing notes
  1275. gClient.on("n", function(msg) {
  1276. var t = msg.t - gClient.serverTimeOffset + TIMING_TARGET - Date.now();
  1277. var participant = gClient.findParticipantById(msg.p);
  1278. if(gPianoMutes.indexOf(participant._id) !== -1)
  1279. return;
  1280. for(var i = 0; i < msg.n.length; i++) {
  1281. var note = msg.n[i];
  1282. var ms = t + (note.d || 0);
  1283. if(ms < 0) {
  1284. ms = 0;
  1285. }
  1286. else if(ms > 10000) continue;
  1287. if(note.s) {
  1288. gPiano.stop(note.n, participant, ms);
  1289. } else {
  1290. var vel = (typeof note.v !== "undefined")? parseFloat(note.v) : DEFAULT_VELOCITY;
  1291. if(vel < 0) vel = 0; else if (vel > 1) vel = 1;
  1292. gPiano.play(note.n, vel, participant, ms);
  1293. if(enableSynth) {
  1294. gPiano.stop(note.n, participant, ms + 1000);
  1295. }
  1296. }
  1297. }
  1298. });
  1299.  
  1300. // Send cursor updates
  1301. var mx = 0, last_mx = -10, my = 0, last_my = -10;
  1302. setInterval(function() {
  1303. if(Math.abs(mx - last_mx) > 0.1 || Math.abs(my - last_my) > 0.1) {
  1304. last_mx = mx;
  1305. last_my = my;
  1306. gClient.sendArray([{m: "m", x: mx, y: my}]);
  1307. var part = gClient.getOwnParticipant();
  1308. if(part) {
  1309. part.x = mx;
  1310. part.y = my;
  1311. }
  1312. }
  1313. }, 50);
  1314. $(document).mousemove(function(event) {
  1315. mx = ((event.pageX / $(window).width()) * 100).toFixed(2);
  1316. my = ((event.pageY / $(window).height()) * 100).toFixed(2);
  1317. });
  1318.  
  1319. // Animate cursors
  1320. setInterval(function() {
  1321. for(var id in gClient.ppl) {
  1322. if(!gClient.ppl.hasOwnProperty(id)) continue;
  1323. var part = gClient.ppl[id];
  1324. if(part.cursorDiv && (Math.abs(part.x - part.displayX) > 0.1 || Math.abs(part.y - part.displayY) > 0.1)) {
  1325. part.displayX += (part.x - part.displayX) * 0.75;
  1326. part.displayY += (part.y - part.displayY) * 0.75;
  1327. part.cursorDiv.style.left = part.displayX + "%";
  1328. part.cursorDiv.style.top = part.displayY + "%";
  1329. }
  1330. }
  1331. }, 50);
  1332.  
  1333.  
  1334. // Room settings button
  1335. (function() {
  1336. gClient.on("ch", function(msg) {
  1337. if(gClient.isOwner()) {
  1338. $("#room-settings-btn").show();
  1339. } else {
  1340. $("#room-settings-btn").hide();
  1341. }
  1342. });
  1343. $("#room-settings-btn").click(function(evt) {
  1344. if(gClient.channel && gClient.isOwner()) {
  1345. var settings = gClient.channel.settings;
  1346. openModal("#room-settings");
  1347. setTimeout(function() {
  1348. $("#room-settings .checkbox[name=visible]").prop("checked", settings.visible);
  1349. $("#room-settings .checkbox[name=chat]").prop("checked", settings.chat);
  1350. $("#room-settings .checkbox[name=crownsolo]").prop("checked", settings.crownsolo);
  1351. $("#room-settings input[name=color]").val(settings.color);
  1352. }, 100);
  1353. }
  1354. });
  1355. $("#room-settings .submit").click(function() {
  1356. var settings = {
  1357. visible: $("#room-settings .checkbox[name=visible]").is(":checked"),
  1358. chat: $("#room-settings .checkbox[name=chat]").is(":checked"),
  1359. crownsolo: $("#room-settings .checkbox[name=crownsolo]").is(":checked"),
  1360. color: $("#room-settings input[name=color]").val()
  1361. };
  1362. gClient.sendArray([{m: "chset", set: settings}]);
  1363. closeModal();
  1364. });
  1365. $("#room-settings .drop-crown").click(function() {
  1366. gClient.sendArray([{m: "chown"}]);
  1367. closeModal();
  1368. });
  1369. })();
  1370.  
  1371. // Handle notifications
  1372. gClient.on("notification", function(msg) {
  1373. new Notification(msg);
  1374. });
  1375.  
  1376. // Don't foget spin
  1377. /* Do you really need this???? WHY???? No!
  1378. gClient.on("ch", function(msg) {
  1379. var chidlo = msg.ch._id.toLowerCase();
  1380. if(chidlo === "spin" || chidlo.substr(-5) === "/spin") {
  1381. $("#piano").addClass("spin");
  1382. } else {
  1383. $("#piano").removeClass("spin");
  1384. }
  1385. });
  1386. */
  1387.  
  1388. /*function eb() {
  1389. if(gClient.channel && gClient.channel._id.toLowerCase() === "test/fishing") {
  1390. ebsprite.start(gClient);
  1391. } else {
  1392. ebsprite.stop();
  1393. }
  1394. }
  1395. if(ebsprite) {
  1396. gClient.on("ch", eb);
  1397. eb();
  1398. }*/
  1399.  
  1400. // Crownsolo notice
  1401. gClient.on("ch", function(msg) {
  1402. if(msg.ch.settings.crownsolo) {
  1403. if($("#crownsolo-notice").length == 0) {
  1404. $('<div id="crownsolo-notice">').text('This room is set to "only the owner can play."').appendTo("body").fadeIn(1000);
  1405. }
  1406. } else {
  1407. $("#crownsolo-notice").remove();
  1408. }
  1409. });
  1410. gClient.on("disconnect", function() {
  1411. $("#crownsolo-notice").remove();
  1412. });
  1413.  
  1414.  
  1415. // Background color
  1416. (function() {
  1417. var old_color1 = new Color("#3b5054");
  1418. var old_color2 = new Color("#3b5054");
  1419. function setColor(hex) {
  1420. var color1 = new Color(hex);
  1421. var color2 = new Color(hex);
  1422. color2.add(-0x40, -0x40, -0x40);
  1423.  
  1424. var bottom = document.getElementById("bottom");
  1425.  
  1426. var duration = 500;
  1427. var step = 0;
  1428. var steps = 30;
  1429. var step_ms = duration / steps;
  1430. var difference = new Color(color1.r, color1.g, color1.b);
  1431. difference.r -= old_color1.r;
  1432. difference.g -= old_color1.g;
  1433. difference.b -= old_color1.b;
  1434. var inc = new Color(difference.r / steps, difference.g / steps, difference.b / steps);
  1435. var iv;
  1436. iv = setInterval(function() {
  1437. old_color1.add(inc.r, inc.g, inc.b);
  1438. old_color2.add(inc.r, inc.g, inc.b);
  1439. document.body.style.background = "radial-gradient(ellipse at center, "+old_color1.toHexa()+" 0%,"+old_color2.toHexa()+" 100%)";
  1440. bottom.style.background = old_color2.toHexa();
  1441. if(++step >= steps) {
  1442. clearInterval(iv);
  1443. old_color1 = color1;
  1444. old_color2 = color2;
  1445. document.body.style.background = "radial-gradient(ellipse at center, "+color1.toHexa()+" 0%,"+color2.toHexa()+" 100%)";
  1446. bottom.style.background = color2.toHexa();
  1447. }
  1448. }, step_ms);
  1449. }
  1450.  
  1451. setColor("#3b5054");
  1452.  
  1453. gClient.on("ch", function(ch) {
  1454. if(ch.ch.settings) {
  1455. if(ch.ch.settings.color) {
  1456. // MODIFICATION: keep default background color, no bright colors
  1457. // setColor(ch.ch.settings.color);
  1458. } else {
  1459. setColor("#3b5054");
  1460. }
  1461. }
  1462. });
  1463. })();
  1464.  
  1465.  
  1466.  
  1467.  
  1468.  
  1469.  
  1470.  
  1471. var volume_slider = new VolumeSlider(document.getElementById("volume"), function(v) {
  1472. gPiano.audio.setVolume(v);
  1473. if(window.localStorage) localStorage.volume = v;
  1474. });
  1475. volume_slider.set(gPiano.audio.volume);
  1476.  
  1477. var Note = function(note, octave) {
  1478. this.note = note;
  1479. this.octave = octave || 0;
  1480. };
  1481.  
  1482.  
  1483.  
  1484. var n = function(a, b) { return {note: new Note(a, b), held: false}; };
  1485. var key_binding = {
  1486. 65: n("gs"),
  1487. 90: n("a"),
  1488. 83: n("as"),
  1489. 88: n("b"),
  1490. 67: n("c", 1),
  1491. 70: n("cs", 1),
  1492. 86: n("d", 1),
  1493. 71: n("ds", 1),
  1494. 66: n("e", 1),
  1495. 78: n("f", 1),
  1496. 74: n("fs", 1),
  1497. 77: n("g", 1),
  1498. 75: n("gs", 1),
  1499. 188: n("a", 1),
  1500. 76: n("as", 1),
  1501. 190: n("b", 1),
  1502. 191: n("c", 2),
  1503. 222: n("cs", 2),
  1504.  
  1505. 49: n("gs", 1),
  1506. 81: n("a", 1),
  1507. 50: n("as", 1),
  1508. 87: n("b", 1),
  1509. 69: n("c", 2),
  1510. 52: n("cs", 2),
  1511. 82: n("d", 2),
  1512. 53: n("ds", 2),
  1513. 84: n("e", 2),
  1514. 89: n("f", 2),
  1515. 55: n("fs", 2),
  1516. 85: n("g", 2),
  1517. 56: n("gs", 2),
  1518. 73: n("a", 2),
  1519. 57: n("as", 2),
  1520. 79: n("b", 2),
  1521. 80: n("c", 3),
  1522. 189: n("cs", 3),
  1523. 219: n("d", 3),
  1524. 187: n("ds", 3),
  1525. 221: n("e", 3)
  1526. };
  1527.  
  1528. var capsLockKey = false;
  1529.  
  1530. var transpose_octave = 0;
  1531.  
  1532. function handleKeyDown(evt) {
  1533. //console.log(evt);
  1534. var code = parseInt(evt.keyCode);
  1535. if(key_binding[code] !== undefined) {
  1536. var binding = key_binding[code];
  1537. if(!binding.held) {
  1538. binding.held = true;
  1539.  
  1540. var note = binding.note;
  1541. var octave = 1 + note.octave + transpose_octave;
  1542. if(evt.shiftKey) ++octave;
  1543. else if(capsLockKey || evt.ctrlKey) --octave;
  1544. note = note.note + octave;
  1545. var vol = velocityFromMouseY();
  1546. press(note, vol);
  1547. }
  1548.  
  1549. if(++gKeyboardSeq == 3) {
  1550. gKnowsYouCanUseKeyboard = true;
  1551. if(window.gKnowsYouCanUseKeyboardTimeout) clearTimeout(gKnowsYouCanUseKeyboardTimeout);
  1552. if(localStorage) localStorage.knowsYouCanUseKeyboard = true;
  1553. if(window.gKnowsYouCanUseKeyboardNotification) gKnowsYouCanUseKeyboardNotification.close();
  1554. }
  1555.  
  1556. evt.preventDefault();
  1557. evt.stopPropagation();
  1558. return false;
  1559. } else if(code == 20) { // Caps Lock
  1560. capsLockKey = true;
  1561. evt.preventDefault();
  1562. } else if(code === 0x20) { // Space Bar
  1563. pressSustain();
  1564. evt.preventDefault();
  1565. } else if((code === 38 || code === 39) && transpose_octave < 3) {
  1566. ++transpose_octave;
  1567. } else if((code === 40 || code === 37) && transpose_octave > -2) {
  1568. --transpose_octave;
  1569. } else if(code == 9) { // Tab (don't tab away from the piano)
  1570. evt.preventDefault();
  1571. } else if(code == 8) { // Backspace (don't navigate Back)
  1572. gAutoSustain = !gAutoSustain;
  1573. evt.preventDefault();
  1574. }
  1575. };
  1576.  
  1577. function handleKeyUp(evt) {
  1578. var code = parseInt(evt.keyCode);
  1579. if(key_binding[code] !== undefined) {
  1580. var binding = key_binding[code];
  1581. if(binding.held) {
  1582. binding.held = false;
  1583.  
  1584. var note = binding.note;
  1585. var octave = 1 + note.octave + transpose_octave;
  1586. if(evt.shiftKey) ++octave;
  1587. else if(capsLockKey || evt.ctrlKey) --octave;
  1588. note = note.note + octave;
  1589. release(note);
  1590. }
  1591.  
  1592. evt.preventDefault();
  1593. evt.stopPropagation();
  1594. return false;
  1595. } else if(code == 20) { // Caps Lock
  1596. capsLockKey = false;
  1597. evt.preventDefault();
  1598. } else if(code === 0x20) { // Space Bar
  1599. releaseSustain();
  1600. evt.preventDefault();
  1601. }
  1602. };
  1603.  
  1604. function handleKeyPress(evt) {
  1605. evt.preventDefault();
  1606. evt.stopPropagation();
  1607. if(evt.keyCode == 27 || evt.keyCode == 13) {
  1608. //$("#chat input").focus();
  1609. }
  1610. return false;
  1611. };
  1612.  
  1613. var recapListener = function(evt) {
  1614. captureKeyboard();
  1615. };
  1616.  
  1617. function captureKeyboard() {
  1618. $("#piano").off("mousedown", recapListener);
  1619. $("#piano").off("touchstart", recapListener);
  1620. $(document).on("keydown", handleKeyDown );
  1621. $(document).on("keyup", handleKeyUp);
  1622. $(window).on("keypress", handleKeyPress );
  1623. };
  1624.  
  1625. function releaseKeyboard() {
  1626. $(document).off("keydown", handleKeyDown );
  1627. $(document).off("keyup", handleKeyUp);
  1628. $(window).off("keypress", handleKeyPress );
  1629. $("#piano").on("mousedown", recapListener);
  1630. $("#piano").on("touchstart", recapListener);
  1631. };
  1632.  
  1633. captureKeyboard();
  1634.  
  1635.  
  1636. var velocityFromMouseY = function() {
  1637. return 0.1 + (my / 100) * 0.6;
  1638. };
  1639.  
  1640.  
  1641. // NoteQuota
  1642. var gNoteQuota = (function() {
  1643. var last_rat = 0;
  1644. var nqjq = $("#quota .value");
  1645. setInterval(function() {
  1646. gNoteQuota.tick();
  1647. }, 2000);
  1648. return new NoteQuota(function(points) {
  1649. // update UI
  1650. var rat = (points / this.max) * 100;
  1651. if(rat <= last_rat)
  1652. nqjq.stop(true, true).css("width", rat.toFixed(0) + "%");
  1653. else
  1654. nqjq.stop(true, true).animate({"width": rat.toFixed(0) + "%"}, 2000, "linear");
  1655. last_rat = rat;
  1656. });
  1657. })();
  1658. gClient.on("nq", function(nq_params) {
  1659. gNoteQuota.setParams(nq_params);
  1660. });
  1661. gClient.on("disconnect", function() {
  1662. gNoteQuota.setParams(NoteQuota.PARAMS_OFFLINE);
  1663. });
  1664.  
  1665.  
  1666.  
  1667. // click participant names
  1668. (function() {
  1669. var ele = document.getElementById("names");
  1670. var touchhandler = function(e) {
  1671. var target_jq = $(e.target);
  1672. if(target_jq.hasClass("name")) {
  1673. target_jq.addClass("play");
  1674. if(e.target.participantId == gClient.participantId) {
  1675. openModal("#rename", "input[name=name]");
  1676. setTimeout(function() {
  1677. $("#rename input[name=name]").val(gClient.ppl[gClient.participantId].name);
  1678. $("#rename input[name=color]").val(gClient.ppl[gClient.participantId].color);
  1679. }, 100);
  1680. } else if(e.target.participantId) {
  1681. var id = e.target.participantId;
  1682. var part = gClient.ppl[id] || null;
  1683. if(part) {
  1684. participantMenu(part);
  1685. e.stopPropagation();
  1686. }
  1687. }
  1688. }
  1689. };
  1690. ele.addEventListener("mousedown", touchhandler);
  1691. ele.addEventListener("touchstart", touchhandler);
  1692. var releasehandler = function(e) {
  1693. $("#names .name").removeClass("play");
  1694. };
  1695. document.body.addEventListener("mouseup", releasehandler);
  1696. document.body.addEventListener("touchend", releasehandler);
  1697.  
  1698. var removeParticipantMenus = function() {
  1699. $(".participant-menu").remove();
  1700. $(".participantSpotlight").hide();
  1701. document.removeEventListener("mousedown", removeParticipantMenus);
  1702. document.removeEventListener("touchstart", removeParticipantMenus);
  1703. };
  1704.  
  1705. var participantMenu = function(part) {
  1706. if(!part) return;
  1707. removeParticipantMenus();
  1708. document.addEventListener("mousedown", removeParticipantMenus);
  1709. document.addEventListener("touchstart", removeParticipantMenus);
  1710. $("#" + part.id).find(".enemySpotlight").show();
  1711. var menu = $('<div class="participant-menu"></div>');
  1712. $("body").append(menu);
  1713. // move menu to name position
  1714. var jq_nd = $(part.nameDiv);
  1715. var pos = jq_nd.position();
  1716. menu.css({
  1717. "top": pos.top + jq_nd.height() + 15,
  1718. "left": pos.left + 6,
  1719. "background": part.color || "black"
  1720. });
  1721. menu.on("mousedown touchstart", function(evt) {
  1722. evt.stopPropagation();
  1723. var target = $(evt.target);
  1724. if(target.hasClass("menu-item")) {
  1725. target.addClass("clicked");
  1726. menu.fadeOut(200, function() {
  1727. removeParticipantMenus();
  1728. });
  1729. }
  1730. });
  1731. // this spaces stuff out but also can be used for informational
  1732. $('<div class="info"></div>').appendTo(menu).text(part._id);
  1733. // add menu items
  1734. if(gPianoMutes.indexOf(part._id) == -1) {
  1735. $('<div class="menu-item">Mute Notes</div>').appendTo(menu)
  1736. .on("mousedown touchstart", function(evt) {
  1737. gPianoMutes.push(part._id);
  1738. $(part.nameDiv).addClass("muted-notes");
  1739. });
  1740. } else {
  1741. $('<div class="menu-item">Unmute Notes</div>').appendTo(menu)
  1742. .on("mousedown touchstart", function(evt) {
  1743. var i;
  1744. while((i = gPianoMutes.indexOf(part._id)) != -1)
  1745. gPianoMutes.splice(i, 1);
  1746. $(part.nameDiv).removeClass("muted-notes");
  1747. });
  1748. }
  1749. if(gChatMutes.indexOf(part._id) == -1) {
  1750. $('<div class="menu-item">Mute Chat</div>').appendTo(menu)
  1751. .on("mousedown touchstart", function(evt) {
  1752. gChatMutes.push(part._id);
  1753. $(part.nameDiv).addClass("muted-chat");
  1754. });
  1755. } else {
  1756. $('<div class="menu-item">Unmute Chat</div>').appendTo(menu)
  1757. .on("mousedown touchstart", function(evt) {
  1758. var i;
  1759. while((i = gChatMutes.indexOf(part._id)) != -1)
  1760. gChatMutes.splice(i, 1);
  1761. $(part.nameDiv).removeClass("muted-chat");
  1762. });
  1763. }
  1764. if(!(gPianoMutes.indexOf(part._id) >= 0) || !(gChatMutes.indexOf(part._id) >= 0)) {
  1765. $('<div class="menu-item">Mute Completely</div>').appendTo(menu)
  1766. .on("mousedown touchstart", function(evt) {
  1767. gPianoMutes.push(part._id);
  1768. gChatMutes.push(part._id);
  1769. $(part.nameDiv).addClass("muted-notes");
  1770. $(part.nameDiv).addClass("muted-chat");
  1771. });
  1772. }
  1773. if((gPianoMutes.indexOf(part._id) >= 0) || (gChatMutes.indexOf(part._id) >= 0)) {
  1774. $('<div class="menu-item">Unmute Completely</div>').appendTo(menu)
  1775. .on("mousedown touchstart", function(evt) {
  1776. var i;
  1777. while((i = gPianoMutes.indexOf(part._id)) != -1)
  1778. gPianoMutes.splice(i, 1);
  1779. while((i = gChatMutes.indexOf(part._id)) != -1)
  1780. gChatMutes.splice(i, 1);
  1781. $(part.nameDiv).removeClass("muted-notes");
  1782. $(part.nameDiv).removeClass("muted-chat");
  1783. });
  1784. }
  1785. if(gClient.isOwner()) {
  1786. $('<div class="menu-item give-crown">Give Crown</div>').appendTo(menu)
  1787. .on("mousedown touchstart", function(evt) {
  1788. gClient.sendArray([{m: "chown", id: part.id}]);
  1789. });
  1790. $('<div class="menu-item kickban">Kickban</div>').appendTo(menu)
  1791. .on("mousedown touchstart", function(evt) {
  1792. var minutes = prompt("How many minutes? (0-60)", "30");
  1793. if(minutes === null) return;
  1794. minutes = parseFloat(minutes) || 0;
  1795. var ms = minutes * 60 * 1000;
  1796. gClient.sendArray([{m: "kickban", _id: part._id, ms: ms}]);
  1797. });
  1798. }
  1799. menu.fadeIn(100);
  1800. };
  1801. })();
  1802.  
  1803.  
  1804. // Notification class
  1805.  
  1806. ////////////////////////////////////////////////////////////////
  1807.  
  1808. var Notification = function(par) {
  1809. EventEmitter.call(this);
  1810.  
  1811. var par = par || {};
  1812.  
  1813. this.id = "Notification-" + (par.id || Math.random());
  1814. this.title = par.title || "";
  1815. this.text = par.text || "";
  1816. this.html = par.html || "";
  1817. this.target = $(par.target || "#piano");
  1818. this.duration = par.duration || 30000;
  1819. this["class"] = par["class"] || "classic";
  1820.  
  1821. var self = this;
  1822. var eles = $("#" + this.id);
  1823. if(eles.length > 0) {
  1824. eles.remove();
  1825. }
  1826. this.domElement = $('<div class="notification"><div class="notification-body"><div class="title"></div>' +
  1827. '<div class="text"></div></div><div class="x">x</div></div>');
  1828. this.domElement[0].id = this.id;
  1829. this.domElement.addClass(this["class"]);
  1830. this.domElement.find(".title").text(this.title);
  1831. if(this.text.length > 0) {
  1832. this.domElement.find(".text").text(this.text);
  1833. } else if(this.html instanceof HTMLElement) {
  1834. this.domElement.find(".text")[0].appendChild(this.html);
  1835. } else if(this.html.length > 0) {
  1836. this.domElement.find(".text").html(this.html);
  1837. }
  1838. document.body.appendChild(this.domElement.get(0));
  1839.  
  1840. this.position();
  1841. this.onresize = function() {
  1842. self.position();
  1843. };
  1844. window.addEventListener("resize", this.onresize);
  1845.  
  1846. this.domElement.find(".x").click(function() {
  1847. self.close();
  1848. });
  1849.  
  1850. if(this.duration > 0) {
  1851. setTimeout(function() {
  1852. self.close();
  1853. }, this.duration);
  1854. }
  1855.  
  1856. return this;
  1857. }
  1858.  
  1859. mixin(Notification.prototype, EventEmitter.prototype);
  1860. Notification.prototype.constructor = Notification;
  1861.  
  1862. Notification.prototype.position = function() {
  1863. var pos = this.target.offset();
  1864. var x = pos.left - (this.domElement.width() / 2) + (this.target.width() / 4);
  1865. var y = pos.top - this.domElement.height() - 8;
  1866. var width = this.domElement.width();
  1867. if(x + width > $("body").width()) {
  1868. x -= ((x + width) - $("body").width());
  1869. }
  1870. if(x < 0) x = 0;
  1871. this.domElement.offset({left: x, top: y});
  1872. };
  1873.  
  1874. Notification.prototype.close = function() {
  1875. var self = this;
  1876. window.removeEventListener("resize", this.onresize);
  1877. this.domElement.fadeOut(500, function() {
  1878. self.domElement.remove();
  1879. self.emit("close");
  1880. });
  1881. };
  1882.  
  1883.  
  1884. // set variables from settings or set settings
  1885.  
  1886. ////////////////////////////////////////////////////////////////
  1887.  
  1888. var gKeyboardSeq = 0;
  1889. var gKnowsYouCanUseKeyboard = false;
  1890. if(localStorage && localStorage.knowsYouCanUseKeyboard) gKnowsYouCanUseKeyboard = true;
  1891. if(!gKnowsYouCanUseKeyboard) {
  1892. window.gKnowsYouCanUseKeyboardTimeout = setTimeout(function() {
  1893. window.gKnowsYouCanUseKeyboardNotification = new Notification({title: "Did you know!?!",
  1894. text: "No MIDI input device found!", target: "#piano", duration: 10000});
  1895. }, 30000);
  1896. }
  1897.  
  1898.  
  1899.  
  1900.  
  1901. if(window.localStorage) {
  1902.  
  1903. if(localStorage.volume) {
  1904. volume_slider.set(localStorage.volume);
  1905. gPiano.audio.setVolume(localStorage.volume);
  1906. }
  1907. else localStorage.volume = gPiano.audio.volume;
  1908.  
  1909. window.gHasBeenHereBefore = (localStorage.gHasBeenHereBefore || false);
  1910. if(gHasBeenHereBefore) {
  1911. }
  1912. localStorage.gHasBeenHereBefore = true;
  1913.  
  1914. }
  1915.  
  1916.  
  1917. // New room, change room
  1918.  
  1919. ////////////////////////////////////////////////////////////////
  1920.  
  1921. $("#room > .info").text("--");
  1922. gClient.on("ch", function(msg) {
  1923. var channel = msg.ch;
  1924. var info = $("#room > .info");
  1925. info.text(channel._id);
  1926. if(channel.settings.lobby) info.addClass("lobby");
  1927. else info.removeClass("lobby");
  1928. if(!channel.settings.chat) info.addClass("no-chat");
  1929. else info.removeClass("no-chat");
  1930. if(channel.settings.crownsolo) info.addClass("crownsolo");
  1931. else info.removeClass("crownsolo");
  1932. if(!channel.settings.visible) info.addClass("not-visible");
  1933. else info.removeClass("not-visible");
  1934. });
  1935. gClient.on("ls", function(ls) {
  1936. for(var i in ls.u) {
  1937. if(!ls.u.hasOwnProperty(i)) continue;
  1938. var room = ls.u[i];
  1939. var info = $("#room .info[roomname=\"" + (room._id + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0') + "\"]");
  1940. if(info.length == 0) {
  1941. info = $("<div class=\"info\"></div>");
  1942. info.attr("roomname", room._id);
  1943. $("#room .more").append(info);
  1944. }
  1945. info.text(room._id + " (" + room.count + ")");
  1946. if(room.settings.lobby) info.addClass("lobby");
  1947. else info.removeClass("lobby");
  1948. if(!room.settings.chat) info.addClass("no-chat");
  1949. else info.removeClass("no-chat");
  1950. if(room.settings.crownsolo) info.addClass("crownsolo");
  1951. else info.removeClass("crownsolo");
  1952. if(!room.settings.visible) info.addClass("not-visible");
  1953. else info.removeClass("not-visible");
  1954. }
  1955. });
  1956. $("#room").on("click", function(evt) {
  1957. evt.stopPropagation();
  1958.  
  1959. // clicks on a new room
  1960. if($(evt.target).hasClass("info") && $(evt.target).parents(".more").length) {
  1961. $("#room .more").fadeOut(250);
  1962. var selected_name = $(evt.target).attr("roomname");
  1963. if(typeof selected_name != "undefined") {
  1964. changeRoom(selected_name, "right");
  1965. }
  1966. return false;
  1967. }
  1968. // clicks on "New Room..."
  1969. else if($(evt.target).hasClass("new")) {
  1970. openModal("#new-room", "input[name=name]");
  1971. }
  1972. // all other clicks
  1973. var doc_click = function(evt) {
  1974. if($(evt.target).is("#room .more")) return;
  1975. $(document).off("mousedown", doc_click);
  1976. $("#room .more").fadeOut(250);
  1977. gClient.sendArray([{m: "-ls"}]);
  1978. }
  1979. $(document).on("mousedown", doc_click);
  1980. $("#room .more .info").remove();
  1981. $("#room .more").show();
  1982. gClient.sendArray([{m: "+ls"}]);
  1983. });
  1984. $("#new-room-btn").on("click", function(evt) {
  1985. evt.stopPropagation();
  1986. openModal("#new-room", "input[name=name]");
  1987. });
  1988.  
  1989.  
  1990. $("#play-alone-btn").on("click", function(evt) {
  1991. evt.stopPropagation();
  1992. var room_name = "Room" + Math.floor(Math.random() * 1000000000000);
  1993. changeRoom(room_name, "right", {"visible": false, "chat": true, "crownsolo": false});
  1994. setTimeout(function() {
  1995. new Notification({id: "share", title: "Playing alone", html: 'You are playing alone in a room by yourself, but you can always invite \
  1996. friends by sending them the link.<br/><br/>\
  1997. <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/>\
  1998. <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Tweet</a>', duration: 25000});
  1999. }, 1000);
  2000. });
  2001.  
  2002.  
  2003.  
  2004. var gModal;
  2005.  
  2006. function modalHandleEsc(evt) {
  2007. if(evt.keyCode == 27) {
  2008. closeModal();
  2009. evt.preventDefault();
  2010. evt.stopPropagation();
  2011. }
  2012. };
  2013.  
  2014. function openModal(selector, focus) {
  2015. chat.blur();
  2016. releaseKeyboard();
  2017. $(document).on("keydown", modalHandleEsc);
  2018. $("#modal #modals > *").hide();
  2019. $("#modal").fadeIn(250);
  2020. $(selector).show();
  2021. setTimeout(function() {
  2022. $(selector).find(focus).focus();
  2023. }, 100);
  2024. gModal = selector;
  2025. };
  2026.  
  2027. function closeModal() {
  2028. $(document).off("keydown", modalHandleEsc);
  2029. $("#modal").fadeOut(100);
  2030. $("#modal #modals > *").hide();
  2031. captureKeyboard();
  2032. gModal = null;
  2033. };
  2034.  
  2035. var modal_bg = $("#modal .bg")[0];
  2036. $(modal_bg).on("click", function(evt) {
  2037. if(evt.target != modal_bg) return;
  2038. closeModal();
  2039. });
  2040.  
  2041. (function() {
  2042. function submit() {
  2043. var name = $("#new-room .text[name=name]").val();
  2044. var settings = {
  2045. visible: $("#new-room .checkbox[name=visible]").is(":checked"),
  2046. chat: true,
  2047. crownsolo: false
  2048. };
  2049. $("#new-room .text[name=name]").val("");
  2050. closeModal();
  2051. changeRoom(name, "right", settings);
  2052. setTimeout(function() {
  2053. new Notification({id: "share", title: "Created a Room", html: 'You can invite friends to your room by sending them the link.<br/><br/>\
  2054. <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/>\
  2055. <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Tweet</a>', duration: 25000});
  2056. }, 1000);
  2057. };
  2058. $("#new-room .submit").click(function(evt) {
  2059. submit();
  2060. });
  2061. $("#new-room .text[name=name]").keypress(function(evt) {
  2062. if(evt.keyCode == 13) {
  2063. submit();
  2064. } else if(evt.keyCode == 27) {
  2065. closeModal();
  2066. } else {
  2067. return;
  2068. }
  2069. evt.preventDefault();
  2070. evt.stopPropagation();
  2071. return false;
  2072. });
  2073. })();
  2074.  
  2075.  
  2076.  
  2077.  
  2078.  
  2079.  
  2080.  
  2081.  
  2082. function changeRoom(name, direction, settings, push) {
  2083. if(!settings) settings = {};
  2084. if(!direction) direction = "right";
  2085. if(typeof push == "undefined") push = true;
  2086. var opposite = direction == "left" ? "right" : "left";
  2087.  
  2088. if(name == "") name = "lobby";
  2089. if(gClient.channel && gClient.channel._id === name) return;
  2090. if(push) {
  2091. var url = "/" + encodeURIComponent(name).replace("'", "%27");
  2092. if(window.history && history.pushState) {
  2093. history.pushState({"depth": gHistoryDepth += 1, "name": name}, "Piano > " + name, url);
  2094. } else {
  2095. window.location = url;
  2096. return;
  2097. }
  2098. }
  2099.  
  2100. gClient.setChannel(name, settings);
  2101.  
  2102. var t = 0, d = 100;
  2103. $("#piano").addClass("ease-out").addClass("slide-" + opposite);
  2104. setTimeout(function() {
  2105. $("#piano").removeClass("ease-out").removeClass("slide-" + opposite).addClass("slide-" + direction);
  2106. }, t += d);
  2107. setTimeout(function() {
  2108. $("#piano").addClass("ease-in").removeClass("slide-" + direction);
  2109. }, t += d);
  2110. setTimeout(function() {
  2111. $("#piano").removeClass("ease-in");
  2112. }, t += d);
  2113. };
  2114.  
  2115. var gHistoryDepth = 0;
  2116. $(window).on("popstate", function(evt) {
  2117. var depth = evt.state ? evt.state.depth : 0;
  2118. if(depth == gHistoryDepth) return; // <-- forgot why I did that though...
  2119.  
  2120. var direction = depth <= gHistoryDepth ? "left" : "right";
  2121. gHistoryDepth = depth;
  2122.  
  2123. var name = decodeURIComponent(window.location.pathname);
  2124. if(name.substr(0, 1) == "/") name = name.substr(1);
  2125. changeRoom(name, direction, null, false);
  2126. });
  2127.  
  2128.  
  2129. // Rename
  2130.  
  2131. ////////////////////////////////////////////////////////////////
  2132.  
  2133. (function() {
  2134. function submit() {
  2135. var set = {
  2136. name: $("#rename input[name=name]").val(),
  2137. color: $("#rename input[name=color]").val()
  2138. };
  2139. //$("#rename .text[name=name]").val("");
  2140. closeModal();
  2141. gClient.sendArray([{m: "userset", set: set}]);
  2142. };
  2143. $("#rename .submit").click(function(evt) {
  2144. submit();
  2145. });
  2146. $("#rename .text[name=name]").keypress(function(evt) {
  2147. if(evt.keyCode == 13) {
  2148. submit();
  2149. } else if(evt.keyCode == 27) {
  2150. closeModal();
  2151. } else {
  2152. return;
  2153. }
  2154. evt.preventDefault();
  2155. evt.stopPropagation();
  2156. return false;
  2157. });
  2158. })();
  2159.  
  2160.  
  2161. // chatctor
  2162.  
  2163. ////////////////////////////////////////////////////////////////
  2164.  
  2165. var chatLoaded = false;
  2166.  
  2167. var chat = (function() {
  2168. gClient.on("ch", function(msg) {
  2169. if(msg.ch.settings.chat) {
  2170. chat.show();
  2171. } else {
  2172. chat.hide();
  2173. }
  2174. });
  2175. gClient.on("disconnect", function(msg) {
  2176. chat.hide();
  2177. });
  2178. gClient.on("c", function(msg) {
  2179. chat.clear();
  2180. if(msg.c) {
  2181. chatLoaded = false;
  2182. for(var i = 0; i < msg.c.length; i++) {
  2183. chat.receive(msg.c[i]);
  2184. }
  2185. chatLoaded = true;
  2186. }
  2187. });
  2188. gClient.on("a", function(msg) {
  2189. chat.receive(msg);
  2190. });
  2191.  
  2192. $("#chat input").on("focus", function(evt) {
  2193. releaseKeyboard();
  2194. $("#chat").addClass("chatting");
  2195. chat.scrollToBottom();
  2196. });
  2197. /*$("#chat input").on("blur", function(evt) {
  2198. captureKeyboard();
  2199. $("#chat").removeClass("chatting");
  2200. chat.scrollToBottom();
  2201. });*/
  2202. $(document).mousedown(function(evt) {
  2203. if(!$("#chat").has(evt.target).length > 0) {
  2204. chat.blur();
  2205. }
  2206. });
  2207. document.addEventListener("touchstart", function(event) {
  2208. for(var i in event.changedTouches) {
  2209. var touch = event.changedTouches[i];
  2210. if(!$("#chat").has(touch.target).length > 0) {
  2211. chat.blur();
  2212. }
  2213. }
  2214. });
  2215. $(document).on("keydown", function(evt) {
  2216. if($("#chat").hasClass("chatting")) {
  2217. if(evt.keyCode == 27) {
  2218. chat.blur();
  2219. evt.preventDefault();
  2220. evt.stopPropagation();
  2221. } else if(evt.keyCode == 13) {
  2222. $("#chat input").focus();
  2223. }
  2224. } else if(!gModal && (evt.keyCode == 27 || evt.keyCode == 13)) {
  2225. $("#chat input").focus();
  2226. }
  2227. });
  2228. $("#chat input").on("keydown", function(evt) {
  2229. if(evt.keyCode == 13) {
  2230. var message = $(this).val();
  2231. if(message.length == 0) {
  2232. setTimeout(function() {
  2233. chat.blur();
  2234. }, 100);
  2235. } else if(message.length <= 512) {
  2236. chat.send(message);
  2237. $(this).val("");
  2238. setTimeout(function() {
  2239. chat.blur();
  2240. }, 100);
  2241. }
  2242. evt.preventDefault();
  2243. evt.stopPropagation();
  2244. } else if(evt.keyCode == 27) {
  2245. chat.blur();
  2246. evt.preventDefault();
  2247. evt.stopPropagation();
  2248. } else if(evt.keyCode == 9) {
  2249. evt.preventDefault();
  2250. evt.stopPropagation();
  2251. }
  2252. });
  2253.  
  2254. return {
  2255. show: function() {
  2256. $("#chat").fadeIn();
  2257. },
  2258.  
  2259. hide: function() {
  2260. $("#chat").fadeOut();
  2261. },
  2262.  
  2263. clear: function() {
  2264. $("#chat li").remove();
  2265. },
  2266.  
  2267. scrollToBottom: function() {
  2268. var ele = $("#chat ul").get(0);
  2269. ele.scrollTop = ele.scrollHeight;
  2270. },
  2271.  
  2272. blur: function() {
  2273. if($("#chat").hasClass("chatting")) {
  2274. $("#chat input").get(0).blur();
  2275. $("#chat").removeClass("chatting");
  2276. chat.scrollToBottom();
  2277. captureKeyboard();
  2278. }
  2279. },
  2280.  
  2281. send: function(message) {
  2282. gClient.sendArray([{m:"a", message: message}]);
  2283. },
  2284.  
  2285. receive: function(msg) {
  2286. if(gChatMutes.indexOf(msg.p._id) != -1) return;
  2287.  
  2288. var li = $('<li><span class="name"/><span class="message"/>');
  2289.  
  2290. li.find(".name").text(msg.p.name + ":");
  2291. li.find(".message").text(msg.a);
  2292. li.css("color", msg.p.color || "white");
  2293.  
  2294. $("#chat ul").append(li);
  2295.  
  2296. var eles = $("#chat ul li").get();
  2297. for(var i = 1; i <= 50 && i <= eles.length; i++) {
  2298. eles[eles.length - i].style.opacity = 1.0 - (i * 0.03);
  2299. }
  2300. if(eles.length > 50) {
  2301. eles[0].style.display = "none";
  2302. }
  2303. if(eles.length > 256) {
  2304. $(eles[0]).remove();
  2305. }
  2306.  
  2307. // scroll to bottom if not "chatting" or if not scrolled up
  2308. if(!$("#chat").hasClass("chatting")) {
  2309. chat.scrollToBottom();
  2310. } else {
  2311. var ele = $("#chat ul").get(0);
  2312. if(ele.scrollTop > ele.scrollHeight - ele.offsetHeight - 50)
  2313. chat.scrollToBottom();
  2314. }
  2315.  
  2316. // MODIFICATION: Commands in chat
  2317. // Change: command output is given in console (F12) instead of chat.
  2318. // commands: // wiped out.
  2319.  
  2320. }
  2321. };
  2322. })();
  2323.  
  2324.  
  2325.  
  2326. // MIDI
  2327.  
  2328. ////////////////////////////////////////////////////////////////
  2329.  
  2330. var MIDI_TRANSPOSE = -12;
  2331. var MIDI_KEY_NAMES = ["a-1", "as-1", "b-1"];
  2332. var bare_notes = "c cs d ds e f fs g gs a as b".split(" ");
  2333. for(var oct = 0; oct < 7; oct++) {
  2334. for(var i in bare_notes) {
  2335. MIDI_KEY_NAMES.push(bare_notes[i] + oct);
  2336. }
  2337. }
  2338. MIDI_KEY_NAMES.push("c7");
  2339.  
  2340. (function() {
  2341.  
  2342. if (navigator.requestMIDIAccess) {
  2343. navigator.requestMIDIAccess().then(
  2344. function(midi) {
  2345. console.log(midi);
  2346. function midimessagehandler(evt) {
  2347. if(!evt.target.enabled) return;
  2348. //console.log(evt);
  2349. var channel = evt.data[0] & 0xf;
  2350. var cmd = evt.data[0] >> 4;
  2351. var note_number = evt.data[1];
  2352. var vel = evt.data[2];
  2353. //console.log(channel, cmd, note_number, vel);
  2354. if(cmd == 8 || (cmd == 9 && vel == 0)) {
  2355. // NOTE_OFF
  2356. release(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE]);
  2357. } else if(cmd == 9) {
  2358. // NOTE_ON
  2359. press(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE], vel / 100);
  2360. } else if(cmd == 11) {
  2361. // CONTROL_CHANGE
  2362. if(!gAutoSustain) {
  2363. if(note_number == 64) {
  2364. if(vel > 0) {
  2365. pressSustain();
  2366. } else {
  2367. releaseSustain();
  2368. }
  2369. }
  2370. }
  2371. }
  2372. }
  2373.  
  2374. function plug() {
  2375. if(midi.inputs.size > 0) {
  2376. var inputs = midi.inputs.values();
  2377. for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2378. var input = input_it.value;
  2379. //input.removeEventListener("midimessage", midimessagehandler);
  2380. //input.addEventListener("midimessage", midimessagehandler);
  2381. input.onmidimessage = midimessagehandler;
  2382. if(input.enabled !== false) {
  2383. if (!input.name.includes("Midi Through")) {// lekro added this line. For him he uses a midi through device and didn't want to accidentally create a loop!
  2384. input.enabled = true;
  2385. }
  2386. }
  2387. // This is commented since I don't really need to see this.
  2388. // console.log("input", input);
  2389.  
  2390. }
  2391. }
  2392. if(midi.outputs.size > 0) {
  2393. var outputs = midi.outputs.values();
  2394. for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2395. var output = output_it.value;
  2396. //output.enabled = false; // edit: don't touch
  2397.  
  2398. // The following is for since I want the Midi through output enabled by default.
  2399. if (output.name.includes("Midi Through")) {
  2400. output.enabled = true;
  2401. }
  2402.  
  2403. // The following is commented since I don't need to see all outputs either.
  2404. console.log("output", output);
  2405. }
  2406. gMidiOutTest = function(note_name, vel, delay_ms) {
  2407. var note_number = MIDI_KEY_NAMES.indexOf(note_name);
  2408. if(note_number == -1) return;
  2409. note_number = note_number + 9 - MIDI_TRANSPOSE;
  2410.  
  2411. var outputs = midi.outputs.values();
  2412. for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2413. var output = output_it.value;
  2414. if(output.enabled) {
  2415. output.send([0x90, note_number, vel], window.performance.now() + delay_ms);
  2416. }
  2417. }
  2418. }
  2419. }
  2420. // I don't need to see a window when the thing init's either.
  2421. // showConnections(false);
  2422. }
  2423.  
  2424. midi.addEventListener("statechange", function(evt) {
  2425. if(evt instanceof MIDIConnectionEvent) {
  2426. plug();
  2427. }
  2428. });
  2429.  
  2430. plug();
  2431.  
  2432.  
  2433. var connectionsNotification;
  2434.  
  2435. function showConnections(sticky) {
  2436. //if(document.getElementById("Notification-MIDI-Connections"))
  2437. //sticky = 1; // todo: instead,
  2438. var inputs_ul = document.createElement("ul");
  2439. if(midi.inputs.size > 0) {
  2440. var inputs = midi.inputs.values();
  2441. for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2442. var input = input_it.value;
  2443. var li = document.createElement("li");
  2444. li.connectionId = input.id;
  2445. li.classList.add("connection");
  2446. if(input.enabled) li.classList.add("enabled");
  2447. li.textContent = input.name;
  2448. li.addEventListener("click", function(evt) {
  2449. var inputs = midi.inputs.values();
  2450. for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2451. var input = input_it.value;
  2452. if(input.id === evt.target.connectionId) {
  2453. input.enabled = !input.enabled;
  2454. evt.target.classList.toggle("enabled");
  2455. console.log("click", input);
  2456. return;
  2457. }
  2458. }
  2459. });
  2460. inputs_ul.appendChild(li);
  2461. }
  2462. } else {
  2463. inputs_ul.textContent = "(none)";
  2464. }
  2465. var outputs_ul = document.createElement("ul");
  2466. if(midi.outputs.size > 0) {
  2467. var outputs = midi.outputs.values();
  2468. for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2469. var output = output_it.value;
  2470. var li = document.createElement("li");
  2471. li.connectionId = output.id;
  2472. li.classList.add("connection");
  2473. if(output.enabled) li.classList.add("enabled");
  2474. li.textContent = output.name;
  2475. li.addEventListener("click", function(evt) {
  2476. var outputs = midi.outputs.values();
  2477. for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2478. var output = output_it.value;
  2479. if(output.id === evt.target.connectionId) {
  2480. output.enabled = !output.enabled;
  2481. evt.target.classList.toggle("enabled");
  2482. console.log("click", output);
  2483. return;
  2484. }
  2485. }
  2486. });
  2487. outputs_ul.appendChild(li);
  2488. }
  2489. } else {
  2490. outputs_ul.textContent = "(none)";
  2491. }
  2492. var div = document.createElement("div");
  2493. var h1 = document.createElement("h1");
  2494. h1.textContent = "Inputs";
  2495. div.appendChild(h1);
  2496. div.appendChild(inputs_ul);
  2497. h1 = document.createElement("h1");
  2498. h1.textContent = "Outputs";
  2499. div.appendChild(h1);
  2500. div.appendChild(outputs_ul);
  2501. connectionsNotification = new Notification({"id":"MIDI-Connections", "title":"MIDI Connections","duration":sticky?"-1":"4500","html":div,"target":"#midi-btn"});
  2502. }
  2503.  
  2504. document.getElementById("midi-btn").addEventListener("click", function(evt) {
  2505. if(!document.getElementById("Notification-MIDI-Connections"))
  2506. showConnections(true);
  2507. else {
  2508. connectionsNotification.close();
  2509. }
  2510. });
  2511. },
  2512. function(err){
  2513. console.log(err);
  2514. } );
  2515. }
  2516. })();
  2517.  
  2518.  
  2519. // bug supply
  2520.  
  2521. ////////////////////////////////////////////////////////////////
  2522.  
  2523. window.onerror = function(message, url, line) {
  2524. var url = url || "(no url)";
  2525. var line = line || "(no line)";
  2526. // errors in socket.io
  2527. if(url.indexOf("socket.io.js") !== -1) {
  2528. if(message.indexOf("INVALID_STATE_ERR") !== -1) return;
  2529. if(message.indexOf("InvalidStateError") !== -1) return;
  2530. if(message.indexOf("DOM Exception 11") !== -1) return;
  2531. if(message.indexOf("Property 'open' of object #<c> is not a function") !== -1) return;
  2532. if(message.indexOf("Cannot call method 'close' of undefined") !== -1) return;
  2533. if(message.indexOf("Cannot call method 'close' of null") !== -1) return;
  2534. if(message.indexOf("Cannot call method 'onClose' of null") !== -1) return;
  2535. if(message.indexOf("Cannot call method 'payload' of null") !== -1) return;
  2536. if(message.indexOf("Unable to get value of the property 'close'") !== -1) return;
  2537. if(message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) return;
  2538. if(message.indexOf("Unable to get property 'close' of undefined or null reference") !== -1) return;
  2539. if(message.indexOf("Unable to get value of the property 'close': object is null or undefined") !== -1) return;
  2540. if(message.indexOf("this.transport is null") !== -1) return;
  2541. }
  2542. // errors in soundmanager2
  2543. if(url.indexOf("soundmanager2.js") !== -1) {
  2544. // operation disabled in safe mode?
  2545. if(message.indexOf("Could not complete the operation due to error c00d36ef") !== -1) return;
  2546. if(message.indexOf("_s.o._setVolume is not a function") !== -1) return;
  2547. }
  2548. // errors in midibridge
  2549. if(url.indexOf("midibridge") !== -1) {
  2550. if(message.indexOf("Error calling method on NPObject") !== -1) return;
  2551. }
  2552. // too many failing extensions injected in my html
  2553. if(url.indexOf(".js") !== url.length - 3) return;
  2554. // extensions inject cross-domain embeds too
  2555. if(url.toLowerCase().indexOf("multiplayerpiano.com") == -1) return;
  2556.  
  2557. // errors in my code
  2558. if(url.indexOf("script.js") !== -1) {
  2559. if(message.indexOf("Object [object Object] has no method 'on'") !== -1) return;
  2560. if(message.indexOf("Object [object Object] has no method 'off'") !== -1) return;
  2561. if(message.indexOf("Property '$' of object [object Object] is not a function") !== -1) return;
  2562. }
  2563.  
  2564. var enc = "/bugreport/"
  2565. + (message ? encodeURIComponent(message) : "") + "/"
  2566. + (url ? encodeURIComponent(url) : "") + "/"
  2567. + (line ? encodeURIComponent(line) : "");
  2568. var img = new Image();
  2569. img.src = enc;
  2570. };
  2571.  
  2572.  
  2573. // API
  2574. window.MPP = {
  2575. press: press,
  2576. release: release,
  2577. piano: gPiano,
  2578. client: gClient,
  2579. chat: chat
  2580. };
  2581.  
  2582.  
  2583. // record mp3
  2584. (function() {
  2585. var button = document.querySelector("#record-btn");
  2586. var audio = MPP.piano.audio;
  2587. var context = audio.context;
  2588. var encoder_sample_rate = 44100;
  2589. var encoder_kbps = 128;
  2590. var encoder = null;
  2591. var scriptProcessorNode = context.createScriptProcessor(4096, 2, 2);
  2592. var recording = false;
  2593. var recording_start_time = 0;
  2594. var mp3_buffer = [];
  2595. button.addEventListener("click", function(evt) {
  2596. if(!recording) {
  2597. // start recording
  2598. mp3_buffer = [];
  2599. encoder = new lamejs.Mp3Encoder(2, encoder_sample_rate, encoder_kbps);
  2600. scriptProcessorNode.onaudioprocess = onAudioProcess;
  2601. audio.masterGain.connect(scriptProcessorNode);
  2602. scriptProcessorNode.connect(context.destination);
  2603. recording_start_time = Date.now();
  2604. recording = true;
  2605. button.textContent = "Stop Recording";
  2606. button.classList.add("stuck");
  2607. 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:multiplayerpiano.com@gmail.com\">multiplayerpiano.com@gmail.com</a>.", "duration": 10000});
  2608. } else {
  2609. // stop recording
  2610. var mp3buf = encoder.flush();
  2611. mp3_buffer.push(mp3buf);
  2612. var blob = new Blob(mp3_buffer, {type: "audio/mp3"});
  2613. var url = URL.createObjectURL(blob);
  2614. scriptProcessorNode.onaudioprocess = null;
  2615. audio.masterGain.disconnect(scriptProcessorNode);
  2616. scriptProcessorNode.disconnect(context.destination);
  2617. recording = false;
  2618. button.textContent = "Record MP3";
  2619. button.classList.remove("stuck");
  2620. 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:multiplayerpiano.com@gmail.com\">multiplayerpiano.com@gmail.com</a>.", "duration": 0});
  2621. }
  2622. });
  2623. function onAudioProcess(evt) {
  2624. var inputL = evt.inputBuffer.getChannelData(0);
  2625. var inputR = evt.inputBuffer.getChannelData(1);
  2626. var mp3buf = encoder.encodeBuffer(convert16(inputL), convert16(inputR));
  2627. mp3_buffer.push(mp3buf);
  2628. }
  2629. function convert16(samples) {
  2630. var len = samples.length;
  2631. var result = new Int16Array(len);
  2632. for(var i = 0; i < len; i++) {
  2633. result[i] = 0x8000 * samples[i];
  2634. }
  2635. return(result);
  2636. }
  2637. })();
  2638.  
  2639.  
  2640.  
  2641.  
  2642.  
  2643.  
  2644.  
  2645. // synth
  2646. var enableSynth = false;
  2647. var audio = gPiano.audio;
  2648. var context = gPiano.audio.context;
  2649. var synth_gain = context.createGain();
  2650. synth_gain.gain.value = 0.05;
  2651. synth_gain.connect(audio.synthGain);
  2652.  
  2653. var osc_types = ["sine", "square", "sawtooth", "triangle"];
  2654. var osc_type_index = 1;
  2655.  
  2656. var osc1_type = "square";
  2657. var osc1_attack = 0;
  2658. var osc1_decay = 0.2;
  2659. var osc1_sustain = 0.5;
  2660. var osc1_release = 2.0;
  2661.  
  2662. function synthVoice(note_name, time) {
  2663. var note_number = MIDI_KEY_NAMES.indexOf(note_name);
  2664. note_number = note_number + 9 - MIDI_TRANSPOSE;
  2665. var freq = Math.pow(2, (note_number - 69) / 12) * 440.0;
  2666. this.osc = context.createOscillator();
  2667. this.osc.type = osc1_type;
  2668. this.osc.frequency.value = freq;
  2669. this.gain = context.createGain();
  2670. this.gain.gain.value = 0;
  2671. this.osc.connect(this.gain);
  2672. this.gain.connect(synth_gain);
  2673. this.osc.start(time);
  2674. this.gain.gain.setValueAtTime(0, time);
  2675. this.gain.gain.linearRampToValueAtTime(1, time + osc1_attack);
  2676. this.gain.gain.linearRampToValueAtTime(osc1_sustain, time + osc1_attack + osc1_decay);
  2677. }
  2678.  
  2679. synthVoice.prototype.stop = function(time) {
  2680. //this.gain.gain.setValueAtTime(osc1_sustain, time);
  2681. this.gain.gain.linearRampToValueAtTime(0, time + osc1_release);
  2682. this.osc.stop(time + osc1_release);
  2683. };
  2684.  
  2685. (function() {
  2686. var button = document.getElementById("synth-btn");
  2687. var notification;
  2688.  
  2689. button.addEventListener("click", function() {
  2690. if(notification) {
  2691. notification.close();
  2692. } else {
  2693. showSynth();
  2694. }
  2695. });
  2696.  
  2697. function showSynth() {
  2698.  
  2699. var html = document.createElement("div");
  2700.  
  2701. // on/off button
  2702. (function() {
  2703. var button = document.createElement("input");
  2704. mixin(button, {type: "button", value: "ON/OFF", className: enableSynth ? "switched-on" : "switched-off"});
  2705. button.addEventListener("click", function(evt) {
  2706. enableSynth = !enableSynth;
  2707. button.className = enableSynth ? "switched-on" : "switched-off";
  2708. if(!enableSynth) {
  2709. // stop all
  2710. for(var i in audio.playings) {
  2711. if(!audio.playings.hasOwnProperty(i)) continue;
  2712. var playing = audio.playings[i];
  2713. if(playing && playing.voice) {
  2714. playing.voice.osc.stop();
  2715. playing.voice = undefined;
  2716. }
  2717. }
  2718. }
  2719. });
  2720. html.appendChild(button);
  2721. })();
  2722.  
  2723. // mix
  2724. var knob = document.createElement("canvas");
  2725. mixin(knob, {width: 32, height: 32, className: "knob"});
  2726. html.appendChild(knob);
  2727. knob = new Knob(knob, 0, 100, 0.1, 50, "mix", "%");
  2728. knob.on("change", function(k) {
  2729. var mix = k.value / 100;
  2730. audio.pianoGain.gain.value = 1 - mix;
  2731. audio.synthGain.gain.value = mix;
  2732. });
  2733. knob.emit("change", knob);
  2734.  
  2735. // osc1 type
  2736. (function() {
  2737. osc1_type = osc_types[osc_type_index];
  2738. var button = document.createElement("input");
  2739. mixin(button, {type: "button", value: osc_types[osc_type_index]});
  2740. button.addEventListener("click", function(evt) {
  2741. if(++osc_type_index >= osc_types.length) osc_type_index = 0;
  2742. osc1_type = osc_types[osc_type_index];
  2743. button.value = osc1_type;
  2744. });
  2745. html.appendChild(button);
  2746. })();
  2747.  
  2748. // osc1 attack
  2749. var knob = document.createElement("canvas");
  2750. mixin(knob, {width: 32, height: 32, className: "knob"});
  2751. html.appendChild(knob);
  2752. knob = new Knob(knob, 0, 1, 0.001, osc1_attack, "osc1 attack", "s");
  2753. knob.on("change", function(k) {
  2754. osc1_attack = k.value;
  2755. });
  2756. knob.emit("change", knob);
  2757.  
  2758. // osc1 decay
  2759. var knob = document.createElement("canvas");
  2760. mixin(knob, {width: 32, height: 32, className: "knob"});
  2761. html.appendChild(knob);
  2762. knob = new Knob(knob, 0, 2, 0.001, osc1_decay, "osc1 decay", "s");
  2763. knob.on("change", function(k) {
  2764. osc1_decay = k.value;
  2765. });
  2766. knob.emit("change", knob);
  2767.  
  2768. var knob = document.createElement("canvas");
  2769. mixin(knob, {width: 32, height: 32, className: "knob"});
  2770. html.appendChild(knob);
  2771. knob = new Knob(knob, 0, 1, 0.001, osc1_sustain, "osc1 sustain", "x");
  2772. knob.on("change", function(k) {
  2773. osc1_sustain = k.value;
  2774. });
  2775. knob.emit("change", knob);
  2776.  
  2777. // osc1 release
  2778. var knob = document.createElement("canvas");
  2779. mixin(knob, {width: 32, height: 32, className: "knob"});
  2780. html.appendChild(knob);
  2781. knob = new Knob(knob, 0, 2, 0.001, osc1_release, "osc1 release", "s");
  2782. knob.on("change", function(k) {
  2783. osc1_release = k.value;
  2784. });
  2785. knob.emit("change", knob);
  2786.  
  2787.  
  2788.  
  2789. var div = document.createElement("div");
  2790. div.innerHTML = "<br><br><br><br><center>this space intentionally left blank</center><br><br><br><br>";
  2791. html.appendChild(div);
  2792.  
  2793.  
  2794.  
  2795. // notification
  2796. notification = new Notification({title: "Synthesize", html: html, duration: -1, target: "#synth-btn"});
  2797. notification.on("close", function() {
  2798. var tip = document.getElementById("tooltip");
  2799. if(tip) tip.parentNode.removeChild(tip);
  2800. notification = null;
  2801. });
  2802. }
  2803. })();
  2804.  
  2805.  
  2806.  
  2807. // more button
  2808. (function() {
  2809. var loaded = false;
  2810. setTimeout(function() {
  2811. $("#social").fadeIn(250);
  2812. $("#more-button").click(function() {
  2813. openModal("#more");
  2814. if(loaded === false) {
  2815. $.get("/more.html").success(function(data) {
  2816. loaded = true;
  2817. var items = $(data).find(".item");
  2818. if(items.length > 0) {
  2819. $("#more .items").append(items);
  2820. }
  2821. try {
  2822. var ele = document.getElementById("email");
  2823. var email = ele.getAttribute("obscured").replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});
  2824. ele.href = "mailto:" + email;
  2825. ele.textContent = email;
  2826.  
  2827. // MODIFICATION: LINK TO DOCUMENTATION OF THIS MODIFICATION
  2828.  
  2829. for (var i = 0; i < items.length; i++) {
  2830. var rect_empty_str = 'Hey this rectangle is completely empty!';
  2831. var rect_inner_div = items[i];
  2832. var rect_inner_p = items[i].getElementsByTagName("div")[0].getElementsByTagName("p")[0];
  2833. if (rect_inner_p.innerHTML.includes(rect_empty_str)) {
  2834. rect_inner_div.setAttribute("style","background: #eedddd; transition: background 0.25s;"); // I want it colored differently since this isn't vanilla script anymore
  2835. var ban_script_docs_str = "";
  2836. ban_script_docs_str += '<b>You have a script modification loaded! (Congratulations?)</b><br><br>';
  2837. ban_script_docs_str += 'You are using <a href="http://www.github.com/lekro">lekro</a>\'s <b>autoban script</b>.<br>';
  2838. ban_script_docs_str += 'Remember to use this modification wisely.<br>';
  2839. ban_script_docs_str += 'You can read the documentation and code <a href="https://gist.github.com/lekro/966fc6f58c16702985b7a92a21ee585b">here</a>!';
  2840.  
  2841. rect_inner_p.innerHTML = rect_inner_p.innerHTML.replace(new RegExp(rect_empty_str, 'g'), ban_script_docs_str);
  2842. }
  2843. }
  2844.  
  2845. // end modification
  2846. } catch(e) { }
  2847. });
  2848.  
  2849. }
  2850. });
  2851. }, 5000);
  2852. })();
  2853. });
  2854.  
  2855. // I don't want ads anyway ;-;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement