Advertisement
Ninjalord8

MPP Base Piano 2

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