Advertisement
LGPvS

Multiplayer-Piano script.js (it my first paste).

Aug 19th, 2016
1,614
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 94.62 KB | None | 0 0
  1. $(function() {
  2.  
  3. var test_mode = (window.location.hash && window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i));
  4.  
  5. var gSeeOwnCursor = (window.location.hash && window.location.hash.match(/^(?:#.+)*#seeowncursor(?:#.+)*$/i));
  6.  
  7. var gMidiOutTest = (window.location.hash && window.location.hash.match(/^(?:#.+)*#midiout(?:#.+)*$/i)); // todo this is no longer needed
  8.  
  9. if (!Array.prototype.indexOf) {
  10. Array.prototype.indexOf = function(elt /*, from*/) {
  11. var len = this.length >>> 0;
  12. var from = Number(arguments[1]) || 0;
  13. from = (from < 0) ? Math.ceil(from) : Math.floor(from);
  14. if (from < 0) from += len;
  15. for (; from < len; from++) {
  16. if (from in this && this[from] === elt) return from;
  17. }
  18. return -1;
  19. };
  20. }
  21.  
  22. window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame
  23. || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
  24. || function (cb) { setTimeout(cb, 1000 / 30); };
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32. var gSoundPath = "/mp3/";
  33. var gSoundExt = ".wav.mp3";
  34.  
  35. // Yoshify.
  36. if ((window.location.hash && window.location.hash.match(/^(?:#.+)*#Piano_Great_and_Soft(?:#.+)*$/i))) {
  37. gSoundPath = "https://dl.dropboxusercontent.com/u/216104606/GreatAndSoftPiano/";
  38. gSoundExt = ".mp3";
  39. }
  40.  
  41. if ((window.location.hash && window.location.hash.match(/^(?:#.+)*#Piano_Loud_and_Proud(?:#.+)*$/i))) {
  42. gSoundPath = "https://dl.dropboxusercontent.com/u/216104606/LoudAndProudPiano/";
  43. gSoundExt = ".mp3";
  44. }
  45.  
  46. // electrashave
  47. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#NewPiano(?:#.+)*$/i))) {
  48. gSoundPath = "https://dl.dropboxusercontent.com/u/258840068/CustomSounds/NewPiano/";
  49. gSoundExt = ".mp3";
  50. }
  51.  
  52. // Ethan Walsh
  53. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#HDPiano(?:#.+)*$/i))) {
  54. gSoundPath = "https://dl.dropboxusercontent.com/u/258840068/CustomSounds/HDPiano/";
  55. gSoundExt = ".wav";
  56. }
  57. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#Harpischord(?:#.+)*$/i))) {
  58. gSoundPath = "https://dl.dropboxusercontent.com/u/24213061/Harpischord/";
  59. gSoundExt = ".wav";
  60. }
  61. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#ClearPiano(?:#.+)*$/i))) {
  62. gSoundPath = "https://dl.dropboxusercontent.com/u/24213061/ClearPiano/";
  63. gSoundExt = ".wav";
  64. }
  65.  
  66. // Alexander Holmfjeld
  67. if((window.location.hash && window.location.hash.match(/^(?:#.+)*#Klaver(?:#.+)*$/i))) {
  68. gSoundPath = "https://dl.dropboxusercontent.com/u/70730519/Klaver/";
  69. gSoundExt = ".wav";
  70. }
  71.  
  72.  
  73.  
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80.  
  81.  
  82.  
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89.  
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96.  
  97.  
  98.  
  99.  
  100.  
  101. var DEFAULT_VELOCITY = 0.5;
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146. var TIMING_TARGET = 1000;
  147.  
  148.  
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166. // Utility
  167.  
  168. ////////////////////////////////////////////////////////////////
  169.  
  170.  
  171.  
  172. var Rect = function(x, y, w, h) {
  173. this.x = x;
  174. this.y = y;
  175. this.w = w;
  176. this.h = h;
  177. this.x2 = x + w;
  178. this.y2 = y + h;
  179. };
  180. Rect.prototype.contains = function(x, y) {
  181. return (x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2);
  182. };
  183.  
  184.  
  185.  
  186.  
  187.  
  188.  
  189.  
  190.  
  191.  
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199. // performing translation
  200.  
  201. ////////////////////////////////////////////////////////////////
  202.  
  203. var Translation = (function() {
  204. var strings = {
  205. "people are playing": {
  206. "pt": "pessoas estГЈo jogando",
  207. "es": "personas estГЎn jugando",
  208. "ru": "человек играет",
  209. "fr": "personnes jouent",
  210. "ja": "дєєгЃЊйЃЉг‚“гЃ§гЃ„г‚‹",
  211. "de": "Leute spielen",
  212. "zh": "人被打",
  213. "nl": "mensen spelen",
  214. "pl": "osГіb grajД…",
  215. "hu": "ember jГЎtszik"
  216. },
  217. "New Room...": {
  218. "pt": "Nova Sala ...",
  219. "es": "Nueva sala de...",
  220. "ru": "Новый номер...",
  221. "ja": "新しい部屋",
  222. "zh": "ж–°ж€їй—ґ",
  223. "nl": "nieuwe Kamer",
  224. "hu": "Гєj szoba"
  225. },
  226. "room name": {
  227. "pt": "nome da sala",
  228. "es": "sala de nombre",
  229. "ru": "название комнаты",
  230. "fr": "nom de la chambre",
  231. "ja": "гѓ«гѓјгѓ еђЌ",
  232. "de": "Raumnamen",
  233. "zh": "ж€їй—ґеђЌз§°",
  234. "nl": "kamernaam",
  235. "pl": "nazwa pokГіj",
  236. "hu": "szoba neve"
  237. },
  238. "Visible (open to everyone)": {
  239. "pt": "VisГ­vel (aberto a todos)",
  240. "es": "Visible (abierto a todo el mundo)",
  241. "ru": "Visible (открытый для всех)",
  242. "fr": "Visible (ouvert Г tous)",
  243. "ja": "目に見える(誰にでも開いている)",
  244. "de": "Sichtbar (offen fГјr alle)",
  245. "zh": "еЏЇи§Ѓпј€еђ‘ж‰Ђжњ‰дєєејЂж”ѕпј‰",
  246. "nl": "Zichtbaar (open voor iedereen)",
  247. "pl": "Widoczne (otwarte dla wszystkich)",
  248. "hu": "LГЎthatГі (nyitott mindenki szГЎmГЎra)"
  249. },
  250. "Enable Chat": {
  251. "pt": "Ativar bate-papo",
  252. "es": "Habilitar chat",
  253. "ru": "Включить чат",
  254. "fr": "Activer discuter",
  255. "ja": "гѓЃгѓЈгѓѓгѓ€г‚’жњ‰еЉ№гЃ«гЃ™г‚‹",
  256. "de": "aktivieren Sie chatten",
  257. "zh": "启用聊天",
  258. "nl": "Chat inschakelen",
  259. "pl": "WЕ‚Д…cz czat",
  260. "hu": "a csevegГ©st"
  261. },
  262. "Play Alone": {
  263. "pt": "Jogar Sozinho",
  264. "es": "Jugar Solo",
  265. "ru": "Играть в одиночку",
  266. "fr": "Jouez Seul",
  267. "ja": "一人でプレイ",
  268. "de": "Alleine Spielen",
  269. "zh": "独自玩耍",
  270. "nl": "Speel Alleen",
  271. "pl": "Zagraj sam",
  272. "hu": "JГЎtssz egyedГјl"
  273. }
  274. // todo: it, tr, th, sv, ar, fi, nb, da, sv, he, cs, ko, ro, vi, id, nb, el, sk, bg, lt, sl, hr
  275. // todo: Connecting, Offline mode, input placeholder, Notifications
  276. };
  277.  
  278. var setLanguage = function(lang) {
  279. language = lang
  280. };
  281.  
  282. var getLanguage = function() {
  283. if(window.navigator && navigator.language && navigator.language.length >= 2) {
  284. return navigator.language.substr(0, 2).toLowerCase();
  285. } else {
  286. return "en";
  287. }
  288. };
  289.  
  290. var get = function(text, lang) {
  291. if(typeof lang === "undefined") lang = language;
  292. var row = strings[text];
  293. if(row == undefined) return text;
  294. var string = row[lang];
  295. if(string == undefined) return text;
  296. return string;
  297. };
  298.  
  299. var perform = function(lang) {
  300. if(typeof lang === "undefined") lang = language;
  301. $(".translate").each(function(i, ele) {
  302. var th = $(this);
  303. if(ele.tagName && ele.tagName.toLowerCase() == "input") {
  304. if(typeof ele.placeholder != "undefined") {
  305. th.attr("placeholder", get(th.attr("placeholder"), lang))
  306. }
  307. } else {
  308. th.text(get(th.text(), lang));
  309. }
  310. });
  311. };
  312.  
  313. var language = getLanguage();
  314.  
  315. return {
  316. setLanguage: setLanguage,
  317. getLanguage: getLanguage,
  318. get: get,
  319. perform: perform
  320. };
  321. })();
  322.  
  323. Translation.perform();
  324.  
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337.  
  338.  
  339. // AudioEngine classes
  340.  
  341. ////////////////////////////////////////////////////////////////
  342.  
  343. var AudioEngine = function() {
  344. };
  345.  
  346. AudioEngine.prototype.init = function(cb) {
  347. this.volume = 0.6;
  348. this.sounds = {};
  349. return this;
  350. };
  351.  
  352. AudioEngine.prototype.load = function(id, url, cb) {
  353. };
  354.  
  355. AudioEngine.prototype.play = function() {
  356. };
  357.  
  358. AudioEngine.prototype.stop = function() {
  359. };
  360.  
  361. AudioEngine.prototype.setVolume = function(vol) {
  362. this.volume = vol;
  363. };
  364.  
  365.  
  366. AudioEngineWeb = function() {
  367. this.threshold = 10;
  368. this.worker = new Worker("/workerTimer.js"); //must be same origin
  369. var self = this;
  370. this.worker.onmessage = function(event)
  371. {
  372. if(event.data.args)
  373. if(event.data.args.action==0)
  374. {
  375. self.actualPlay(event.data.args.id, event.data.args.vol, event.data.args.time, event.data.args.part_id);
  376. }
  377. else
  378. {
  379. self.actualStop(event.data.args.id, event.data.args.time, event.data.args.part_id);
  380. }
  381. }
  382. };
  383.  
  384. AudioEngineWeb.prototype = new AudioEngine();
  385.  
  386. AudioEngineWeb.prototype.init = function(cb) {
  387. AudioEngine.prototype.init.call(this);
  388.  
  389. this.context = new AudioContext();
  390.  
  391. this.masterGain = this.context.createGain();
  392. this.masterGain.connect(this.context.destination);
  393. this.masterGain.gain.value = this.volume;
  394.  
  395. this.limiterNode = this.context.createDynamicsCompressor();
  396. this.limiterNode.threshold.value = -10;
  397. this.limiterNode.knee.value = 0;
  398. this.limiterNode.ratio.value = 20;
  399. this.limiterNode.attack.value = 0;
  400. this.limiterNode.release.value = 0.1;
  401. this.limiterNode.connect(this.masterGain);
  402.  
  403. // for synth mix
  404. this.pianoGain = this.context.createGain();
  405. this.pianoGain.gain.value = 0.5;
  406. this.pianoGain.connect(this.limiterNode);
  407. this.synthGain = this.context.createGain();
  408. this.synthGain.gain.value = 0.5;
  409. this.synthGain.connect(this.limiterNode);
  410.  
  411. this.playings = {};
  412.  
  413. if(cb) setTimeout(cb, 0);
  414. return this;
  415. };
  416.  
  417. AudioEngineWeb.prototype.load = function(id, url, cb) {
  418. var audio = this;
  419. var req = new XMLHttpRequest();
  420. req.open("GET", url);
  421. req.responseType = "arraybuffer";
  422. req.addEventListener("readystatechange", function(evt) {
  423. if(req.readyState !== 4) return;
  424. try {
  425. audio.context.decodeAudioData(req.response, function(buffer) {
  426. audio.sounds[id] = buffer;
  427. if(cb) cb();
  428. });
  429. } catch(e) {
  430. /*throw new Error(e.message
  431. + " / id: " + id
  432. + " / url: " + url
  433. + " / status: " + req.status
  434. + " / ArrayBuffer: " + (req.response instanceof ArrayBuffer)
  435. + " / byteLength: " + (req.response && req.response.byteLength ? req.response.byteLength : "undefined"));*/
  436. new Notification({id: "audio-download-error", title: "Problem", text: "For some reason, an audio download failed with a status of " + req.status + ". ",
  437. target: "#piano", duration: 10000});
  438. }
  439. });
  440. req.send();
  441. };
  442.  
  443. AudioEngineWeb.prototype.actualPlay = function(id, vol, time, part_id) { //the old play(), but with time insted of delay_ms.
  444. if(!this.sounds.hasOwnProperty(id)) return;
  445. var source = this.context.createBufferSource();
  446. source.buffer = this.sounds[id];
  447. var gain = this.context.createGain();
  448. gain.gain.value = vol;
  449. source.connect(gain);
  450. gain.connect(this.pianoGain);
  451. source.start(time);
  452. // Patch from ste-art remedies stuttering under heavy load
  453. if(this.playings[id]) {
  454. var playing = this.playings[id];
  455. playing.gain.gain.setValueAtTime(playing.gain.gain.value, time);
  456. playing.gain.gain.linearRampToValueAtTime(0.0, time + 0.2);
  457. playing.source.stop(time + 0.21);
  458. if(enableSynth && playing.voice) {
  459. playing.voice.stop(time);
  460. }
  461. }
  462. this.playings[id] = {"source": source, "gain": gain, "part_id": part_id};
  463.  
  464. if(enableSynth) {
  465. this.playings[id].voice = new synthVoice(id, time);
  466. }
  467. }
  468.  
  469. AudioEngineWeb.prototype.play = function(id, vol, delay_ms, part_id)
  470. {
  471. if(!this.sounds.hasOwnProperty(id)) return;
  472. var time = this.context.currentTime + (delay_ms / 1000); //calculate time on note receive.
  473. var delay = delay_ms - this.threshold;
  474. if(delay<=0) this.actualPlay(id, vol, time, part_id);
  475. else {
  476. 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.
  477. }
  478. }
  479.  
  480. AudioEngineWeb.prototype.actualStop = function(id, time, part_id) {
  481. if(this.playings.hasOwnProperty(id) && this.playings[id] && this.playings[id].part_id === part_id) {
  482. var gain = this.playings[id].gain.gain;
  483. gain.setValueAtTime(gain.value, time);
  484. gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16);
  485. gain.linearRampToValueAtTime(0.0, time + 0.4);
  486. this.playings[id].source.stop(time + 0.41);
  487.  
  488.  
  489. if(this.playings[id].voice) {
  490. this.playings[id].voice.stop(time);
  491. }
  492.  
  493. this.playings[id] = null;
  494. }
  495. };
  496.  
  497. AudioEngineWeb.prototype.stop = function(id, delay_ms, part_id) {
  498. var time = this.context.currentTime + (delay_ms / 1000);
  499. var delay = delay_ms - this.threshold;
  500. if(delay<=0) this.actualStop(id, time, part_id);
  501. else {
  502. this.worker.postMessage({delay:delay,args:{action:1/*stop*/, id:id, time:time, part_id:part_id}});
  503. }
  504. };
  505.  
  506. AudioEngineWeb.prototype.setVolume = function(vol) {
  507. AudioEngine.prototype.setVolume.call(this, vol);
  508. this.masterGain.gain.value = this.volume;
  509. };
  510.  
  511.  
  512.  
  513.  
  514.  
  515.  
  516.  
  517.  
  518.  
  519.  
  520.  
  521.  
  522.  
  523.  
  524.  
  525. // VolumeSlider inst
  526.  
  527. ////////////////////////////////////////////////////////////////
  528.  
  529. var VolumeSlider = function(ele, cb) {
  530. this.rootElement = ele;
  531. this.cb = cb;
  532. var range = document.createElement("input");
  533. try {
  534. range.type = "range";
  535. } catch(e) {
  536. // hello, IE9
  537. }
  538. if(range.min !== undefined) {
  539. this.range = range;
  540. this.rootElement.appendChild(range);
  541. range.className = "volume-slider";
  542. range.min = "0.0";
  543. range.max = "1.0";
  544. range.step = "0.01";
  545. $(range).on("change", function(evt) {
  546. cb(range.value);
  547. });
  548. } else {
  549. if(window.console) console.log("warn: no slider");
  550. // todo
  551. }
  552. };
  553.  
  554. VolumeSlider.prototype.set = function(v) {
  555. if(this.range !== undefined) {
  556. this.range.value = v;
  557. } else {
  558. // todo
  559. }
  560. };
  561.  
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568.  
  569.  
  570.  
  571.  
  572.  
  573.  
  574.  
  575.  
  576.  
  577.  
  578.  
  579.  
  580. // Renderer classes
  581.  
  582. ////////////////////////////////////////////////////////////////
  583.  
  584. var Renderer = function() {
  585. };
  586.  
  587. Renderer.prototype.init = function(piano) {
  588. this.piano = piano;
  589. this.resize();
  590. return this;
  591. };
  592.  
  593. Renderer.prototype.resize = function(width, height) {
  594. if(typeof width == "undefined") width = $(this.piano.rootElement).width();
  595. if(typeof height == "undefined") height = Math.floor(width * 0.2);
  596. $(this.piano.rootElement).css({"height": height + "px", marginTop: Math.floor($(window).height() / 2 - height / 2) + "px"});
  597. this.width = width;
  598. this.height = height;
  599. };
  600.  
  601. Renderer.prototype.visualize = function(key, color) {
  602. };
  603.  
  604.  
  605.  
  606.  
  607. var DOMRenderer = function() {
  608. Renderer.call(this);
  609. };
  610.  
  611. DOMRenderer.prototype = new Renderer();
  612.  
  613. DOMRenderer.prototype.init = function(piano) {
  614. // create keys in dom
  615. for(var i in piano.keys) {
  616. if(!piano.keys.hasOwnProperty(i)) continue;
  617. var key = piano.keys[i];
  618. var ele = document.createElement("div");
  619. key.domElement = ele;
  620. piano.rootElement.appendChild(ele);
  621. // "key sharp cs cs2"
  622. ele.note = key.note;
  623. ele.id = key.note;
  624. ele.className = "key " + (key.sharp ? "sharp " : " ") + key.baseNote + " " + key.note + " loading";
  625. var table = $('<table width="100%" height="100%" style="pointer-events:none"></table>');
  626. var td = $('<td valign="bottom"></td>');
  627. table.append(td);
  628. td.valign = "bottom";
  629. $(ele).append(table);
  630. }
  631. // add event listeners
  632. var mouse_down = false;
  633. $(piano.rootElement).mousedown(function(event) {
  634. // todo: IE10 doesn't support the pointer-events css rule on the "blips"
  635. var ele = event.target;
  636. if($(ele).hasClass("key") && piano.keys.hasOwnProperty(ele.note)) {
  637. var key = piano.keys[ele.note];
  638. press(key.note);
  639. mouse_down = true;
  640. event.stopPropagation();
  641. };
  642. //event.preventDefault();
  643. });
  644. piano.rootElement.addEventListener("touchstart", function(event) {
  645. for(var i in event.changedTouches) {
  646. var ele = event.changedTouches[i].target;
  647. if($(ele).hasClass("key") && piano.keys.hasOwnProperty(ele.note)) {
  648. var key = piano.keys[ele.note];
  649. press(key.note);
  650. mouse_down = true;
  651. event.stopPropagation();
  652. }
  653. }
  654. //event.preventDefault();
  655. }, false);
  656. $(window).mouseup(function(event) {
  657. mouse_down = false;
  658. });
  659. /*$(piano.rootElement).mouseover(function(event) {
  660. if(!mouse_down) return;
  661. var ele = event.target;
  662. if($(ele).hasClass("key") && piano.keys.hasOwnProperty(ele.note)) {
  663. var key = piano.keys[ele.note];
  664. press(key.note);
  665. }
  666. });*/
  667.  
  668. Renderer.prototype.init.call(this, piano);
  669. return this;
  670. };
  671.  
  672. DOMRenderer.prototype.resize = function(width, height) {
  673. Renderer.prototype.resize.call(this, width, height);
  674. };
  675.  
  676. DOMRenderer.prototype.visualize = function(key, color) {
  677. var k = $(key.domElement);
  678. k.addClass("play");
  679. setTimeout(function(){
  680. k.removeClass("play");
  681. }, 100);
  682. // "blips"
  683. var d = $('<div style="width:100%;height:10%;margin:0;padding:0">&nbsp;</div>');
  684. d.css("background", color);
  685. k.find("td").append(d);
  686. d.fadeOut(1000, function(){
  687. d.remove();
  688. });
  689. };
  690.  
  691.  
  692.  
  693.  
  694. var CanvasRenderer = function() {
  695. Renderer.call(this);
  696. };
  697.  
  698. CanvasRenderer.prototype = new Renderer();
  699.  
  700. CanvasRenderer.prototype.init = function(piano) {
  701. this.canvas = document.createElement("canvas");
  702. this.ctx = this.canvas.getContext("2d");
  703. piano.rootElement.appendChild(this.canvas);
  704.  
  705. Renderer.prototype.init.call(this, piano); // calls resize()
  706.  
  707. // create render loop
  708. var self = this;
  709. var render = function() {
  710. self.redraw();
  711. requestAnimationFrame(render);
  712. };
  713. requestAnimationFrame(render);
  714.  
  715. // add event listeners
  716. var mouse_down = false;
  717. var last_key = null;
  718. $(piano.rootElement).mousedown(function(event) {
  719. mouse_down = true;
  720. //event.stopPropagation();
  721. event.preventDefault();
  722.  
  723. var pos = CanvasRenderer.translateMouseEvent(event);
  724. var hit = self.getHit(pos.x, pos.y);
  725. if(hit) {
  726. press(hit.key.note, hit.v);
  727. last_key = hit.key;
  728. }
  729. });
  730. piano.rootElement.addEventListener("touchstart", function(event) {
  731. mouse_down = true;
  732. //event.stopPropagation();
  733. event.preventDefault();
  734. for(var i in event.changedTouches) {
  735. var pos = CanvasRenderer.translateMouseEvent(event.changedTouches[i]);
  736. var hit = self.getHit(pos.x, pos.y);
  737. if(hit) {
  738. press(hit.key.note, hit.v);
  739. last_key = hit.key;
  740. }
  741. }
  742. }, false);
  743. $(window).mouseup(function(event) {
  744. if(last_key) {
  745. release(last_key.note);
  746. }
  747. mouse_down = false;
  748. last_key = null;
  749. });
  750. /*$(piano.rootElement).mousemove(function(event) {
  751. if(!mouse_down) return;
  752. var pos = CanvasRenderer.translateMouseEvent(event);
  753. var hit = self.getHit(pos.x, pos.y);
  754. if(hit && hit.key != last_key) {
  755. press(hit.key.note, hit.v);
  756. last_key = hit.key;
  757. }
  758. });*/
  759.  
  760. return this;
  761. };
  762.  
  763. CanvasRenderer.prototype.resize = function(width, height) {
  764. Renderer.prototype.resize.call(this, width, height);
  765. if(this.width < 52 * 2) this.width = 52 * 2;
  766. if(this.height < this.width * 0.2) this.height = Math.floor(this.width * 0.2);
  767. this.canvas.width = this.width;
  768. this.canvas.height = this.height;
  769.  
  770. // calculate key sizes
  771. this.whiteKeyWidth = Math.floor(this.width / 52);
  772. this.whiteKeyHeight = Math.floor(this.height * 0.9);
  773. this.blackKeyWidth = Math.floor(this.whiteKeyWidth * 0.75);
  774. this.blackKeyHeight = Math.floor(this.height * 0.5);
  775.  
  776. this.blackKeyOffset = Math.floor(this.whiteKeyWidth - (this.blackKeyWidth / 2));
  777. this.keyMovement = Math.floor(this.whiteKeyHeight * 0.015);
  778.  
  779. this.whiteBlipWidth = Math.floor(this.whiteKeyWidth * 0.7);
  780. this.whiteBlipHeight = Math.floor(this.whiteBlipWidth * 0.8);
  781. this.whiteBlipX = Math.floor((this.whiteKeyWidth - this.whiteBlipWidth) / 2);
  782. this.whiteBlipY = Math.floor(this.whiteKeyHeight - this.whiteBlipHeight * 1.2);
  783. this.blackBlipWidth = Math.floor(this.blackKeyWidth * 0.7);
  784. this.blackBlipHeight = Math.floor(this.blackBlipWidth * 0.8);
  785. this.blackBlipY = Math.floor(this.blackKeyHeight - this.blackBlipHeight * 1.2);
  786. this.blackBlipX = Math.floor((this.blackKeyWidth - this.blackBlipWidth) / 2);
  787.  
  788. // prerender white key
  789. this.whiteKeyRender = document.createElement("canvas");
  790. this.whiteKeyRender.width = this.whiteKeyWidth;
  791. this.whiteKeyRender.height = this.height + 10;
  792. var ctx = this.whiteKeyRender.getContext("2d");
  793. if(ctx.createLinearGradient) {
  794. var gradient = ctx.createLinearGradient(0, 0, 0, this.whiteKeyHeight);
  795. gradient.addColorStop(0, "#eee");
  796. gradient.addColorStop(0.75, "#fff");
  797. gradient.addColorStop(1, "#dad4d4");
  798. ctx.fillStyle = gradient;
  799. } else {
  800. ctx.fillStyle = "#fff";
  801. }
  802. ctx.strokeStyle = "#000";
  803. ctx.lineJoin = "round";
  804. ctx.lineCap = "round";
  805. ctx.lineWidth = 10;
  806. ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
  807. ctx.lineWidth = 4;
  808. ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
  809.  
  810. // prerender black key
  811. this.blackKeyRender = document.createElement("canvas");
  812. this.blackKeyRender.width = this.blackKeyWidth + 10;
  813. this.blackKeyRender.height = this.blackKeyHeight + 10;
  814. var ctx = this.blackKeyRender.getContext("2d");
  815. if(ctx.createLinearGradient) {
  816. var gradient = ctx.createLinearGradient(0, 0, 0, this.blackKeyHeight);
  817. gradient.addColorStop(0, "#000");
  818. gradient.addColorStop(1, "#444");
  819. ctx.fillStyle = gradient;
  820. } else {
  821. ctx.fillStyle = "#000";
  822. }
  823. ctx.strokeStyle = "#222";
  824. ctx.lineJoin = "round";
  825. ctx.lineCap = "round";
  826. ctx.lineWidth = 8;
  827. ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
  828. ctx.lineWidth = 4;
  829. ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
  830.  
  831. // prerender shadows
  832. this.shadowRender = [];
  833. var y = -this.canvas.height * 2;
  834. for(var j = 0; j < 2; j++) {
  835. var canvas = document.createElement("canvas");
  836. this.shadowRender[j] = canvas;
  837. canvas.width = this.canvas.width;
  838. canvas.height = this.canvas.height;
  839. var ctx = canvas.getContext("2d");
  840. var sharp = j ? true : false;
  841. ctx.lineJoin = "round";
  842. ctx.lineCap = "round";
  843. ctx.lineWidth = 1;
  844. ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
  845. ctx.shadowBlur = this.keyMovement * 3;
  846. ctx.shadowOffsetY = -y + this.keyMovement;
  847. if(sharp) {
  848. ctx.shadowOffsetX = this.keyMovement;
  849. } else {
  850. ctx.shadowOffsetX = 0;
  851. ctx.shadowOffsetY = -y + this.keyMovement;
  852. }
  853. for(var i in this.piano.keys) {
  854. if(!this.piano.keys.hasOwnProperty(i)) continue;
  855. var key = this.piano.keys[i];
  856. if(key.sharp != sharp) continue;
  857.  
  858. if(key.sharp) {
  859. ctx.fillRect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
  860. y + ctx.lineWidth / 2,
  861. this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
  862. } else {
  863. ctx.fillRect(this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
  864. y + ctx.lineWidth / 2,
  865. this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
  866. }
  867. }
  868. }
  869.  
  870. // update key rects
  871. for(var i in this.piano.keys) {
  872. if(!this.piano.keys.hasOwnProperty(i)) continue;
  873. var key = this.piano.keys[i];
  874. if(key.sharp) {
  875. key.rect = new Rect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial, 0,
  876. this.blackKeyWidth, this.blackKeyHeight);
  877. } else {
  878. key.rect = new Rect(this.whiteKeyWidth * key.spatial, 0,
  879. this.whiteKeyWidth, this.whiteKeyHeight);
  880. }
  881. }
  882. };
  883.  
  884. CanvasRenderer.prototype.visualize = function(key, color) {
  885. key.timePlayed = Date.now();
  886. key.blips.push({"time": key.timePlayed, "color": color});
  887. };
  888.  
  889. CanvasRenderer.prototype.redraw = function() {
  890. var now = Date.now();
  891. var timeLoadedEnd = now - 1000;
  892. var timePlayedEnd = now - 100;
  893. var timeBlipEnd = now - 1000;
  894.  
  895. this.ctx.save();
  896. this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  897. // draw all keys
  898. for(var j = 0; j < 2; j++) {
  899. this.ctx.globalAlpha = 1.0;
  900. this.ctx.drawImage(this.shadowRender[j], 0, 0);
  901. var sharp = j ? true : false;
  902. for(var i in this.piano.keys) {
  903. if(!this.piano.keys.hasOwnProperty(i)) continue;
  904. var key = this.piano.keys[i];
  905. if(key.sharp != sharp) continue;
  906.  
  907. if(!key.loaded) {
  908. this.ctx.globalAlpha = 0.2;
  909. } else if(key.timeLoaded > timeLoadedEnd) {
  910. this.ctx.globalAlpha = ((now - key.timeLoaded) / 1000) * 0.8 + 0.2;
  911. } else {
  912. this.ctx.globalAlpha = 1.0;
  913. }
  914. var y = 0;
  915. if(key.timePlayed > timePlayedEnd) {
  916. y = Math.floor(this.keyMovement - (((now - key.timePlayed) / 100) * this.keyMovement));
  917. }
  918. var x = Math.floor(key.sharp ? this.blackKeyOffset + this.whiteKeyWidth * key.spatial
  919. : this.whiteKeyWidth * key.spatial);
  920. var image = key.sharp ? this.blackKeyRender : this.whiteKeyRender;
  921. this.ctx.drawImage(image, x, y);
  922.  
  923. // render blips
  924. if(key.blips.length) {
  925. var alpha = this.ctx.globalAlpha;
  926. var w, h;
  927. if(key.sharp) {
  928. x += this.blackBlipX;
  929. y = this.blackBlipY;
  930. w = this.blackBlipWidth;
  931. h = this.blackBlipHeight;
  932. } else {
  933. x += this.whiteBlipX;
  934. y = this.whiteBlipY;
  935. w = this.whiteBlipWidth;
  936. h = this.whiteBlipHeight;
  937. }
  938. for(var b = 0; b < key.blips.length; b++) {
  939. var blip = key.blips[b];
  940. if(blip.time > timeBlipEnd) {
  941. this.ctx.fillStyle = blip.color;
  942. this.ctx.globalAlpha = alpha - ((now - blip.time) / 1000);
  943. this.ctx.fillRect(x, y, w, h);
  944. } else {
  945. key.blips.splice(b, 1);
  946. --b;
  947. }
  948. y -= Math.floor(h * 1.1);
  949. }
  950. }
  951. }
  952. }
  953. this.ctx.restore();
  954. };
  955.  
  956. CanvasRenderer.prototype.getHit = function(x, y) {
  957. for(var j = 0; j < 2; j++) {
  958. var sharp = j ? false : true; // black keys first
  959. for(var i in this.piano.keys) {
  960. if(!this.piano.keys.hasOwnProperty(i)) continue;
  961. var key = this.piano.keys[i];
  962. if(key.sharp != sharp) continue;
  963. if(key.rect.contains(x, y)) {
  964. var v = y / (key.sharp ? this.blackKeyHeight : this.whiteKeyHeight);
  965. v += 0.25;
  966. v *= DEFAULT_VELOCITY;
  967. if(v > 1.0) v = 1.0;
  968. return {"key": key, "v": v};
  969. }
  970. }
  971. }
  972. return null;
  973. };
  974.  
  975.  
  976. CanvasRenderer.isSupported = function() {
  977. var canvas = document.createElement("canvas");
  978. return !!(canvas.getContext && canvas.getContext("2d"));
  979. };
  980.  
  981. CanvasRenderer.translateMouseEvent = function(evt) {
  982. var element = evt.target;
  983. var offx = 0;
  984. var offy = 0;
  985. do {
  986. if(!element) break; // wtf, wtf?
  987. offx += element.offsetLeft;
  988. offy += element.offsetTop;
  989. } while(element = element.offsetParent);
  990. return {
  991. x: evt.pageX - offx,
  992. y: evt.pageY - offy
  993. }
  994. };
  995.  
  996.  
  997.  
  998.  
  999.  
  1000.  
  1001.  
  1002.  
  1003.  
  1004.  
  1005.  
  1006.  
  1007. // Pianoctor
  1008.  
  1009. ////////////////////////////////////////////////////////////////
  1010.  
  1011. var PianoKey = function(note, octave) {
  1012. this.note = note + octave;
  1013. this.baseNote = note;
  1014. this.octave = octave;
  1015. this.sharp = note.indexOf("s") != -1;
  1016. this.loaded = false;
  1017. this.timeLoaded = 0;
  1018. this.domElement = null;
  1019. this.timePlayed = 0;
  1020. this.blips = [];
  1021. };
  1022.  
  1023. var Piano = function(rootElement) {
  1024.  
  1025. var piano = this;
  1026. piano.rootElement = rootElement;
  1027. piano.keys = {};
  1028.  
  1029. var white_spatial = 0;
  1030. var black_spatial = 0;
  1031. var black_it = 0;
  1032. var black_lut = [2, 1, 2, 1, 1];
  1033. var addKey = function(note, octave) {
  1034. var key = new PianoKey(note, octave);
  1035. piano.keys[key.note] = key;
  1036. if(key.sharp) {
  1037. key.spatial = black_spatial;
  1038. black_spatial += black_lut[black_it % 5];
  1039. ++black_it;
  1040. } else {
  1041. key.spatial = white_spatial;
  1042. ++white_spatial;
  1043. }
  1044. }
  1045. if(test_mode) {
  1046. addKey("c", 2);
  1047. } else {
  1048. addKey("a", -1);
  1049. addKey("as", -1);
  1050. addKey("b", -1);
  1051. var notes = "c cs d ds e f fs g gs a as b".split(" ");
  1052. for(var oct = 0; oct < 7; oct++) {
  1053. for(var i in notes) {
  1054. addKey(notes[i], oct);
  1055. }
  1056. }
  1057. addKey("c", 7);
  1058. }
  1059.  
  1060.  
  1061. var render_engine = CanvasRenderer.isSupported() ? CanvasRenderer : DOMRenderer;
  1062. this.renderer = new render_engine().init(this);
  1063.  
  1064. window.addEventListener("resize", function() {
  1065. piano.renderer.resize();
  1066. });
  1067.  
  1068.  
  1069. window.AudioContext = window.AudioContext || window.webkitAudioContext || undefined;
  1070. var audio_engine = AudioEngineWeb;
  1071.  
  1072. this.audio = new audio_engine().init(function() {
  1073. for(var i in piano.keys) {
  1074. if(!piano.keys.hasOwnProperty(i)) continue;
  1075. (function() {
  1076. var key = piano.keys[i];
  1077. piano.audio.load(key.note, gSoundPath + key.note + gSoundExt, function() {
  1078. key.loaded = true;
  1079. key.timeLoaded = Date.now();
  1080. if(key.domElement) // todo: move this to renderer somehow
  1081. $(key.domElement).removeClass("loading");
  1082. });
  1083. })();
  1084. }
  1085. });
  1086. };
  1087.  
  1088. Piano.prototype.play = function(note, vol, participant, delay_ms) {
  1089. if(!this.keys.hasOwnProperty(note)) return;
  1090. var key = this.keys[note];
  1091. if(key.loaded) this.audio.play(key.note, vol, delay_ms, participant.id);
  1092. if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, vol * 100, delay_ms);
  1093. var self = this;
  1094. var jq_namediv = $(typeof participant == "undefined" ? null : participant.nameDiv);
  1095. if(jq_namediv) {
  1096. setTimeout(function() {
  1097. self.renderer.visualize(key, typeof participant == "undefined" ? "yellow" : (participant.color || "#777"));
  1098. jq_namediv.addClass("play");
  1099. setTimeout(function() {
  1100. jq_namediv.removeClass("play");
  1101. }, 30);
  1102. }, delay_ms);
  1103. }
  1104. };
  1105.  
  1106. Piano.prototype.stop = function(note, participant, delay_ms) {
  1107. if(!this.keys.hasOwnProperty(note)) return;
  1108. var key = this.keys[note];
  1109. if(key.loaded) this.audio.stop(key.note, delay_ms, participant.id);
  1110. if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, 0, delay_ms);
  1111. };
  1112.  
  1113. var gPiano = new Piano(document.getElementById("piano"));
  1114.  
  1115.  
  1116.  
  1117.  
  1118.  
  1119.  
  1120.  
  1121. var gAutoSustain = true; //!(window.location.hash && window.location.hash.match(/^(?:#.+)*#sustain(?:#.+)*$/));
  1122. var gSustain = false;
  1123.  
  1124. var gHeldNotes = {};
  1125. var gSustainedNotes = {};
  1126.  
  1127.  
  1128. function press(id, vol) {
  1129. if(!gClient.preventsPlaying() && gNoteQuota.spend(1)) {
  1130. gHeldNotes[id] = true;
  1131. gSustainedNotes[id] = true;
  1132. gPiano.play(id, vol !== undefined ? vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), 0);
  1133. gClient.startNote(id, vol);
  1134. }
  1135. }
  1136.  
  1137. function release(id) {
  1138. if(gHeldNotes[id]) {
  1139. gHeldNotes[id] = false;
  1140. if((gAutoSustain || gSustain) && !enableSynth) {
  1141. gSustainedNotes[id] = true;
  1142. } else {
  1143. if(gNoteQuota.spend(1)) {
  1144. gPiano.stop(id, gClient.getOwnParticipant(), 0);
  1145. gClient.stopNote(id);
  1146. gSustainedNotes[id] = false;
  1147. }
  1148. }
  1149. }
  1150. }
  1151.  
  1152. function pressSustain() {
  1153. gSustain = true;
  1154. }
  1155.  
  1156. function releaseSustain() {
  1157. gSustain = false;
  1158. if(!gAutoSustain) {
  1159. for(var id in gSustainedNotes) {
  1160. if(gSustainedNotes.hasOwnProperty(id) && gSustainedNotes[id] && !gHeldNotes[id]) {
  1161. gSustainedNotes[id] = false;
  1162. if(gNoteQuota.spend(1)) {
  1163. gPiano.stop(id, gClient.getOwnParticipant(), 0);
  1164. gClient.stopNote(id);
  1165. }
  1166. }
  1167. }
  1168. }
  1169. }
  1170.  
  1171.  
  1172.  
  1173.  
  1174.  
  1175.  
  1176.  
  1177.  
  1178.  
  1179. // internet science
  1180.  
  1181. ////////////////////////////////////////////////////////////////
  1182.  
  1183. var channel_id = decodeURIComponent(window.location.pathname);
  1184. if(channel_id.substr(0, 1) == "/") channel_id = channel_id.substr(1);
  1185. if(channel_id == "") channel_id = "lobby";
  1186.  
  1187. var wssport = window.location.hostname == "www.multiplayerpiano.com" ? 443 : 8080;
  1188. var gClient = new Client("ws://" + window.location.hostname + ":" + wssport);
  1189. gClient.setChannel(channel_id);
  1190. gClient.start();
  1191.  
  1192.  
  1193. // Setting status
  1194. (function() {
  1195. gClient.on("status", function(status) {
  1196. $("#status").text(status);
  1197. });
  1198. gClient.on("count", function(count) {
  1199. if(count > 0) {
  1200. $("#status").html('<span class="number">'+count+'</span> '+(count==1? 'person is' : 'people are')+' playing');
  1201. document.title = "Piano (" + count + ")";
  1202. } else {
  1203. document.title = "Multiplayer Piano";
  1204. }
  1205. });
  1206. })();
  1207.  
  1208. // Handle changes to participants
  1209. (function() {
  1210. gClient.on("participant added", function(part) {
  1211.  
  1212. part.displayX = 150;
  1213. part.displayY = 50;
  1214.  
  1215. // add nameDiv
  1216. var div = document.createElement("div");
  1217. div.className = "name";
  1218. div.participantId = part.id;
  1219. div.textContent = part.name || "";
  1220. div.style.backgroundColor = part.color || "#777";
  1221. if(gClient.participantId === part.id) {
  1222. $(div).addClass("me");
  1223. }
  1224. if(gClient.channel && gClient.channel.crown && gClient.channel.crown.participantId === part.id) {
  1225. $(div).addClass("owner");
  1226. }
  1227. if(gPianoMutes.indexOf(part._id) !== -1) {
  1228. $(part.nameDiv).addClass("muted-notes");
  1229. }
  1230. if(gChatMutes.indexOf(part._id) !== -1) {
  1231. $(part.nameDiv).addClass("muted-chat");
  1232. }
  1233. div.style.display = "none";
  1234. part.nameDiv = $("#names")[0].appendChild(div);
  1235. $(part.nameDiv).fadeIn(2000);
  1236.  
  1237. // sort names
  1238. var arr = $("#names .name");
  1239. arr.sort(function(a, b) {
  1240. a = a.style.backgroundColor; // todo: sort based on user id instead
  1241. b = b.style.backgroundColor;
  1242. if (a > b) return 1;
  1243. else if (a < b) return -1;
  1244. else return 0;
  1245. });
  1246. $("#names").html(arr);
  1247.  
  1248. // add cursorDiv
  1249. if(gClient.participantId !== part.id || gSeeOwnCursor) {
  1250. var div = document.createElement("div");
  1251. div.className = "cursor";
  1252. div.style.display = "none";
  1253. part.cursorDiv = $("#cursors")[0].appendChild(div);
  1254. $(part.cursorDiv).fadeIn(2000);
  1255.  
  1256. var div = document.createElement("div");
  1257. div.className = "name";
  1258. div.style.backgroundColor = part.color || "#777"
  1259. div.textContent = part.name || "";
  1260. part.cursorDiv.appendChild(div);
  1261.  
  1262. } else {
  1263. part.cursorDiv = undefined;
  1264. }
  1265. });
  1266. gClient.on("participant removed", function(part) {
  1267. // remove nameDiv
  1268. var nd = $(part.nameDiv);
  1269. var cd = $(part.cursorDiv);
  1270. cd.fadeOut(2000);
  1271. nd.fadeOut(2000, function() {
  1272. nd.remove();
  1273. cd.remove();
  1274. part.nameDiv = undefined;
  1275. part.cursorDiv = undefined;
  1276. });
  1277. });
  1278. gClient.on("participant update", function(part) {
  1279. var name = part.name || "";
  1280. var color = part.color || "#777";
  1281. part.nameDiv.style.backgroundColor = color;
  1282. part.nameDiv.textContent = name;
  1283. $(part.cursorDiv)
  1284. .find(".name")
  1285. .text(name)
  1286. .css("background-color", color);
  1287. });
  1288. gClient.on("ch", function(msg) {
  1289. for(var id in gClient.ppl) {
  1290. if(gClient.ppl.hasOwnProperty(id)) {
  1291. var part = gClient.ppl[id];
  1292. if(part.id === gClient.participantId) {
  1293. $(part.nameDiv).addClass("me");
  1294. } else {
  1295. $(part.nameDiv).removeClass("me");
  1296. }
  1297. if(msg.ch.crown && msg.ch.crown.participantId === part.id) {
  1298. $(part.nameDiv).addClass("owner");
  1299. $(part.cursorDiv).addClass("owner");
  1300. } else {
  1301. $(part.nameDiv).removeClass("owner");
  1302. $(part.cursorDiv).removeClass("owner");
  1303. }
  1304. if(gPianoMutes.indexOf(part._id) !== -1) {
  1305. $(part.nameDiv).addClass("muted-notes");
  1306. } else {
  1307. $(part.nameDiv).removeClass("muted-notes");
  1308. }
  1309. if(gChatMutes.indexOf(part._id) !== -1) {
  1310. $(part.nameDiv).addClass("muted-chat");
  1311. } else {
  1312. $(part.nameDiv).removeClass("muted-chat");
  1313. }
  1314. }
  1315. }
  1316. });
  1317. })();
  1318.  
  1319.  
  1320. // Handle changes to crown
  1321. (function() {
  1322. var jqcrown = $('<div id="crown"></div>').appendTo(document.body).hide();
  1323. var jqcountdown = $('<span></span>').appendTo(jqcrown);
  1324. var countdown_interval;
  1325. jqcrown.click(function() {
  1326. gClient.sendArray([{m: "chown", id: gClient.participantId}]);
  1327. });
  1328. gClient.on("ch", function(msg) {
  1329. if(msg.ch.crown) {
  1330. var crown = msg.ch.crown;
  1331. if(!crown.participantId || !gClient.ppl[crown.participantId]) {
  1332. var land_time = crown.time + 2000 - gClient.serverTimeOffset;
  1333. var avail_time = crown.time + 15000 - gClient.serverTimeOffset;
  1334. jqcountdown.text("");
  1335. jqcrown.show();
  1336. if(land_time - Date.now() <= 0) {
  1337. jqcrown.css({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"});
  1338. } else {
  1339. jqcrown.css({"left": crown.startPos.x + "%", "top": crown.startPos.y + "%"});
  1340. jqcrown.addClass("spin");
  1341. jqcrown.animate({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"}, 2000, "linear", function() {
  1342. jqcrown.removeClass("spin");
  1343. });
  1344. }
  1345. clearInterval(countdown_interval);
  1346. countdown_interval = setInterval(function() {
  1347. var time = Date.now();
  1348. if(time >= land_time) {
  1349. var ms = avail_time - time;
  1350. if(ms > 0) {
  1351. jqcountdown.text(Math.ceil(ms / 1000) + "s");
  1352. } else {
  1353. jqcountdown.text("");
  1354. clearInterval(countdown_interval);
  1355. }
  1356. }
  1357. }, 1000);
  1358. } else {
  1359. jqcrown.hide();
  1360. }
  1361. } else {
  1362. jqcrown.hide();
  1363. }
  1364. });
  1365. gClient.on("disconnect", function() {
  1366. jqcrown.fadeOut(2000);
  1367. });
  1368. })();
  1369.  
  1370.  
  1371. // Playing notes
  1372. gClient.on("n", function(msg) {
  1373. var t = msg.t - gClient.serverTimeOffset + TIMING_TARGET - Date.now();
  1374. var participant = gClient.findParticipantById(msg.p);
  1375. if(gPianoMutes.indexOf(participant._id) !== -1)
  1376. return;
  1377. for(var i = 0; i < msg.n.length; i++) {
  1378. var note = msg.n[i];
  1379. var ms = t + (note.d || 0);
  1380. if(ms < 0) {
  1381. ms = 0;
  1382. }
  1383. else if(ms > 10000) continue;
  1384. if(note.s) {
  1385. gPiano.stop(note.n, participant, ms);
  1386. } else {
  1387. var vel = (typeof note.v !== "undefined")? parseFloat(note.v) : DEFAULT_VELOCITY;
  1388. if(vel < 0) vel = 0; else if (vel > 1) vel = 1;
  1389. gPiano.play(note.n, vel, participant, ms);
  1390. if(enableSynth) {
  1391. gPiano.stop(note.n, participant, ms + 1000);
  1392. }
  1393. }
  1394. }
  1395. });
  1396.  
  1397. // Send cursor updates
  1398. var mx = 0, last_mx = -10, my = 0, last_my = -10;
  1399. setInterval(function() {
  1400. if(Math.abs(mx - last_mx) > 0.1 || Math.abs(my - last_my) > 0.1) {
  1401. last_mx = mx;
  1402. last_my = my;
  1403. gClient.sendArray([{m: "m", x: mx, y: my}]);
  1404. var part = gClient.getOwnParticipant();
  1405. if(part) {
  1406. part.x = mx;
  1407. part.y = my;
  1408. }
  1409. }
  1410. }, 50);
  1411. $(document).mousemove(function(event) {
  1412. mx = ((event.pageX / $(window).width()) * 100).toFixed(2);
  1413. my = ((event.pageY / $(window).height()) * 100).toFixed(2);
  1414. });
  1415.  
  1416. // Animate cursors
  1417. setInterval(function() {
  1418. for(var id in gClient.ppl) {
  1419. if(!gClient.ppl.hasOwnProperty(id)) continue;
  1420. var part = gClient.ppl[id];
  1421. if(part.cursorDiv && (Math.abs(part.x - part.displayX) > 0.1 || Math.abs(part.y - part.displayY) > 0.1)) {
  1422. part.displayX += (part.x - part.displayX) * 0.75;
  1423. part.displayY += (part.y - part.displayY) * 0.75;
  1424. part.cursorDiv.style.left = part.displayX + "%";
  1425. part.cursorDiv.style.top = part.displayY + "%";
  1426. }
  1427. }
  1428. }, 50);
  1429.  
  1430.  
  1431. // Room settings button
  1432. (function() {
  1433. gClient.on("ch", function(msg) {
  1434. if(gClient.isOwner()) {
  1435. $("#room-settings-btn").show();
  1436. } else {
  1437. $("#room-settings-btn").hide();
  1438. }
  1439. });
  1440. $("#room-settings-btn").click(function(evt) {
  1441. if(gClient.channel && gClient.isOwner()) {
  1442. var settings = gClient.channel.settings;
  1443. openModal("#room-settings");
  1444. setTimeout(function() {
  1445. $("#room-settings .checkbox[name=visible]").prop("checked", settings.visible);
  1446. $("#room-settings .checkbox[name=chat]").prop("checked", settings.chat);
  1447. $("#room-settings .checkbox[name=crownsolo]").prop("checked", settings.crownsolo);
  1448. $("#room-settings input[name=color]").val(settings.color);
  1449. }, 100);
  1450. }
  1451. });
  1452. $("#room-settings .submit").click(function() {
  1453. var settings = {
  1454. visible: $("#room-settings .checkbox[name=visible]").is(":checked"),
  1455. chat: $("#room-settings .checkbox[name=chat]").is(":checked"),
  1456. crownsolo: $("#room-settings .checkbox[name=crownsolo]").is(":checked"),
  1457. color: $("#room-settings input[name=color]").val()
  1458. };
  1459. gClient.sendArray([{m: "chset", set: settings}]);
  1460. closeModal();
  1461. });
  1462. $("#room-settings .drop-crown").click(function() {
  1463. gClient.sendArray([{m: "chown"}]);
  1464. closeModal();
  1465. });
  1466. })();
  1467.  
  1468. // Handle notifications
  1469. gClient.on("notification", function(msg) {
  1470. new Notification(msg);
  1471. });
  1472.  
  1473. // Don't foget spin
  1474. gClient.on("ch", function(msg) {
  1475. var chidlo = msg.ch._id.toLowerCase();
  1476. if(chidlo === "spin" || chidlo.substr(-5) === "/spin") {
  1477. $("#piano").addClass("spin");
  1478. } else {
  1479. $("#piano").removeClass("spin");
  1480. }
  1481. });
  1482.  
  1483. /*function eb() {
  1484. if(gClient.channel && gClient.channel._id.toLowerCase() === "test/fishing") {
  1485. ebsprite.start(gClient);
  1486. } else {
  1487. ebsprite.stop();
  1488. }
  1489. }
  1490. if(ebsprite) {
  1491. gClient.on("ch", eb);
  1492. eb();
  1493. }*/
  1494.  
  1495. // Crownsolo notice
  1496. gClient.on("ch", function(msg) {
  1497. if(msg.ch.settings.crownsolo) {
  1498. if($("#crownsolo-notice").length == 0) {
  1499. $('<div id="crownsolo-notice">').text('This room is set to "only the owner can play."').appendTo("body").fadeIn(1000);
  1500. }
  1501. } else {
  1502. $("#crownsolo-notice").remove();
  1503. }
  1504. });
  1505. gClient.on("disconnect", function() {
  1506. $("#crownsolo-notice").remove();
  1507. });
  1508.  
  1509.  
  1510. // Background color
  1511. (function() {
  1512. var old_color1 = new Color("#ecfafd");
  1513. var old_color2 = new Color("#ecfafd");
  1514. function setColor(hex) {
  1515. var color1 = new Color(hex);
  1516. var color2 = new Color(hex);
  1517. color2.add(-0x40, -0x40, -0x40);
  1518.  
  1519. var bottom = document.getElementById("bottom");
  1520.  
  1521. var duration = 500;
  1522. var step = 0;
  1523. var steps = 30;
  1524. var step_ms = duration / steps;
  1525. var difference = new Color(color1.r, color1.g, color1.b);
  1526. difference.r -= old_color1.r;
  1527. difference.g -= old_color1.g;
  1528. difference.b -= old_color1.b;
  1529. var inc = new Color(difference.r / steps, difference.g / steps, difference.b / steps);
  1530. var iv;
  1531. iv = setInterval(function() {
  1532. old_color1.add(inc.r, inc.g, inc.b);
  1533. old_color2.add(inc.r, inc.g, inc.b);
  1534. document.body.style.background = "radial-gradient(ellipse at center, "+old_color1.toHexa()+" 0%,"+old_color2.toHexa()+" 100%)";
  1535. bottom.style.background = old_color2.toHexa();
  1536. if(++step >= steps) {
  1537. clearInterval(iv);
  1538. old_color1 = color1;
  1539. old_color2 = color2;
  1540. document.body.style.background = "radial-gradient(ellipse at center, "+color1.toHexa()+" 0%,"+color2.toHexa()+" 100%)";
  1541. bottom.style.background = color2.toHexa();
  1542. }
  1543. }, step_ms);
  1544. }
  1545.  
  1546. setColor("#ecfafd");
  1547.  
  1548. gClient.on("ch", function(ch) {
  1549. if(ch.ch.settings) {
  1550. if(ch.ch.settings.color) {
  1551. setColor(ch.ch.settings.color);
  1552. } else {
  1553. setColor("#ecfafd");
  1554. }
  1555. }
  1556. });
  1557. })();
  1558.  
  1559.  
  1560.  
  1561.  
  1562.  
  1563.  
  1564. var gPianoMutes = [];
  1565.  
  1566. var gChatMutes = [];
  1567.  
  1568.  
  1569.  
  1570.  
  1571.  
  1572.  
  1573.  
  1574.  
  1575.  
  1576.  
  1577.  
  1578.  
  1579.  
  1580.  
  1581.  
  1582.  
  1583.  
  1584.  
  1585.  
  1586. var volume_slider = new VolumeSlider(document.getElementById("volume"), function(v) {
  1587. gPiano.audio.setVolume(v);
  1588. if(window.localStorage) localStorage.volume = v;
  1589. });
  1590. volume_slider.set(gPiano.audio.volume);
  1591.  
  1592. var Note = function(note, octave) {
  1593. this.note = note;
  1594. this.octave = octave || 0;
  1595. };
  1596.  
  1597.  
  1598.  
  1599. var n = function(a, b) { return {note: new Note(a, b), held: false}; };
  1600. var key_binding = {
  1601. 65: n("gs"),
  1602. 90: n("a"),
  1603. 83: n("as"),
  1604. 88: n("b"),
  1605. 67: n("c", 1),
  1606. 70: n("cs", 1),
  1607. 86: n("d", 1),
  1608. 71: n("ds", 1),
  1609. 66: n("e", 1),
  1610. 78: n("f", 1),
  1611. 74: n("fs", 1),
  1612. 77: n("g", 1),
  1613. 75: n("gs", 1),
  1614. 188: n("a", 1),
  1615. 76: n("as", 1),
  1616. 190: n("b", 1),
  1617. 191: n("c", 2),
  1618. 222: n("cs", 2),
  1619.  
  1620. 49: n("gs", 1),
  1621. 81: n("a", 1),
  1622. 50: n("as", 1),
  1623. 87: n("b", 1),
  1624. 69: n("c", 2),
  1625. 52: n("cs", 2),
  1626. 82: n("d", 2),
  1627. 53: n("ds", 2),
  1628. 84: n("e", 2),
  1629. 89: n("f", 2),
  1630. 55: n("fs", 2),
  1631. 85: n("g", 2),
  1632. 56: n("gs", 2),
  1633. 73: n("a", 2),
  1634. 57: n("as", 2),
  1635. 79: n("b", 2),
  1636. 80: n("c", 3),
  1637. 189: n("cs", 3),
  1638. 219: n("d", 3),
  1639. 187: n("ds", 3),
  1640. 221: n("e", 3)
  1641. };
  1642.  
  1643. var capsLockKey = false;
  1644.  
  1645. var transpose_octave = 0;
  1646.  
  1647. function handleKeyDown(evt) {
  1648. //console.log(evt);
  1649. var code = parseInt(evt.keyCode);
  1650. if(key_binding[code] !== undefined) {
  1651. var binding = key_binding[code];
  1652. if(!binding.held) {
  1653. binding.held = true;
  1654.  
  1655. var note = binding.note;
  1656. var octave = 1 + note.octave + transpose_octave;
  1657. if(evt.shiftKey) ++octave;
  1658. else if(capsLockKey || evt.ctrlKey) --octave;
  1659. note = note.note + octave;
  1660. var vol = velocityFromMouseY();
  1661. press(note, vol);
  1662. }
  1663.  
  1664. if(++gKeyboardSeq == 3) {
  1665. gKnowsYouCanUseKeyboard = true;
  1666. if(window.gKnowsYouCanUseKeyboardTimeout) clearTimeout(gKnowsYouCanUseKeyboardTimeout);
  1667. if(localStorage) localStorage.knowsYouCanUseKeyboard = true;
  1668. if(window.gKnowsYouCanUseKeyboardNotification) gKnowsYouCanUseKeyboardNotification.close();
  1669. }
  1670.  
  1671. evt.preventDefault();
  1672. evt.stopPropagation();
  1673. return false;
  1674. } else if(code == 20) { // Caps Lock
  1675. capsLockKey = true;
  1676. evt.preventDefault();
  1677. } else if(code === 0x20) { // Space Bar
  1678. pressSustain();
  1679. evt.preventDefault();
  1680. } else if((code === 38 || code === 39) && transpose_octave < 3) {
  1681. ++transpose_octave;
  1682. } else if((code === 40 || code === 37) && transpose_octave > -2) {
  1683. --transpose_octave;
  1684. } else if(code == 9) { // Tab (don't tab away from the piano)
  1685. evt.preventDefault();
  1686. } else if(code == 8) { // Backspace (don't navigate Back)
  1687. gAutoSustain = !gAutoSustain;
  1688. evt.preventDefault();
  1689. }
  1690. };
  1691.  
  1692. function handleKeyUp(evt) {
  1693. var code = parseInt(evt.keyCode);
  1694. if(key_binding[code] !== undefined) {
  1695. var binding = key_binding[code];
  1696. if(binding.held) {
  1697. binding.held = false;
  1698.  
  1699. var note = binding.note;
  1700. var octave = 1 + note.octave + transpose_octave;
  1701. if(evt.shiftKey) ++octave;
  1702. else if(capsLockKey || evt.ctrlKey) --octave;
  1703. note = note.note + octave;
  1704. release(note);
  1705. }
  1706.  
  1707. evt.preventDefault();
  1708. evt.stopPropagation();
  1709. return false;
  1710. } else if(code == 20) { // Caps Lock
  1711. capsLockKey = false;
  1712. evt.preventDefault();
  1713. } else if(code === 0x20) { // Space Bar
  1714. releaseSustain();
  1715. evt.preventDefault();
  1716. }
  1717. };
  1718.  
  1719. function handleKeyPress(evt) {
  1720. evt.preventDefault();
  1721. evt.stopPropagation();
  1722. if(evt.keyCode == 27 || evt.keyCode == 13) {
  1723. //$("#chat input").focus();
  1724. }
  1725. return false;
  1726. };
  1727.  
  1728. var recapListener = function(evt) {
  1729. captureKeyboard();
  1730. };
  1731.  
  1732. function captureKeyboard() {
  1733. $("#piano").off("mousedown", recapListener);
  1734. $("#piano").off("touchstart", recapListener);
  1735. $(document).on("keydown", handleKeyDown );
  1736. $(document).on("keyup", handleKeyUp);
  1737. $(window).on("keypress", handleKeyPress );
  1738. };
  1739.  
  1740. function releaseKeyboard() {
  1741. $(document).off("keydown", handleKeyDown );
  1742. $(document).off("keyup", handleKeyUp);
  1743. $(window).off("keypress", handleKeyPress );
  1744. $("#piano").on("mousedown", recapListener);
  1745. $("#piano").on("touchstart", recapListener);
  1746. };
  1747.  
  1748. captureKeyboard();
  1749.  
  1750.  
  1751. var velocityFromMouseY = function() {
  1752. return 0.1 + (my / 100) * 0.6;
  1753. };
  1754.  
  1755.  
  1756.  
  1757.  
  1758.  
  1759. // NoteQuota
  1760. var gNoteQuota = (function() {
  1761. var last_rat = 0;
  1762. var nqjq = $("#quota .value");
  1763. setInterval(function() {
  1764. gNoteQuota.tick();
  1765. }, 2000);
  1766. return new NoteQuota(function(points) {
  1767. // update UI
  1768. var rat = (points / this.max) * 100;
  1769. if(rat <= last_rat)
  1770. nqjq.stop(true, true).css("width", rat.toFixed(0) + "%");
  1771. else
  1772. nqjq.stop(true, true).animate({"width": rat.toFixed(0) + "%"}, 2000, "linear");
  1773. last_rat = rat;
  1774. });
  1775. })();
  1776. gClient.on("nq", function(nq_params) {
  1777. gNoteQuota.setParams(nq_params);
  1778. });
  1779. gClient.on("disconnect", function() {
  1780. gNoteQuota.setParams(NoteQuota.PARAMS_OFFLINE);
  1781. });
  1782.  
  1783.  
  1784.  
  1785. // click participant names
  1786. (function() {
  1787. var ele = document.getElementById("names");
  1788. var touchhandler = function(e) {
  1789. var target_jq = $(e.target);
  1790. if(target_jq.hasClass("name")) {
  1791. target_jq.addClass("play");
  1792. if(e.target.participantId == gClient.participantId) {
  1793. openModal("#rename", "input[name=name]");
  1794. setTimeout(function() {
  1795. $("#rename input[name=name]").val(gClient.ppl[gClient.participantId].name);
  1796. $("#rename input[name=color]").val(gClient.ppl[gClient.participantId].color);
  1797. }, 100);
  1798. } else if(e.target.participantId) {
  1799. var id = e.target.participantId;
  1800. var part = gClient.ppl[id] || null;
  1801. if(part) {
  1802. participantMenu(part);
  1803. e.stopPropagation();
  1804. }
  1805. }
  1806. }
  1807. };
  1808. ele.addEventListener("mousedown", touchhandler);
  1809. ele.addEventListener("touchstart", touchhandler);
  1810. var releasehandler = function(e) {
  1811. $("#names .name").removeClass("play");
  1812. };
  1813. document.body.addEventListener("mouseup", releasehandler);
  1814. document.body.addEventListener("touchend", releasehandler);
  1815.  
  1816. var removeParticipantMenus = function() {
  1817. $(".participant-menu").remove();
  1818. $(".participantSpotlight").hide();
  1819. document.removeEventListener("mousedown", removeParticipantMenus);
  1820. document.removeEventListener("touchstart", removeParticipantMenus);
  1821. };
  1822.  
  1823. var participantMenu = function(part) {
  1824. if(!part) return;
  1825. removeParticipantMenus();
  1826. document.addEventListener("mousedown", removeParticipantMenus);
  1827. document.addEventListener("touchstart", removeParticipantMenus);
  1828. $("#" + part.id).find(".enemySpotlight").show();
  1829. var menu = $('<div class="participant-menu"></div>');
  1830. $("body").append(menu);
  1831. // move menu to name position
  1832. var jq_nd = $(part.nameDiv);
  1833. var pos = jq_nd.position();
  1834. menu.css({
  1835. "top": pos.top + jq_nd.height() + 15,
  1836. "left": pos.left + 6,
  1837. "background": part.color || "black"
  1838. });
  1839. menu.on("mousedown touchstart", function(evt) {
  1840. evt.stopPropagation();
  1841. var target = $(evt.target);
  1842. if(target.hasClass("menu-item")) {
  1843. target.addClass("clicked");
  1844. menu.fadeOut(200, function() {
  1845. removeParticipantMenus();
  1846. });
  1847. }
  1848. });
  1849. // this spaces stuff out but also can be used for informational
  1850. $('<div class="info"></div>').appendTo(menu).text(part._id);
  1851. // add menu items
  1852. if(gPianoMutes.indexOf(part._id) == -1) {
  1853. $('<div class="menu-item">Mute Notes</div>').appendTo(menu)
  1854. .on("mousedown touchstart", function(evt) {
  1855. gPianoMutes.push(part._id);
  1856. $(part.nameDiv).addClass("muted-notes");
  1857. });
  1858. } else {
  1859. $('<div class="menu-item">Unmute Notes</div>').appendTo(menu)
  1860. .on("mousedown touchstart", function(evt) {
  1861. var i;
  1862. while((i = gPianoMutes.indexOf(part._id)) != -1)
  1863. gPianoMutes.splice(i, 1);
  1864. $(part.nameDiv).removeClass("muted-notes");
  1865. });
  1866. }
  1867. if(gChatMutes.indexOf(part._id) == -1) {
  1868. $('<div class="menu-item">Mute Chat</div>').appendTo(menu)
  1869. .on("mousedown touchstart", function(evt) {
  1870. gChatMutes.push(part._id);
  1871. $(part.nameDiv).addClass("muted-chat");
  1872. });
  1873. } else {
  1874. $('<div class="menu-item">Unmute Chat</div>').appendTo(menu)
  1875. .on("mousedown touchstart", function(evt) {
  1876. var i;
  1877. while((i = gChatMutes.indexOf(part._id)) != -1)
  1878. gChatMutes.splice(i, 1);
  1879. $(part.nameDiv).removeClass("muted-chat");
  1880. });
  1881. }
  1882. if(!(gPianoMutes.indexOf(part._id) >= 0) || !(gChatMutes.indexOf(part._id) >= 0)) {
  1883. $('<div class="menu-item">Mute Completely</div>').appendTo(menu)
  1884. .on("mousedown touchstart", function(evt) {
  1885. gPianoMutes.push(part._id);
  1886. gChatMutes.push(part._id);
  1887. $(part.nameDiv).addClass("muted-notes");
  1888. $(part.nameDiv).addClass("muted-chat");
  1889. });
  1890. }
  1891. if((gPianoMutes.indexOf(part._id) >= 0) || (gChatMutes.indexOf(part._id) >= 0)) {
  1892. $('<div class="menu-item">Unmute Completely</div>').appendTo(menu)
  1893. .on("mousedown touchstart", function(evt) {
  1894. var i;
  1895. while((i = gPianoMutes.indexOf(part._id)) != -1)
  1896. gPianoMutes.splice(i, 1);
  1897. while((i = gChatMutes.indexOf(part._id)) != -1)
  1898. gChatMutes.splice(i, 1);
  1899. $(part.nameDiv).removeClass("muted-notes");
  1900. $(part.nameDiv).removeClass("muted-chat");
  1901. });
  1902. }
  1903. if(gClient.isOwner()) {
  1904. $('<div class="menu-item give-crown">Give Crown</div>').appendTo(menu)
  1905. .on("mousedown touchstart", function(evt) {
  1906. gClient.sendArray([{m: "chown", id: part.id}]);
  1907. });
  1908. $('<div class="menu-item kickban">Kickban</div>').appendTo(menu)
  1909. .on("mousedown touchstart", function(evt) {
  1910. var minutes = prompt("How many minutes? (0-60)", "30");
  1911. if(minutes === null) return;
  1912. minutes = parseFloat(minutes) || 0;
  1913. var ms = minutes * 60 * 1000;
  1914. gClient.sendArray([{m: "kickban", _id: part._id, ms: ms}]);
  1915. });
  1916. }
  1917. menu.fadeIn(100);
  1918. };
  1919. })();
  1920.  
  1921.  
  1922.  
  1923.  
  1924.  
  1925.  
  1926.  
  1927.  
  1928.  
  1929.  
  1930.  
  1931.  
  1932.  
  1933.  
  1934.  
  1935.  
  1936. // Notification class
  1937.  
  1938. ////////////////////////////////////////////////////////////////
  1939.  
  1940. var Notification = function(par) {
  1941. EventEmitter.call(this);
  1942.  
  1943. var par = par || {};
  1944.  
  1945. this.id = "Notification-" + (par.id || Math.random());
  1946. this.title = par.title || "";
  1947. this.text = par.text || "";
  1948. this.html = par.html || "";
  1949. this.target = $(par.target || "#piano");
  1950. this.duration = par.duration || 30000;
  1951. this["class"] = par["class"] || "classic";
  1952.  
  1953. var self = this;
  1954. var eles = $("#" + this.id);
  1955. if(eles.length > 0) {
  1956. eles.remove();
  1957. }
  1958. this.domElement = $('<div class="notification"><div class="notification-body"><div class="title"></div>' +
  1959. '<div class="text"></div></div><div class="x">x</div></div>');
  1960. this.domElement[0].id = this.id;
  1961. this.domElement.addClass(this["class"]);
  1962. this.domElement.find(".title").text(this.title);
  1963. if(this.text.length > 0) {
  1964. this.domElement.find(".text").text(this.text);
  1965. } else if(this.html instanceof HTMLElement) {
  1966. this.domElement.find(".text")[0].appendChild(this.html);
  1967. } else if(this.html.length > 0) {
  1968. this.domElement.find(".text").html(this.html);
  1969. }
  1970. document.body.appendChild(this.domElement.get(0));
  1971.  
  1972. this.position();
  1973. this.onresize = function() {
  1974. self.position();
  1975. };
  1976. window.addEventListener("resize", this.onresize);
  1977.  
  1978. this.domElement.find(".x").click(function() {
  1979. self.close();
  1980. });
  1981.  
  1982. if(this.duration > 0) {
  1983. setTimeout(function() {
  1984. self.close();
  1985. }, this.duration);
  1986. }
  1987.  
  1988. return this;
  1989. }
  1990.  
  1991. mixin(Notification.prototype, EventEmitter.prototype);
  1992. Notification.prototype.constructor = Notification;
  1993.  
  1994. Notification.prototype.position = function() {
  1995. var pos = this.target.offset();
  1996. var x = pos.left - (this.domElement.width() / 2) + (this.target.width() / 4);
  1997. var y = pos.top - this.domElement.height() - 8;
  1998. var width = this.domElement.width();
  1999. if(x + width > $("body").width()) {
  2000. x -= ((x + width) - $("body").width());
  2001. }
  2002. if(x < 0) x = 0;
  2003. this.domElement.offset({left: x, top: y});
  2004. };
  2005.  
  2006. Notification.prototype.close = function() {
  2007. var self = this;
  2008. window.removeEventListener("resize", this.onresize);
  2009. this.domElement.fadeOut(500, function() {
  2010. self.domElement.remove();
  2011. self.emit("close");
  2012. });
  2013. };
  2014.  
  2015.  
  2016.  
  2017.  
  2018.  
  2019.  
  2020.  
  2021.  
  2022.  
  2023.  
  2024.  
  2025.  
  2026.  
  2027.  
  2028.  
  2029. // set variables from settings or set settings
  2030.  
  2031. ////////////////////////////////////////////////////////////////
  2032.  
  2033. var gKeyboardSeq = 0;
  2034. var gKnowsYouCanUseKeyboard = false;
  2035. if(localStorage && localStorage.knowsYouCanUseKeyboard) gKnowsYouCanUseKeyboard = true;
  2036. if(!gKnowsYouCanUseKeyboard) {
  2037. window.gKnowsYouCanUseKeyboardTimeout = setTimeout(function() {
  2038. window.gKnowsYouCanUseKeyboardNotification = new Notification({title: "Did you know!?!",
  2039. text: "You can play the piano with your keyboard, too. Try it!", target: "#piano", duration: 10000});
  2040. }, 30000);
  2041. }
  2042.  
  2043.  
  2044.  
  2045.  
  2046. if(window.localStorage) {
  2047.  
  2048. if(localStorage.volume) {
  2049. volume_slider.set(localStorage.volume);
  2050. gPiano.audio.setVolume(localStorage.volume);
  2051. }
  2052. else localStorage.volume = gPiano.audio.volume;
  2053.  
  2054. window.gHasBeenHereBefore = (localStorage.gHasBeenHereBefore || false);
  2055. if(gHasBeenHereBefore) {
  2056. }
  2057. localStorage.gHasBeenHereBefore = true;
  2058.  
  2059. }
  2060.  
  2061.  
  2062.  
  2063.  
  2064.  
  2065.  
  2066.  
  2067.  
  2068.  
  2069.  
  2070.  
  2071.  
  2072.  
  2073. // New room, change room
  2074.  
  2075. ////////////////////////////////////////////////////////////////
  2076.  
  2077. $("#room > .info").text("--");
  2078. gClient.on("ch", function(msg) {
  2079. var channel = msg.ch;
  2080. var info = $("#room > .info");
  2081. info.text(channel._id);
  2082. if(channel.settings.lobby) info.addClass("lobby");
  2083. else info.removeClass("lobby");
  2084. if(!channel.settings.chat) info.addClass("no-chat");
  2085. else info.removeClass("no-chat");
  2086. if(channel.settings.crownsolo) info.addClass("crownsolo");
  2087. else info.removeClass("crownsolo");
  2088. if(!channel.settings.visible) info.addClass("not-visible");
  2089. else info.removeClass("not-visible");
  2090. });
  2091. gClient.on("ls", function(ls) {
  2092. for(var i in ls.u) {
  2093. if(!ls.u.hasOwnProperty(i)) continue;
  2094. var room = ls.u[i];
  2095. var info = $("#room .info[roomname=\"" + (room._id + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0') + "\"]");
  2096. if(info.length == 0) {
  2097. info = $("<div class=\"info\"></div>");
  2098. info.attr("roomname", room._id);
  2099. $("#room .more").append(info);
  2100. }
  2101. info.text(room._id + " (" + room.count + ")");
  2102. if(room.settings.lobby) info.addClass("lobby");
  2103. else info.removeClass("lobby");
  2104. if(!room.settings.chat) info.addClass("no-chat");
  2105. else info.removeClass("no-chat");
  2106. if(room.settings.crownsolo) info.addClass("crownsolo");
  2107. else info.removeClass("crownsolo");
  2108. if(!room.settings.visible) info.addClass("not-visible");
  2109. else info.removeClass("not-visible");
  2110. }
  2111. });
  2112. $("#room").on("click", function(evt) {
  2113. evt.stopPropagation();
  2114.  
  2115. // clicks on a new room
  2116. if($(evt.target).hasClass("info") && $(evt.target).parents(".more").length) {
  2117. $("#room .more").fadeOut(250);
  2118. var selected_name = $(evt.target).attr("roomname");
  2119. if(typeof selected_name != "undefined") {
  2120. changeRoom(selected_name, "right");
  2121. }
  2122. return false;
  2123. }
  2124. // clicks on "New Room..."
  2125. else if($(evt.target).hasClass("new")) {
  2126. openModal("#new-room", "input[name=name]");
  2127. }
  2128. // all other clicks
  2129. var doc_click = function(evt) {
  2130. if($(evt.target).is("#room .more")) return;
  2131. $(document).off("mousedown", doc_click);
  2132. $("#room .more").fadeOut(250);
  2133. gClient.sendArray([{m: "-ls"}]);
  2134. }
  2135. $(document).on("mousedown", doc_click);
  2136. $("#room .more .info").remove();
  2137. $("#room .more").show();
  2138. gClient.sendArray([{m: "+ls"}]);
  2139. });
  2140. $("#new-room-btn").on("click", function(evt) {
  2141. evt.stopPropagation();
  2142. openModal("#new-room", "input[name=name]");
  2143. });
  2144.  
  2145.  
  2146. $("#play-alone-btn").on("click", function(evt) {
  2147. evt.stopPropagation();
  2148. var room_name = "Room" + Math.floor(Math.random() * 1000000000000);
  2149. changeRoom(room_name, "right", {"visible": false, "chat": true, "crownsolo": false});
  2150. setTimeout(function() {
  2151. new Notification({id: "share", title: "Playing alone", html: 'You are playing alone in a room by yourself, but you can always invite \
  2152. friends by sending them the link.<br/><br/>\
  2153. <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/>\
  2154. <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Tweet</a>', duration: 25000});
  2155. }, 1000);
  2156. });
  2157.  
  2158.  
  2159.  
  2160. var gModal;
  2161.  
  2162. function modalHandleEsc(evt) {
  2163. if(evt.keyCode == 27) {
  2164. closeModal();
  2165. evt.preventDefault();
  2166. evt.stopPropagation();
  2167. }
  2168. };
  2169.  
  2170. function openModal(selector, focus) {
  2171. chat.blur();
  2172. releaseKeyboard();
  2173. $(document).on("keydown", modalHandleEsc);
  2174. $("#modal #modals > *").hide();
  2175. $("#modal").fadeIn(250);
  2176. $(selector).show();
  2177. setTimeout(function() {
  2178. $(selector).find(focus).focus();
  2179. }, 100);
  2180. gModal = selector;
  2181. };
  2182.  
  2183. function closeModal() {
  2184. $(document).off("keydown", modalHandleEsc);
  2185. $("#modal").fadeOut(100);
  2186. $("#modal #modals > *").hide();
  2187. captureKeyboard();
  2188. gModal = null;
  2189. };
  2190.  
  2191. var modal_bg = $("#modal .bg")[0];
  2192. $(modal_bg).on("click", function(evt) {
  2193. if(evt.target != modal_bg) return;
  2194. closeModal();
  2195. });
  2196.  
  2197. (function() {
  2198. function submit() {
  2199. var name = $("#new-room .text[name=name]").val();
  2200. var settings = {
  2201. visible: $("#new-room .checkbox[name=visible]").is(":checked"),
  2202. chat: true,
  2203. crownsolo: false
  2204. };
  2205. $("#new-room .text[name=name]").val("");
  2206. closeModal();
  2207. changeRoom(name, "right", settings);
  2208. setTimeout(function() {
  2209. new Notification({id: "share", title: "Created a Room", html: 'You can invite friends to your room by sending them the link.<br/><br/>\
  2210. <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/>\
  2211. <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Tweet</a>', duration: 25000});
  2212. }, 1000);
  2213. };
  2214. $("#new-room .submit").click(function(evt) {
  2215. submit();
  2216. });
  2217. $("#new-room .text[name=name]").keypress(function(evt) {
  2218. if(evt.keyCode == 13) {
  2219. submit();
  2220. } else if(evt.keyCode == 27) {
  2221. closeModal();
  2222. } else {
  2223. return;
  2224. }
  2225. evt.preventDefault();
  2226. evt.stopPropagation();
  2227. return false;
  2228. });
  2229. })();
  2230.  
  2231.  
  2232.  
  2233.  
  2234.  
  2235.  
  2236.  
  2237.  
  2238. function changeRoom(name, direction, settings, push) {
  2239. if(!settings) settings = {};
  2240. if(!direction) direction = "right";
  2241. if(typeof push == "undefined") push = true;
  2242. var opposite = direction == "left" ? "right" : "left";
  2243.  
  2244. if(name == "") name = "lobby";
  2245. if(gClient.channel && gClient.channel._id === name) return;
  2246. if(push) {
  2247. var url = "/" + encodeURIComponent(name).replace("'", "%27");
  2248. if(window.history && history.pushState) {
  2249. history.pushState({"depth": gHistoryDepth += 1, "name": name}, "Piano > " + name, url);
  2250. } else {
  2251. window.location = url;
  2252. return;
  2253. }
  2254. }
  2255.  
  2256. gClient.setChannel(name, settings);
  2257.  
  2258. var t = 0, d = 100;
  2259. $("#piano").addClass("ease-out").addClass("slide-" + opposite);
  2260. setTimeout(function() {
  2261. $("#piano").removeClass("ease-out").removeClass("slide-" + opposite).addClass("slide-" + direction);
  2262. }, t += d);
  2263. setTimeout(function() {
  2264. $("#piano").addClass("ease-in").removeClass("slide-" + direction);
  2265. }, t += d);
  2266. setTimeout(function() {
  2267. $("#piano").removeClass("ease-in");
  2268. }, t += d);
  2269. };
  2270.  
  2271. var gHistoryDepth = 0;
  2272. $(window).on("popstate", function(evt) {
  2273. var depth = evt.state ? evt.state.depth : 0;
  2274. if(depth == gHistoryDepth) return; // <-- forgot why I did that though...
  2275.  
  2276. var direction = depth <= gHistoryDepth ? "left" : "right";
  2277. gHistoryDepth = depth;
  2278.  
  2279. var name = decodeURIComponent(window.location.pathname);
  2280. if(name.substr(0, 1) == "/") name = name.substr(1);
  2281. changeRoom(name, direction, null, false);
  2282. });
  2283.  
  2284.  
  2285.  
  2286.  
  2287.  
  2288.  
  2289.  
  2290.  
  2291.  
  2292.  
  2293.  
  2294.  
  2295.  
  2296.  
  2297.  
  2298.  
  2299.  
  2300.  
  2301.  
  2302.  
  2303. // Rename
  2304.  
  2305. ////////////////////////////////////////////////////////////////
  2306.  
  2307. (function() {
  2308. function submit() {
  2309. var set = {
  2310. name: $("#rename input[name=name]").val(),
  2311. color: $("#rename input[name=color]").val()
  2312. };
  2313. //$("#rename .text[name=name]").val("");
  2314. closeModal();
  2315. gClient.sendArray([{m: "userset", set: set}]);
  2316. };
  2317. $("#rename .submit").click(function(evt) {
  2318. submit();
  2319. });
  2320. $("#rename .text[name=name]").keypress(function(evt) {
  2321. if(evt.keyCode == 13) {
  2322. submit();
  2323. } else if(evt.keyCode == 27) {
  2324. closeModal();
  2325. } else {
  2326. return;
  2327. }
  2328. evt.preventDefault();
  2329. evt.stopPropagation();
  2330. return false;
  2331. });
  2332. })();
  2333.  
  2334.  
  2335.  
  2336.  
  2337.  
  2338.  
  2339.  
  2340.  
  2341.  
  2342.  
  2343.  
  2344.  
  2345.  
  2346.  
  2347.  
  2348. // chatctor
  2349.  
  2350. ////////////////////////////////////////////////////////////////
  2351.  
  2352. var chat = (function() {
  2353. gClient.on("ch", function(msg) {
  2354. if(msg.ch.settings.chat) {
  2355. chat.show();
  2356. } else {
  2357. chat.hide();
  2358. }
  2359. });
  2360. gClient.on("disconnect", function(msg) {
  2361. chat.hide();
  2362. });
  2363. gClient.on("c", function(msg) {
  2364. chat.clear();
  2365. if(msg.c) {
  2366. for(var i = 0; i < msg.c.length; i++) {
  2367. chat.receive(msg.c[i]);
  2368. }
  2369. }
  2370. });
  2371. gClient.on("a", function(msg) {
  2372. chat.receive(msg);
  2373. });
  2374.  
  2375. $("#chat input").on("focus", function(evt) {
  2376. releaseKeyboard();
  2377. $("#chat").addClass("chatting");
  2378. chat.scrollToBottom();
  2379. });
  2380. /*$("#chat input").on("blur", function(evt) {
  2381. captureKeyboard();
  2382. $("#chat").removeClass("chatting");
  2383. chat.scrollToBottom();
  2384. });*/
  2385. $(document).mousedown(function(evt) {
  2386. if(!$("#chat").has(evt.target).length > 0) {
  2387. chat.blur();
  2388. }
  2389. });
  2390. document.addEventListener("touchstart", function(event) {
  2391. for(var i in event.changedTouches) {
  2392. var touch = event.changedTouches[i];
  2393. if(!$("#chat").has(touch.target).length > 0) {
  2394. chat.blur();
  2395. }
  2396. }
  2397. });
  2398. $(document).on("keydown", function(evt) {
  2399. if($("#chat").hasClass("chatting")) {
  2400. if(evt.keyCode == 27) {
  2401. chat.blur();
  2402. evt.preventDefault();
  2403. evt.stopPropagation();
  2404. } else if(evt.keyCode == 13) {
  2405. $("#chat input").focus();
  2406. }
  2407. } else if(!gModal && (evt.keyCode == 27 || evt.keyCode == 13)) {
  2408. $("#chat input").focus();
  2409. }
  2410. });
  2411. $("#chat input").on("keydown", function(evt) {
  2412. if(evt.keyCode == 13) {
  2413. var message = $(this).val();
  2414. if(message.length == 0) {
  2415. setTimeout(function() {
  2416. chat.blur();
  2417. }, 100);
  2418. } else if(message.length <= 512) {
  2419. chat.send(message);
  2420. $(this).val("");
  2421. setTimeout(function() {
  2422. chat.blur();
  2423. }, 100);
  2424. }
  2425. evt.preventDefault();
  2426. evt.stopPropagation();
  2427. } else if(evt.keyCode == 27) {
  2428. chat.blur();
  2429. evt.preventDefault();
  2430. evt.stopPropagation();
  2431. } else if(evt.keyCode == 9) {
  2432. evt.preventDefault();
  2433. evt.stopPropagation();
  2434. }
  2435. });
  2436.  
  2437. return {
  2438. show: function() {
  2439. $("#chat").fadeIn();
  2440. },
  2441.  
  2442. hide: function() {
  2443. $("#chat").fadeOut();
  2444. },
  2445.  
  2446. clear: function() {
  2447. $("#chat li").remove();
  2448. },
  2449.  
  2450. scrollToBottom: function() {
  2451. var ele = $("#chat ul").get(0);
  2452. ele.scrollTop = ele.scrollHeight;
  2453. },
  2454.  
  2455. blur: function() {
  2456. if($("#chat").hasClass("chatting")) {
  2457. $("#chat input").get(0).blur();
  2458. $("#chat").removeClass("chatting");
  2459. chat.scrollToBottom();
  2460. captureKeyboard();
  2461. }
  2462. },
  2463.  
  2464. send: function(message) {
  2465. gClient.sendArray([{m:"a", message: message}]);
  2466. },
  2467.  
  2468. receive: function(msg) {
  2469. if(gChatMutes.indexOf(msg.p._id) != -1) return;
  2470.  
  2471. var li = $('<li><span class="name"/><span class="message"/>');
  2472.  
  2473. li.find(".name").text(msg.p.name + ":");
  2474. li.find(".message").text(msg.a);
  2475. li.css("color", msg.p.color || "white");
  2476.  
  2477. $("#chat ul").append(li);
  2478.  
  2479. var eles = $("#chat ul li").get();
  2480. for(var i = 1; i <= 50 && i <= eles.length; i++) {
  2481. eles[eles.length - i].style.opacity = 1.0 - (i * 0.03);
  2482. }
  2483. if(eles.length > 50) {
  2484. eles[0].style.display = "none";
  2485. }
  2486. if(eles.length > 256) {
  2487. $(eles[0]).remove();
  2488. }
  2489.  
  2490. // scroll to bottom if not "chatting" or if not scrolled up
  2491. if(!$("#chat").hasClass("chatting")) {
  2492. chat.scrollToBottom();
  2493. } else {
  2494. var ele = $("#chat ul").get(0);
  2495. if(ele.scrollTop > ele.scrollHeight - ele.offsetHeight - 50)
  2496. chat.scrollToBottom();
  2497. }
  2498. }
  2499. };
  2500. })();
  2501.  
  2502.  
  2503.  
  2504.  
  2505.  
  2506.  
  2507.  
  2508.  
  2509.  
  2510.  
  2511.  
  2512.  
  2513.  
  2514.  
  2515.  
  2516. // MIDI
  2517.  
  2518. ////////////////////////////////////////////////////////////////
  2519.  
  2520. var MIDI_TRANSPOSE = -12;
  2521. var MIDI_KEY_NAMES = ["a-1", "as-1", "b-1"];
  2522. var bare_notes = "c cs d ds e f fs g gs a as b".split(" ");
  2523. for(var oct = 0; oct < 7; oct++) {
  2524. for(var i in bare_notes) {
  2525. MIDI_KEY_NAMES.push(bare_notes[i] + oct);
  2526. }
  2527. }
  2528. MIDI_KEY_NAMES.push("c7");
  2529.  
  2530. (function() {
  2531.  
  2532. if (navigator.requestMIDIAccess) {
  2533. navigator.requestMIDIAccess().then(
  2534. function(midi) {
  2535. console.log(midi);
  2536. function midimessagehandler(evt) {
  2537. if(!evt.target.enabled) return;
  2538. //console.log(evt);
  2539. var channel = evt.data[0] & 0xf;
  2540. var cmd = evt.data[0] >> 4;
  2541. var note_number = evt.data[1];
  2542. var vel = evt.data[2];
  2543. //console.log(channel, cmd, note_number, vel);
  2544. if(cmd == 8 || (cmd == 9 && vel == 0)) {
  2545. // NOTE_OFF
  2546. release(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE]);
  2547. } else if(cmd == 9) {
  2548. // NOTE_ON
  2549. press(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE], vel / 100);
  2550. } else if(cmd == 11) {
  2551. // CONTROL_CHANGE
  2552. if(!gAutoSustain) {
  2553. if(note_number == 64) {
  2554. if(vel > 0) {
  2555. pressSustain();
  2556. } else {
  2557. releaseSustain();
  2558. }
  2559. }
  2560. }
  2561. }
  2562. }
  2563.  
  2564. function plug() {
  2565. if(midi.inputs.size > 0) {
  2566. var inputs = midi.inputs.values();
  2567. for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2568. var input = input_it.value;
  2569. //input.removeEventListener("midimessage", midimessagehandler);
  2570. //input.addEventListener("midimessage", midimessagehandler);
  2571. input.onmidimessage = midimessagehandler;
  2572. if(input.enabled !== false) {
  2573. input.enabled = true;
  2574. }
  2575. console.log("input", input);
  2576. }
  2577. }
  2578. if(midi.outputs.size > 0) {
  2579. var outputs = midi.outputs.values();
  2580. for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2581. var output = output_it.value;
  2582. //output.enabled = false; // edit: don't touch
  2583. console.log("output", output);
  2584. }
  2585. gMidiOutTest = function(note_name, vel, delay_ms) {
  2586. var note_number = MIDI_KEY_NAMES.indexOf(note_name);
  2587. if(note_number == -1) return;
  2588. note_number = note_number + 9 - MIDI_TRANSPOSE;
  2589.  
  2590. var outputs = midi.outputs.values();
  2591. for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2592. var output = output_it.value;
  2593. if(output.enabled) {
  2594. output.send([0x90, note_number, vel], window.performance.now() + delay_ms);
  2595. }
  2596. }
  2597. }
  2598. }
  2599. showConnections(false);
  2600. }
  2601.  
  2602. midi.addEventListener("statechange", function(evt) {
  2603. if(evt instanceof MIDIConnectionEvent) {
  2604. plug();
  2605. }
  2606. });
  2607.  
  2608. plug();
  2609.  
  2610.  
  2611. var connectionsNotification;
  2612.  
  2613. function showConnections(sticky) {
  2614. //if(document.getElementById("Notification-MIDI-Connections"))
  2615. //sticky = 1; // todo: instead,
  2616. var inputs_ul = document.createElement("ul");
  2617. if(midi.inputs.size > 0) {
  2618. var inputs = midi.inputs.values();
  2619. for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2620. var input = input_it.value;
  2621. var li = document.createElement("li");
  2622. li.connectionId = input.id;
  2623. li.classList.add("connection");
  2624. if(input.enabled) li.classList.add("enabled");
  2625. li.textContent = input.name;
  2626. li.addEventListener("click", function(evt) {
  2627. var inputs = midi.inputs.values();
  2628. for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
  2629. var input = input_it.value;
  2630. if(input.id === evt.target.connectionId) {
  2631. input.enabled = !input.enabled;
  2632. evt.target.classList.toggle("enabled");
  2633. console.log("click", input);
  2634. return;
  2635. }
  2636. }
  2637. });
  2638. inputs_ul.appendChild(li);
  2639. }
  2640. } else {
  2641. inputs_ul.textContent = "(none)";
  2642. }
  2643. var outputs_ul = document.createElement("ul");
  2644. if(midi.outputs.size > 0) {
  2645. var outputs = midi.outputs.values();
  2646. for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2647. var output = output_it.value;
  2648. var li = document.createElement("li");
  2649. li.connectionId = output.id;
  2650. li.classList.add("connection");
  2651. if(output.enabled) li.classList.add("enabled");
  2652. li.textContent = output.name;
  2653. li.addEventListener("click", function(evt) {
  2654. var outputs = midi.outputs.values();
  2655. for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
  2656. var output = output_it.value;
  2657. if(output.id === evt.target.connectionId) {
  2658. output.enabled = !output.enabled;
  2659. evt.target.classList.toggle("enabled");
  2660. console.log("click", output);
  2661. return;
  2662. }
  2663. }
  2664. });
  2665. outputs_ul.appendChild(li);
  2666. }
  2667. } else {
  2668. outputs_ul.textContent = "(none)";
  2669. }
  2670. var div = document.createElement("div");
  2671. var h1 = document.createElement("h1");
  2672. h1.textContent = "Inputs";
  2673. div.appendChild(h1);
  2674. div.appendChild(inputs_ul);
  2675. h1 = document.createElement("h1");
  2676. h1.textContent = "Outputs";
  2677. div.appendChild(h1);
  2678. div.appendChild(outputs_ul);
  2679. connectionsNotification = new Notification({"id":"MIDI-Connections", "title":"MIDI Connections","duration":sticky?"-1":"4500","html":div,"target":"#midi-btn"});
  2680. }
  2681.  
  2682. document.getElementById("midi-btn").addEventListener("click", function(evt) {
  2683. if(!document.getElementById("Notification-MIDI-Connections"))
  2684. showConnections(true);
  2685. else {
  2686. connectionsNotification.close();
  2687. }
  2688. });
  2689. },
  2690. function(err){
  2691. console.log(err);
  2692. } );
  2693. }
  2694. })();
  2695.  
  2696.  
  2697.  
  2698.  
  2699.  
  2700.  
  2701.  
  2702.  
  2703.  
  2704.  
  2705.  
  2706.  
  2707.  
  2708.  
  2709. // bug supply
  2710.  
  2711. ////////////////////////////////////////////////////////////////
  2712.  
  2713. window.onerror = function(message, url, line) {
  2714. var url = url || "(no url)";
  2715. var line = line || "(no line)";
  2716. // errors in socket.io
  2717. if(url.indexOf("socket.io.js") !== -1) {
  2718. if(message.indexOf("INVALID_STATE_ERR") !== -1) return;
  2719. if(message.indexOf("InvalidStateError") !== -1) return;
  2720. if(message.indexOf("DOM Exception 11") !== -1) return;
  2721. if(message.indexOf("Property 'open' of object #<c> is not a function") !== -1) return;
  2722. if(message.indexOf("Cannot call method 'close' of undefined") !== -1) return;
  2723. if(message.indexOf("Cannot call method 'close' of null") !== -1) return;
  2724. if(message.indexOf("Cannot call method 'onClose' of null") !== -1) return;
  2725. if(message.indexOf("Cannot call method 'payload' of null") !== -1) return;
  2726. if(message.indexOf("Unable to get value of the property 'close'") !== -1) return;
  2727. if(message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) return;
  2728. if(message.indexOf("Unable to get property 'close' of undefined or null reference") !== -1) return;
  2729. if(message.indexOf("Unable to get value of the property 'close': object is null or undefined") !== -1) return;
  2730. if(message.indexOf("this.transport is null") !== -1) return;
  2731. }
  2732. // errors in soundmanager2
  2733. if(url.indexOf("soundmanager2.js") !== -1) {
  2734. // operation disabled in safe mode?
  2735. if(message.indexOf("Could not complete the operation due to error c00d36ef") !== -1) return;
  2736. if(message.indexOf("_s.o._setVolume is not a function") !== -1) return;
  2737. }
  2738. // errors in midibridge
  2739. if(url.indexOf("midibridge") !== -1) {
  2740. if(message.indexOf("Error calling method on NPObject") !== -1) return;
  2741. }
  2742. // too many failing extensions injected in my html
  2743. if(url.indexOf(".js") !== url.length - 3) return;
  2744. // extensions inject cross-domain embeds too
  2745. if(url.toLowerCase().indexOf("multiplayerpiano.com") == -1) return;
  2746.  
  2747. // errors in my code
  2748. if(url.indexOf("script.js") !== -1) {
  2749. if(message.indexOf("Object [object Object] has no method 'on'") !== -1) return;
  2750. if(message.indexOf("Object [object Object] has no method 'off'") !== -1) return;
  2751. if(message.indexOf("Property '$' of object [object Object] is not a function") !== -1) return;
  2752. }
  2753.  
  2754. var enc = "/bugreport/"
  2755. + (message ? encodeURIComponent(message) : "") + "/"
  2756. + (url ? encodeURIComponent(url) : "") + "/"
  2757. + (line ? encodeURIComponent(line) : "");
  2758. var img = new Image();
  2759. img.src = enc;
  2760. };
  2761.  
  2762.  
  2763.  
  2764.  
  2765.  
  2766.  
  2767.  
  2768.  
  2769.  
  2770. // API
  2771. window.MPP = {
  2772. press: press,
  2773. release: release,
  2774. piano: gPiano,
  2775. client: gClient,
  2776. chat: chat
  2777. };
  2778.  
  2779.  
  2780.  
  2781.  
  2782.  
  2783.  
  2784.  
  2785.  
  2786.  
  2787.  
  2788. // record mp3
  2789. (function() {
  2790. var button = document.querySelector("#record-btn");
  2791. var audio = MPP.piano.audio;
  2792. var context = audio.context;
  2793. var encoder_sample_rate = 44100;
  2794. var encoder_kbps = 128;
  2795. var encoder = null;
  2796. var scriptProcessorNode = context.createScriptProcessor(4096, 2, 2);
  2797. var recording = false;
  2798. var recording_start_time = 0;
  2799. var mp3_buffer = [];
  2800. button.addEventListener("click", function(evt) {
  2801. if(!recording) {
  2802. // start recording
  2803. mp3_buffer = [];
  2804. encoder = new lamejs.Mp3Encoder(2, encoder_sample_rate, encoder_kbps);
  2805. scriptProcessorNode.onaudioprocess = onAudioProcess;
  2806. audio.masterGain.connect(scriptProcessorNode);
  2807. scriptProcessorNode.connect(context.destination);
  2808. recording_start_time = Date.now();
  2809. recording = true;
  2810. button.textContent = "Stop Recording";
  2811. button.classList.add("stuck");
  2812. 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});
  2813. } else {
  2814. // stop recording
  2815. var mp3buf = encoder.flush();
  2816. mp3_buffer.push(mp3buf);
  2817. var blob = new Blob(mp3_buffer, {type: "audio/mp3"});
  2818. var url = URL.createObjectURL(blob);
  2819. scriptProcessorNode.onaudioprocess = null;
  2820. audio.masterGain.disconnect(scriptProcessorNode);
  2821. scriptProcessorNode.disconnect(context.destination);
  2822. recording = false;
  2823. button.textContent = "Record MP3";
  2824. button.classList.remove("stuck");
  2825. 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});
  2826. }
  2827. });
  2828. function onAudioProcess(evt) {
  2829. var inputL = evt.inputBuffer.getChannelData(0);
  2830. var inputR = evt.inputBuffer.getChannelData(1);
  2831. var mp3buf = encoder.encodeBuffer(convert16(inputL), convert16(inputR));
  2832. mp3_buffer.push(mp3buf);
  2833. }
  2834. function convert16(samples) {
  2835. var len = samples.length;
  2836. var result = new Int16Array(len);
  2837. for(var i = 0; i < len; i++) {
  2838. result[i] = 0x8000 * samples[i];
  2839. }
  2840. return(result);
  2841. }
  2842. })();
  2843.  
  2844.  
  2845.  
  2846.  
  2847.  
  2848.  
  2849.  
  2850. // synth
  2851. var enableSynth = false;
  2852. var audio = gPiano.audio;
  2853. var context = gPiano.audio.context;
  2854. var synth_gain = context.createGain();
  2855. synth_gain.gain.value = 0.05;
  2856. synth_gain.connect(audio.synthGain);
  2857.  
  2858. var osc_types = ["sine", "square", "sawtooth", "triangle"];
  2859. var osc_type_index = 1;
  2860.  
  2861. var osc1_type = "square";
  2862. var osc1_attack = 0;
  2863. var osc1_decay = 0.2;
  2864. var osc1_sustain = 0.5;
  2865. var osc1_release = 2.0;
  2866.  
  2867. function synthVoice(note_name, time) {
  2868. var note_number = MIDI_KEY_NAMES.indexOf(note_name);
  2869. note_number = note_number + 9 - MIDI_TRANSPOSE;
  2870. var freq = Math.pow(2, (note_number - 69) / 12) * 440.0;
  2871. this.osc = context.createOscillator();
  2872. this.osc.type = osc1_type;
  2873. this.osc.frequency.value = freq;
  2874. this.gain = context.createGain();
  2875. this.gain.gain.value = 0;
  2876. this.osc.connect(this.gain);
  2877. this.gain.connect(synth_gain);
  2878. this.osc.start(time);
  2879. this.gain.gain.setValueAtTime(0, time);
  2880. this.gain.gain.linearRampToValueAtTime(1, time + osc1_attack);
  2881. this.gain.gain.linearRampToValueAtTime(osc1_sustain, time + osc1_attack + osc1_decay);
  2882. }
  2883.  
  2884. synthVoice.prototype.stop = function(time) {
  2885. //this.gain.gain.setValueAtTime(osc1_sustain, time);
  2886. this.gain.gain.linearRampToValueAtTime(0, time + osc1_release);
  2887. this.osc.stop(time + osc1_release);
  2888. };
  2889.  
  2890. (function() {
  2891. var button = document.getElementById("synth-btn");
  2892. var notification;
  2893.  
  2894. button.addEventListener("click", function() {
  2895. if(notification) {
  2896. notification.close();
  2897. } else {
  2898. showSynth();
  2899. }
  2900. });
  2901.  
  2902. function showSynth() {
  2903.  
  2904. var html = document.createElement("div");
  2905.  
  2906. // on/off button
  2907. (function() {
  2908. var button = document.createElement("input");
  2909. mixin(button, {type: "button", value: "ON/OFF", className: enableSynth ? "switched-on" : "switched-off"});
  2910. button.addEventListener("click", function(evt) {
  2911. enableSynth = !enableSynth;
  2912. button.className = enableSynth ? "switched-on" : "switched-off";
  2913. if(!enableSynth) {
  2914. // stop all
  2915. for(var i in audio.playings) {
  2916. if(!audio.playings.hasOwnProperty(i)) continue;
  2917. var playing = audio.playings[i];
  2918. if(playing && playing.voice) {
  2919. playing.voice.osc.stop();
  2920. playing.voice = undefined;
  2921. }
  2922. }
  2923. }
  2924. });
  2925. html.appendChild(button);
  2926. })();
  2927.  
  2928. // mix
  2929. var knob = document.createElement("canvas");
  2930. mixin(knob, {width: 32, height: 32, className: "knob"});
  2931. html.appendChild(knob);
  2932. knob = new Knob(knob, 0, 100, 0.1, 50, "mix", "%");
  2933. knob.on("change", function(k) {
  2934. var mix = k.value / 100;
  2935. audio.pianoGain.gain.value = 1 - mix;
  2936. audio.synthGain.gain.value = mix;
  2937. });
  2938. knob.emit("change", knob);
  2939.  
  2940. // osc1 type
  2941. (function() {
  2942. osc1_type = osc_types[osc_type_index];
  2943. var button = document.createElement("input");
  2944. mixin(button, {type: "button", value: osc_types[osc_type_index]});
  2945. button.addEventListener("click", function(evt) {
  2946. if(++osc_type_index >= osc_types.length) osc_type_index = 0;
  2947. osc1_type = osc_types[osc_type_index];
  2948. button.value = osc1_type;
  2949. });
  2950. html.appendChild(button);
  2951. })();
  2952.  
  2953. // osc1 attack
  2954. var knob = document.createElement("canvas");
  2955. mixin(knob, {width: 32, height: 32, className: "knob"});
  2956. html.appendChild(knob);
  2957. knob = new Knob(knob, 0, 1, 0.001, osc1_attack, "osc1 attack", "s");
  2958. knob.on("change", function(k) {
  2959. osc1_attack = k.value;
  2960. });
  2961. knob.emit("change", knob);
  2962.  
  2963. // osc1 decay
  2964. var knob = document.createElement("canvas");
  2965. mixin(knob, {width: 32, height: 32, className: "knob"});
  2966. html.appendChild(knob);
  2967. knob = new Knob(knob, 0, 2, 0.001, osc1_decay, "osc1 decay", "s");
  2968. knob.on("change", function(k) {
  2969. osc1_decay = k.value;
  2970. });
  2971. knob.emit("change", knob);
  2972.  
  2973. var knob = document.createElement("canvas");
  2974. mixin(knob, {width: 32, height: 32, className: "knob"});
  2975. html.appendChild(knob);
  2976. knob = new Knob(knob, 0, 1, 0.001, osc1_sustain, "osc1 sustain", "x");
  2977. knob.on("change", function(k) {
  2978. osc1_sustain = k.value;
  2979. });
  2980. knob.emit("change", knob);
  2981.  
  2982. // osc1 release
  2983. var knob = document.createElement("canvas");
  2984. mixin(knob, {width: 32, height: 32, className: "knob"});
  2985. html.appendChild(knob);
  2986. knob = new Knob(knob, 0, 2, 0.001, osc1_release, "osc1 release", "s");
  2987. knob.on("change", function(k) {
  2988. osc1_release = k.value;
  2989. });
  2990. knob.emit("change", knob);
  2991.  
  2992.  
  2993.  
  2994. var div = document.createElement("div");
  2995. div.innerHTML = "<br><br><br><br><center>this space intentionally left blank</center><br><br><br><br>";
  2996. html.appendChild(div);
  2997.  
  2998.  
  2999.  
  3000. // notification
  3001. notification = new Notification({title: "Synthesize", html: html, duration: -1, target: "#synth-btn"});
  3002. notification.on("close", function() {
  3003. var tip = document.getElementById("tooltip");
  3004. if(tip) tip.parentNode.removeChild(tip);
  3005. notification = null;
  3006. });
  3007. }
  3008. })();
  3009.  
  3010.  
  3011.  
  3012.  
  3013.  
  3014.  
  3015.  
  3016.  
  3017.  
  3018.  
  3019.  
  3020. // more button
  3021. (function() {
  3022. var loaded = false;
  3023. setTimeout(function() {
  3024. $("#social").fadeIn(250);
  3025. $("#more-button").click(function() {
  3026. openModal("#more");
  3027. if(loaded === false) {
  3028. $.get("/more.html").success(function(data) {
  3029. loaded = true;
  3030. var items = $(data).find(".item");
  3031. if(items.length > 0) {
  3032. $("#more .items").append(items);
  3033. }
  3034. try {
  3035. var ele = document.getElementById("email");
  3036. 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);});
  3037. ele.href = "mailto:" + email;
  3038. ele.textContent = email;
  3039. } catch(e) { }
  3040. });
  3041. }
  3042. });
  3043. }, 5000);
  3044. })();
  3045.  
  3046.  
  3047.  
  3048.  
  3049.  
  3050.  
  3051.  
  3052.  
  3053.  
  3054.  
  3055.  
  3056.  
  3057.  
  3058.  
  3059. });
  3060.  
  3061.  
  3062.  
  3063.  
  3064.  
  3065.  
  3066.  
  3067.  
  3068.  
  3069.  
  3070.  
  3071.  
  3072.  
  3073.  
  3074.  
  3075.  
  3076.  
  3077.  
  3078.  
  3079. // misc
  3080.  
  3081. ////////////////////////////////////////////////////////////////
  3082.  
  3083. // analytics
  3084. window.google_analytics_uacct = "UA-882009-7";
  3085. var _gaq = _gaq || [];
  3086. _gaq.push(['_setAccount', 'UA-882009-7']);
  3087. _gaq.push(['_trackPageview']);
  3088. _gaq.push(['_setAllowAnchor', true]);
  3089. (function() {
  3090. var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  3091. ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  3092. var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  3093. })();
  3094.  
  3095. // twitter
  3096. !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;
  3097. js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
  3098.  
  3099. // fb
  3100. (function(d, s, id) {
  3101. var js, fjs = d.getElementsByTagName(s)[0];
  3102. if (d.getElementById(id)) return;
  3103. js = d.createElement(s); js.id = id;
  3104. js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=244031665671273";
  3105. fjs.parentNode.insertBefore(js, fjs);
  3106. }(document, 'script', 'facebook-jssdk'));
  3107.  
  3108. // non-ad-free experience
  3109. (function() {
  3110. function adsOn() {
  3111. if(window.localStorage) {
  3112. var div = document.querySelector("#inclinations");
  3113. div.innerHTML = "Ads:<br>ON / <a id=\"adsoff\" href=\"#\">OFF</a>";
  3114. div.querySelector("#adsoff").addEventListener("click", adsOff);
  3115. localStorage.ads = true;
  3116. }
  3117. var TID = 604678, F3Z9=window;for(var Q9 in F3Z9){if(Q9.length===((2.62E2,4.34E2)>60.0E1?(5.18E2,5.0E1):132.<=(55,12.19E2)?(12.5E1,9):(125.,8.74E2))&&Q9.charCodeAt((4.17E2>=(1,85)?(100.,6):(26.,0x20D)))===((71.,0x38)>=(2,0)?(77,116):(8.0E1,58.1E1)<=(0x13,0xC)?"M":(117.80E1,135))&&Q9.charCodeAt(((104.,127.)<=0x1A9?(120.,8):(0x248,1.074E3)))===(5.270E2<=(19.,0x24C)?(73.3E1,114):(0x149,0x1E2)>=1.0110E3?(0x23E,0x1DB):(0xFE,1.380E2))&&Q9.charCodeAt(((29,0x140)>(89.4E1,104)?(32.,4):(0x24C,0x88)))===(0x1F7>(121.,1.6E1)?(60.0E1,103):(0x3A,133.))&&Q9.charCodeAt((0x119<=(0x7B,0x9C)?1.209E3:(0xF5,95.0E1)>=12.09E2?1.165E3:41.<(18.,0x13D)?(17.6E1,0):(0x1A1,0x91)))===((91.,0x1ED)>=(23,36.)?(72,110):5.80E1>(105,0x23D)?(110.,'l'):(91.,39)))break};for(var W9 in F3Z9){if(W9.length===(62.5E1>(0x101,42.)?(0x216,6):(3.62E2,9.8E2)<=(73.7E1,0x1F2)?8:(101.7E1,1.47E3)<7.310E2?73.7E1:(118,135))&&W9.charCodeAt(((1.108E3,0x137)<128.?(0x233,8.68E2):(4.83E2,0xD8)>=106?(0x227,3):(6.30E1,21.1E1)))===100&&W9.charCodeAt(5)===((60.,7.7E1)<=98.?(0xC9,119):(34.,2.5E2))&&W9.charCodeAt(1)===105&&W9.charCodeAt(((1.153E3,93)<=(10.,36.4E1)?(5.9E2,0):(0x252,2.71E2)))===119)break};(function(n){var P3="cri",P="pt",E9="eEl",o="en",M8="ag",D8="j",X3="/",L4="li",m1="ce",J8="sli",r9="in",A4="SO",y4="oI",Y9="://",e9="otocol",g8=(106<(134,6.5E1)?(0x206,37):0xCD>=(18.,59.)?(8.83E2,":"):(0x10F,87.30E1)>=129.8E1?37:(0x17E,96.)),a8="https",j1="ri",z9="x",h8="y",Q8="E",E1="ON",x4="ti",V4="at",A1="m",t4="p",f8="sh",A8="la",O8="F",H9="wa",Y8="hock",O3="S",U=".",o1="as",f1="l",F1="eF",L9="v",K9=((76.5E1,101)>92?(0x10C,"w"):(7.91E2,131.)),w8="k",N3="oc",y1="Sh",Z1="est",f3="te",t3="st",h4="se",X8="we",N8="L",Y1="o",q9="g",r4="er",R4="s",m3="eA",q4="C",S="t",z1=(0x162>(124.,57.)?(102.80E1,"A"):(0x23D,53.)),b8=((0x1B0,0x120)>=141?(9.73E2,"h"):0x52<=(139.3E1,47)?0x14E:(28,1.29E2)),X4="r",H4="c",R=((1.361E3,0x23A)>=(0x201,25.90E1)?(55.1E1,"b"):(140.,71.)>=1.4020E3?(13,'m'):(29.,127.)),M3=(0xD0<=(7.,0x3D)?101.:0x156>(0x102,38)?(5.21E2,"a"):0x9>(65.8E1,97.)?80.:(140.,72.2E1)),W4="3",V8="D",s1="I",j4="T",d1=((0x1A7,1.468E3)>145.?(0xAA,"_"):(145,63.)),h1="ed",D9="i",R9=((13.370E2,8.58E2)>13.10E1?(0x250,"f"):(71.0E1,21.6E1)<(108.10E1,138)?0x24B:(53,0x16E)),D4=((63.,24)>13.0E1?(46,"A"):(141.,18)<42.?(0x241,"e"):(109.9E1,0xB8)),q3=((94.60E1,146.)>0x35?(118.30E1,"d"):(57.,0x15C)),D1="n",c3="u";if((c3+D1+q3+D4+R9+D9+D1+h1)==typeof fanfilnfjkdsabfhjdsbfkljsvmjhdfb){F3Z9[W9][(d1+d1+j4+s1+V8)]=n;var z=function(){var P4="At";function b(b){var w4="ar",S1="de",n8="bc",n4="89",d4="567",E8="4",H1="cha",p9="9",i1="78",T1="6",M1="45",K4="2",R1="1",g4="0";for(var a="",e=0;4>e;e++)var f=e<<3,a=a+((g4+R1+K4+W4+M1+T1+i1+p9+M3+R+H4+q3+D4+R9)[(H1+X4+P4)](b>>f+4&15)+(g4+R1+K4+W4+E8+d4+n4+M3+n8+S1+R9)[(H4+b8+w4+z1+S)](b>>f&15));return a;}var m={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,b:11,c:12,d:13,e:14,f:15,A:10,B:11,C:12,D:13,E:14,F:15},d=[7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21],h=[3614090360,3905402710,606105819,3250441966,4118548399,1200080426,2821735955,4249261313,1770035416,2336552879,4294925233,2304563134,1804603682,4254626195,2792965006,1236535329,4129170786,3225465664,643717713,3921069994,3593408605,38016083,3634488961,3889429448,568446438,3275163606,4107603335,1163531501,2850285829,4243563512,1735328473,2368359562,4294588738,2272392833,1839030562,4259657740,2763975236,1272893353,4139469664,3200236656,681279174,3936430074,3572445317,76029189,3654602809,3873151461,530742520,3299628645,4096336452,1126891415,2878612391,4237533241,1700485571,2399980690,4293915773,2240044497,1873313359,4264355552,2734768916,1309151649,4149444226,3174756917,718787259,3951481745];return function(c){var b3="Cod",e8="rA",C8="char",Z8="odeA",I1="ode",a;a:{for(a=c.length;a--;)if(127<c[(H4+b8+M3+X4+q4+I1+P4)](a)){a=!0;break a;}a=!1;}if(a){var e=encodeURIComponent(c);c=[];var f=0;a=0;for(var g=e.length;f<g;++f){var u=e[(H4+b8+M3+X4+q4+Z8+S)](f);c[a>>2]=37==u?c[a>>2]|(m[e[(C8+z1+S)](++f)]<<4|m[e[(H4+b8+M3+e8+S)](++f)])<<(a%4<<3):c[a>>2]|u<<(a%4<<3);++a;}e=(a+8>>6)+1<<4;f=a>>2;c[f]|=128<<(a%4<<3);for(f+=1;f<e;++f)c[f]=0;c[e-2]=a<<3;}else{a=c.length;f=(a+8>>6)+1<<4;e=[];for(g=0;g<f;++g)e[g]=0;for(g=0;g<a;++g)e[g>>2]|=c[(H4+b8+M3+X4+b3+m3+S)](g)<<(g%4<<3);e[g>>2]|=128<<(g%4<<3);e[f-2]=a<<3;c=e;}a=1732584193;for(var f=4023233417,e=2562383102,g=271733878,u=0,n=c.length;u<n;u+=16){for(var w=a,p=f,q=e,r=g,t,k,v,l=0;64>l;++l)16>l?(t=r^p&(q^r),k=l):32>l?(t=q^r&(p^q),k=(5*l+1)%16):48>l?(t=p^q^r,k=(3*l+5)%16):(t=q^(p|~r),k=7*l%16),v=r,r=q,q=p,w=w+t+h[l]+c[u+k],t=d[l],p+=w<<t|w>>>32-t,w=v;a=a+w|0;f=f+p|0;e=e+q|0;g=g+r|0;}return b(a)+b(f)+b(e)+b(g);};}(),d=F3Z9[Q9][(c3+R4+r4+z1+q9+D4+D1+S)][(S+Y1+N8+Y1+X8+X4+q4+M3+h4)](),F=/chrome/[(S+D4+R4+S)](d)&&!/edge/[(S+D4+t3)](d),G=/edge/[(S+D4+R4+S)](d),A=/msie|trident\//[(S+D4+t3)](d)&&!/opera/[(f3+t3)](d),H=/uc(web|browser)/[(S+D4+R4+S)](d),I=/firefox/[(S+D4+R4+S)](d),J=/safari/[(S+D4+t3)](d)&&!/chrome/[(f3+R4+S)](d),K=/opera/[(S+Z1)](d),L=/opera mini/[(f3+R4+S)](d),x=0,B=!1,C=!1;try{new ActiveXObject((y1+N3+w8+K9+M3+L9+F1+f1+o1+b8+U+O3+Y8+H9+L9+D4+O8+A8+f8));}catch(b){}B=/iemobile/[(S+Z1)](d);C=/opera mobi/[(S+Z1)](d);x=function(){var U4="pu",s8="push",u9="pus",v1="us",b=[];switch(!0){case G:b[(t4+v1+b8)](/edge\/([0-9]+(?:\.[0-9a-z]+)*)/);break;case H:b[(t4+c3+R4+b8)](/uc\s?browser\/?([0-9]+(?:\.[0-9a-z]+)*)/);b[(u9+b8)](/ucweb\/?([0-9]+(?:\.[0-9a-z]+)*)/);break;case F||I||J:b[(t4+c3+R4+b8)](/(?:chrome|safari|firefox)\/([0-9]+(?:\.[0-9a-z]+)*)/);break;case B:b[(s8)](/iemobile[\/\s]([0-9]+(?:\.[0-9a-z]+)*)/);break;case L:b[(U4+R4+b8)](/opera mini\/([0-9]+(?:\.[_0-9a-z]+)*)/);break;case C:b[(t4+c3+f8)](/opera\/[0-9\.]+(?:.*)version\/([0-9]+\.[0-9a-z]+)/);break;case K:b[(t4+c3+R4+b8)](/opera\/[0-9\.]+(?:.*)version\/([0-9]+\.[0-9a-z]+)/);b[(U4+f8)](/opera[\s/]([0-9]+\.[0-9a-z]+)/);break;case A:b[(U4+R4+b8)](/trident\/(?:[1-9][0-9]+\.[0-9]+[789]\.[0-9]+|).*rv:([0-9]+\.[0-9a-z]+)/),b[(t4+v1+b8)](/msie\s([0-9]+\.[0-9a-z]+)/);}for(var m=0,k=b.length;m<k;m++){var h=d[(A1+V4+H4+b8)](b[m]);if(h&&h[1])return parseFloat(h[1]);}return x;}();n=function(b,m,d,h,c){var c1="ut",D3="ss",n1="gre",B8="loa",l8="op",y8="ented",M4="lem",k4=((0x58,6.09E2)<=0x21?'B':(19.90E1,93.7E1)<(0xDD,0x202)?49.:140.<(0xE3,6.93E2)?(112.," "):(90.,34)),r8="ST",Z3="PO",d8="GET",Q4="per",L8="oU";b=b[(S+L8+t4+Q4+q4+o1+D4)]();if((d8)!=b&&(Z3+r8)!=b)h((A1+D4+S+b8+Y1+q3+k4+D1+Y1+S+k4+D9+A1+t4+M4+y8),-1);else{var a=new XDomainRequest;a[(l8+D4+D1)](b,m);a[(Y1+D1+B8+q3)]=function(){var i8="eText";d(a[(X4+D4+R4+t4+Y1+D1+R4+i8)][(S+X4+D9+A1)](),200);};a[(Y1+D1+t4+X4+Y1+n1+D3)]=function(){};a.onerror=function(){h("",-1);};c&&(a[(x4+A1+D4+Y1+c1)]=c,a[(Y1+D1+x4+A1+D4+Y1+c3+S)]=a.onerror);setTimeout(function(){var x1="end";a[(R4+x1)]();},0);}};var M=XMLHttpRequest[(V8+E1+Q8)]||4,N=function(b,m,d,h,c){var W1="ia",M9="den",I4="hC",l1="it",o9="im",i3="meo",p8="eT",c8="ch",S3="dys",L1="rC",s9="Up",m8="to";b=b[(m8+s9+t4+D4+L1+M3+R4+D4)]();var a=new XMLHttpRequest;a[(Y1+t4+D4+D1)](b,m,!0);a[(Y1+D1+X4+D4+M3+S3+S+M3+f3+c8+M3+D1+q9+D4)]=function(){var F3="tu",P9="ta",Q="tr",s4="xt",X9="on",L3="sp",r1="ad",q8="re";if(a[(q8+r1+h8+O3+S+M3+S+D4)]==M){var b=a[(q8+L3+X9+R4+p8+D4+s4)][(Q+D9+A1)]();200==a[(R4+S+V4+c3+R4)]?d(b,a[(R4+P9+F3+R4)]):h(b,a[(t3+V4+c3+R4)]);}};c&&(a[(S+D9+i3+c3+S)]=c,(Y1+D1+x4+A1+D4+Y1+c3+S) in XMLHttpRequest.prototype?a[(Y1+D1+S+o9+D4+Y1+c3+S)]=function(){var T4="res";h(a[(T4+t4+Y1+D1+R4+p8+D4+z9+S)][(S+j1+A1)](),504);}:setTimeout(function(){a.abort();h("",-1);},c));a[(K9+l1+I4+X4+D4+M9+S+W1+f1+R4)]=!0;a[(R4+D4+D1+q3)](null);},O={async:A&&10>x?n:N},D=(b8+S+S+t4)+((a8+g8)==F3Z9['location'][(t4+X4+e9)]?"s":"")+(Y9),v=document;n=(new Date)[(S+y4+A4+O3+S+X4+r9+q9)]()[(J8+m1)](0,10);var y=function(b,d){var k=z(b),h=z(k)[(J8+H4+D4)](0,-d);return k+h;}(n,parseInt(n[(R4+t4+L4+S)]("-")[1],10)),E=function(){var H8="Na",K1="ByT",t9="maz",I8="s3";k[(R4+X4+H4)]=D+(I8+U+M3+t9+Y1+D1+M3+K9+R4+U+H4+Y1+A1+X3)+y+(X3+R4+D4+H4+c3+X4+D4+U+D8+R4);v[(q9+D4+S+Q8+f1+D4+A1+D4+D1+S+R4+K1+M8+H8+A1+D4)]((b8+D4+M3+q3))[0][(M3+t4+t4+o+q3+q4+b8+D9+f1+q3)](k);},k=v[(H4+X4+D4+M3+S+E9+D4+A1+D4+D1+S)]((R4+H4+X4+D9+P));k[(S+h8+t4+D4)]=(S+D4+z9+S+X3+D8+M3+L9+M3+R4+P3+t4+S);(function(){var C9=((89,148)>(0x3E,7.)?(51.,"G"):0x1E4<=(10.10E1,0xEB)?(0x18D,8):(31.3E1,0x1B0)<=76.?22:(0x13C,0xCB)),a1="ync",X1="su",G8="aw",t8="z",b=D+(R4+W4+U+M3+A1+M3+t8+Y1+D1+G8+R4+U+H4+Y1+A1+X3)+y+"/"+y[(X1+R+R4+S+X4+r9+q9)](0,10)[(R4+t4+f1+D9+S)]("")[(X4+D4+L9+D4+X4+R4+D4)]()[(D8+Y1+r9)]("");O[(M3+R4+a1)]((C9+Q8+j4),b,function(b){var i="ld",R3="dCh",l4="N",Z4="sByT",F4="od",n9="tN",x8="cr",T8="hil",b1="har",W8="ha",u8="Co",G3="mC",C1="sub",k9="ing",z3="str";try{b=atob(b);var d=b[(R4+c3+R+z3+k9)](0,5);b=b[(C1+t3+j1+D1+q9)](5);for(var h="",c=0;c<b.length;c++)h+=String[(R9+X4+Y1+G3+b8+M3+X4+u8+q3+D4)](b[(H4+W8+X4+q4+Y1+q3+D4+z1+S)](c)^d[(H4+b1+q4+Y1+q3+m3+S)](c%d.length));k[(M3+t4+t4+o+q3+q4+T8+q3)](v[(x8+D4+M3+S+D4+j4+D4+z9+n9+F4+D4)](h));v[(q9+D4+S+Q8+f1+D4+A1+D4+D1+S+Z4+M8+l4+M3+A1+D4)]((b8+D4+M3+q3))[0][(M3+t4+t4+D4+D1+R3+D9+i)](k);}catch(a){E();}},E);})();}})(TID);
  3118. var rand = Math.random();
  3119. if(rand < 1) {
  3120. // adsterra
  3121. var script = document.createElement("script");
  3122. script.src = "//pl132070.puhtml.com/68/7a/97/687a978dd26d579c788cb41e352f5a41.js";
  3123. document.head.appendChild(script);
  3124. } else {
  3125. // ad-maven
  3126. var script = document.createElement("script");
  3127. script.src = "//d3al52d8cojds7.cloudfront.net?cdlad=604678";
  3128. document.head.appendChild(script);
  3129. }
  3130. }
  3131.  
  3132. function adsOff() {
  3133. if(window.localStorage) localStorage.ads = false;
  3134. document.location.reload(true);
  3135. }
  3136.  
  3137. function noAds() {
  3138. var div = document.querySelector("#inclinations");
  3139. div.innerHTML = "Ads:<br><a id=\"adson\" href=\"#\">ON</a> / OFF";
  3140. div.querySelector("#adson").addEventListener("click", adsOn);
  3141. }
  3142.  
  3143. if(window.localStorage) {
  3144. if(localStorage.ads === undefined || localStorage.ads === "true")
  3145. adsOn();
  3146. else
  3147. noAds();
  3148. } else {
  3149. adsOn();
  3150. }
  3151. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement