· 9 years ago · Jan 04, 2017, 08:24 PM
1// ==UserScript==
2// @name MPP Script
3// @namespace http://www.multiplayerpiano.com
4// @version 2.2
5// @description Multiplayer Piano script
6// @author Oekama
7// @include http://www.multiplayerpiano.com/*
8// @match http://www.multiplayerpiano.com/script.js
9// @grant none
10// ==/UserScript==
11
12(function() {
13 $(function ( ) {
14
15 // Язык интерфейÑа
16
17 var Language = { };
18 var Languages =
19 {
20 'РуÑÑкий':
21 {
22 'NewRoom Button' : 'Ðовый номер',
23 'Midi Button' : 'ÐаÑтройки MIDI',
24 'PlayAlone Button' : 'Играть Ñоло',
25 'RoomSettings Button' : 'ÐаÑтройки',
26 'ClearChat Button' : 'ОчиÑтить чат',
27 'SetLanguage Button' : 'Изменить Ñзык',
28 'SecretRoom Button' : 'Ð¢Ð°Ð¹Ð½Ð°Ñ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ð°',
29 'Commands Button' : 'Команды',
30 'DropCrown Button' : 'СнÑть корону',
31
32 'MuteNotes Tool' : 'Отключить звук',
33 'UnmuteNotes Tool' : 'Включить звук',
34 'MuteChat Tool' : 'Отключить чат',
35 'UnmuteChat Tool' : 'Включить чат',
36 'MuteCompletely Tool' : 'Добавить в игнор',
37 'UnmuteCompletely Tool' : 'СнÑть игнор',
38 'GiveCrown Tool' : 'Отдать корону',
39 'Kickban Tool' : 'Заблокировать',
40
41 'Visible Option' : 'Ð’Ð¸Ð´Ð¸Ð¼Ð°Ñ (ÐžÑ‚ÐºÑ€Ñ‹Ñ‚Ð°Ñ Ð´Ð»Ñ Ð²Ñех)',
42 'Chat Option' : 'Включить чат',
43 'Crownsolo Option' : 'Играть можете только вы',
44 'Background Option' : 'Цвет фона',
45
46 'NewRoom Title' : 'Комната добавлена в ÑпиÑок',
47 'Midi Title' : 'ÐаÑтройки MIDI',
48 'MidiInput Title' : 'Входные уÑтройÑтва',
49 'MidiOutput Title' : 'Выходные уÑтройÑтва',
50 'PlayAlone Title' : 'Одиночный режим',
51 'ClearChat Title' : 'Уведомление',
52 'SecretRoom Title' : 'Ð¢Ð°Ð¹Ð½Ð°Ñ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ð°',
53 'UCanPlay Title' : 'Ð’Ñ‹ еще не догадалиÑÑŒ?!',
54 'AudioError Title' : 'Ошибка',
55
56 'NewRoom Description' : 'Ð’Ñ‹ можете приглаÑить ваших друзей в Ñту комнату по ÑÑылке в адреÑной Ñтроке браузера',
57 'PlayAlone Description' : 'Ваша комната Ñкрыта от поÑторонних глаз, однако вы можете приглаÑить Ñюда друзей по ÑÑылке в адреÑной Ñтроке браузера',
58 'ClearChat Description' : 'Чат был уÑпешно очищен (Только Ð´Ð»Ñ Ð²Ð°Ñ)',
59 'SecretRoom Description' : 'Без Ñкрипта и при отÑутÑтвии комнаты в ÑпиÑке здеÑÑŒ Ð²Ð°Ñ Ð½Ð¸ÐºÑ‚Ð¾ не увидит. Даже еÑли вы очень захотите',
60 'UCanPlay Description' : 'Ðа Ñтом пианино можно играть и Ñ ÐºÐ»Ð°Ð²Ð¸Ð°Ñ‚ÑƒÑ€Ñ‹. Попробуйте!',
61 'AudioError Description' : 'Ðе удаетÑÑ Ð·Ð°Ð³Ñ€ÑƒÐ·Ð¸Ñ‚ÑŒ звуковые файлы',
62
63 'Chat Hint' : 'Введите ваше Ñообщение',
64 'NewNickname Hint' : 'Ваш новый никнейм',
65 'NewRoom Hint' : 'Ðазвание комнаты',
66
67 'MidiEmpty Status' : 'нет',
68 'Crownsolo Status' : 'Ð’ Ñтой комнате может играть только ее владелец',
69 'You Status' : 'Ð’Ñ‹',
70
71 'Help Command' : 'ДоÑтупные команды',
72 'LangExist Command' : 'Язык интерфейÑа был изменен на руÑÑкий',
73 'LangNotExist Command' : 'Ðтот Ñзык не поддерживаетÑÑ Ð² данной верÑии',
74 'LangExample Command' : 'Язык',
75 'LangList Command' : 'ДоÑтупные Ñзыки',
76 'UserName Command' : 'Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ',
77 'UserColor Command' : 'цвет',
78 'LocalTimeAgo Command' : ' мин. назад',
79 'LocalTimeNow Command' : 'СейчаÑ',
80 'LocalCount Command' : 'Игроков',
81 'LocalColor Command' : 'цвет фона',
82 'LocalChat Command' : 'вÑего Ñообщений',
83 'LocalLast Command' : 'поÑледнее',
84 'LocalNoData Command' : 'Ðе удаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ информацию по данному запроÑу',
85 'GlobalUsers Command' : 'КоличеÑтво пользователей',
86 'GlobalRooms Command' : 'вÑего комнат',
87 'GlobalCrownsolo Command' : ' Ñ Ð´Ð¾Ñтупом Ð´Ð»Ñ Ð¸Ð³Ñ€Ñ‹ только владельцу',
88 'RoomName Command' : 'Ðазвание комнаты',
89 'RoomCount Command' : 'игроков',
90 'RoomColor Command' : 'цвет фона',
91 'RoomSettings Command' : 'наÑтройки',
92 'RoomDefault Command' : 'Ñтандартные',
93 'RoomCrownsolo Command' : 'может играть только владелец',
94 'RoomNoChat Command' : 'чат отключен',
95 'RoomNoExist Command' : 'Комната Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем Ñкрыта или не ÑущеÑтвует',
96 'Rename Command' : 'Теперь Ñ ',
97 'Identifier Command' : 'Идентификатор команды изменен на',
98 'NoExist Command' : 'Такой команды не ÑущеÑтвует'
99 },
100
101 'English':
102 {
103 'NewRoom Button' : 'New Room',
104 'Midi Button' : 'MIDI Settings',
105 'PlayAlone Button' : 'Play Alone',
106 'RoomSettings Button' : 'Room Settings',
107 'ClearChat Button' : 'Clear Chat',
108 'SetLanguage Button' : 'Set Language',
109 'SecretRoom Button' : 'Secret Room',
110 'Commands Button' : 'Commands',
111 'DropCrown Button' : 'Drop Crown',
112
113 'MuteNotes Tool' : 'Mute Notes',
114 'UnmuteNotes Tool' : 'Unmute Notes',
115 'MuteChat Tool' : 'Mute Chat',
116 'UnmuteChat Tool' : 'Unmute Chat',
117 'MuteCompletely Tool' : 'Mute Completely',
118 'UnmuteCompletely Tool' : 'Unmute Completely',
119 'GiveCrown Tool' : 'Give Crown',
120 'Kickban Tool' : 'Kickban',
121
122 'Visible Option' : 'Visible (Open to everyone)',
123 'Chat Option' : 'Enable Chat',
124 'Crownsolo Option' : 'Only Owner can play',
125 'Background Option' : 'Background color',
126
127 'NewRoom Title' : 'Created a Room',
128 'Midi Title' : 'MIDI Connections',
129 'MidiInput Title' : 'Inputs',
130 'MidiOutput Title' : 'Output',
131 'PlayAlone Title' : 'Playing Alone',
132 'ClearChat Title' : 'Cleared',
133 'SecretRoom Title' : 'Secret room',
134 'UCanPlay Title' : 'Did you know!?',
135 'AudioError Title' : 'Problem',
136
137 'NewRoom Description' : 'You can invite friends to your room by sending them the link',
138 'PlayAlone Description' : 'You are playing alone in a room by yourself, but you can always invite friends by sending them the link',
139 'ClearChat Description' : 'Chat was successfully cleared (Only for you)',
140 'SecretRoom Description' : 'This room isn\'t available to follow the link. Enjoy!',
141 'UCanPlay Description' : 'You can play the piano with your keyboard, too. Try it!',
142 'AudioError Description' : 'For some reason, an audio download failed',
143
144 'Chat Hint' : 'You can chat with this thing',
145 'NewNickname Hint' : 'My Fancy New Nickname',
146 'NewRoom Hint' : 'Room Name',
147
148 'MidiEmpty Status' : 'none',
149 'Crownsolo Status' : 'This room is set to "Only Owner can play"',
150 'You Status' : 'You',
151
152 'Help Command' : 'All commands',
153 'LangExist Command' : 'Language was changed to English',
154 'LangNotExist Command' : 'This language doesn\'t exist',
155 'LangList Command' : 'All languages',
156 'UserName Command' : 'Your nickname',
157 'UserColor Command' : 'color',
158 'LocalTimeAgo Command' : ' min. ago',
159 'LocalTimeNow Command' : 'Now',
160 'LocalCount Command' : 'Count',
161 'LocalColor Command' : 'background',
162 'LocalChat Command' : 'messages count',
163 'LocalLast Command' : 'last message',
164 'LocalNoData Command' : 'No data exists for this query',
165 'GlobalUsers Command' : 'User count',
166 'GlobalRooms Command' : 'room count',
167 'GlobalCrownsolo Command' : ' is set to "Only Owner can play"',
168 'RoomName Command' : 'Room name',
169 'RoomCount Command' : 'user count',
170 'RoomColor Command' : 'background',
171 'RoomSettings Command' : 'settings',
172 'RoomDefault Command' : 'default',
173 'RoomCrownsolo Command' : 'only owner can play',
174 'RoomNoChat Command' : 'chat disabled',
175 'RoomNoExist Command' : 'This room is not visible or doesn\'t exist',
176 'Rename Command' : 'Now i\'m ',
177 'Identifier Command' : 'Command identifier was changed to',
178 'NoExist Command' : 'This command doesn\'t exist'
179 },
180
181 '日本語':
182 {
183 'NewRoom Button' : 'æ–°ã—ã„ルーム',
184 'Midi Button' : 'MIDIã®è¨å®š',
185 'PlayAlone Button' : '一人ã§å¼¾ã‘ã‚‹',
186 'RoomSettings Button' : 'ルームã®è¨å®š',
187 'ClearChat Button' : 'ãƒãƒ£ãƒƒãƒˆã‚’空ã«ã™ã‚‹',
188 'SetLanguage Button' : '言語を変ãˆã‚‹',
189 'SecretRoom Button' : '秘密ã®ãƒ«ãƒ¼ãƒ ',
190 'Commands Button' : 'コマンド',
191 'DropCrown Button' : 'çŽ‹å† ã‚’æŠ•ã’付ã‘ã‚‹',
192
193 'MuteNotes Tool' : 'ミュート音',
194 'UnmuteNotes Tool' : 'ミュート解除ã™ã‚‹éŸ³',
195 'MuteChat Tool' : 'ミュートãƒãƒ£ãƒƒãƒˆ',
196 'UnmuteChat Tool' : 'ミュート解除ã™ã‚‹ãƒãƒ£ãƒƒãƒˆ',
197 'MuteCompletely Tool' : '完全ã«ãƒŸãƒ¥ãƒ¼ãƒˆ',
198 'UnmuteCompletely Tool' : '完全ã«ãƒŸãƒ¥ãƒ¼ãƒˆè§£é™¤ã™ã‚‹',
199 'GiveCrown Tool' : 'çŽ‹å† ã‚’ä¸Žãˆã‚‹',
200 'Kickban Tool' : 'ç¦æ¢ã™ã‚‹',
201
202 'Visible Option' : 'ç›®ã«è¦‹ãˆã‚‹ï¼ˆã¿ã‚“ã‚ã®ãŸã‚ã«ï¼‰',
203 'Chat Option' : 'Enable Chat',
204 'Crownsolo Option' : 'Only Owner can play',
205 'Background Option' : '背景ã®è‰²',
206
207 'NewRoom Title' : 'ルームを作ã£ãŸ',
208 'Midi Title' : 'MIDIã®è¨å®š',
209 'MidiInput Title' : '入力',
210 'MidiOutput Title' : '出力',
211 'PlayAlone Title' : '一人ã§éŠã‚“ã§',
212 'ClearChat Title' : 'Cleared',
213 'SecretRoom Title' : '秘密ã®ãƒ«ãƒ¼ãƒ ',
214 'UCanPlay Title' : 'Did you know!?',
215 'AudioError Title' : 'Problem',
216
217 'NewRoom Description' : 'You can invite friends to your room by sending them the link',
218 'PlayAlone Description' : 'ã‚ãªãŸã¯ãƒ«ãƒ¼ãƒ ã§ä¸€äººã§å¼¾ãã¾ã™ã§ã™ã‘ã©ã€ã‚ãªãŸã¯ã‚ãªãŸã®å‹é”ã‚’ã“ã“ã«æ‹›å¾…ã§ãã‚‹',
219 'ClearChat Description' : 'Chat was successfully cleared (Only for you)',
220 'SecretRoom Description' : 'ã“ã®ãƒ«ãƒ¼ãƒ ã¯ãƒã‚¤ãƒ‘ーリンクã§å…¥ã‚Œã‚‹ã€‚楽ã—ã‚“ã§ï¼',
221 'UCanPlay Description' : 'You can play the piano with your keyboard, too. Try it!',
222 'AudioError Description' : 'For some reason, an audio download failed',
223
224 'Chat Hint' : 'ã‚ãªãŸã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’入力ã—ã¾ã™',
225 'NewNickname Hint' : 'ã‚ãªãŸã®ãƒ‹ãƒƒã‚¯ãƒãƒ¼ãƒ ',
226 'NewRoom Hint' : 'ルームã®åå‰',
227
228 'MidiEmpty Status' : 'ãªã—',
229 'Crownsolo Status' : 'This room is set to "Only Owner can play"',
230 'You Status' : 'ã‚ãªãŸ',
231
232 'Help Command' : 'ã™ã¹ã¦ã®ã‚³ãƒžãƒ³ãƒ‰',
233 'LangExist Command' : 'è¨€èªžã¯æ—¥æœ¬èªžã«å¤‰æ›´ã•れã¾ã—ãŸ',
234 'LangNotExist Command' : 'ã“ã®è¨€èªžãŒã‚りã¾ã›ã‚“',
235 'LangList Command' : 'ã™ã¹ã¦ã®è¨€èªž',
236 'UserName Command' : 'ã‚ãªãŸã®åå‰',
237 'UserColor Command' : '色',
238 'LocalTimeAgo Command' : '分å‰',
239 'LocalTimeNow Command' : '今',
240 'LocalCount Command' : 'ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æ•°',
241 'LocalColor Command' : '背景ã®è‰²',
242 'LocalChat Command' : 'messages count',
243 'LocalLast Command' : 'last message',
244 'LocalNoData Command' : 'No data exists for this query',
245 'GlobalUsers Command' : 'User count',
246 'GlobalRooms Command' : 'room count',
247 'GlobalCrownsolo Command' : ' is set to "Only Owner can play"',
248 'RoomName Command' : 'Room name',
249 'RoomCount Command' : 'user count',
250 'RoomColor Command' : 'background',
251 'RoomSettings Command' : 'settings',
252 'RoomDefault Command' : 'default',
253 'RoomCrownsolo Command' : 'only owner can play',
254 'RoomNoChat Command' : 'chat disabled',
255 'RoomNoExist Command' : 'This room is not visible or doesn\'t exist',
256 'Rename Command' : '今ç§ã¯',
257 'Identifier Command' : 'コマンドã®è˜åˆ¥åã¯',
258 'NoExist Command' : 'ã“ã®ã‚³ãƒžãƒ³ãƒ‰ãŒã‚りã¾ã›ã‚“'
259 }
260 }
261
262 function SetLanguage (Title)
263 {
264 var Options = $('#room-settings').find ('label').get ( );
265 var Texts = $('.text').get ( );
266
267 Language = Languages [Title];
268
269 Options [0].childNodes [1].nodeValue = Language ['Visible Option'];
270 Options [1].childNodes [1].nodeValue = Language ['Chat Option'];
271 Options [2].childNodes [1].nodeValue = Language ['Crownsolo Option'];
272 Options [3].childNodes [0].nodeValue = Language ['Background Option'] + ': ';
273
274 Texts [0].placeholder = Language ['NewRoom Hint'];
275 Texts [1].placeholder = Language ['NewNickname Hint'];
276
277 $('.more').find ('.translate').html (Language ['NewRoom Button'] + '...');
278
279 $('#new-room-btn').html (Language ['NewRoom Button'] + '...');
280 $('#midi-btn').html (Language ['Midi Button']);
281 $('#play-alone-btn').html (Language ['PlayAlone Button']);
282 $('#room-settings-btn').html (Language ['RoomSettings Button']);
283 $('#clear-chat-btn').html (Language ['ClearChat Button']);
284 $('#set-lang-btn').html (Language ['SetLanguage Button']);
285 $('#secret-room-btn').html (Language ['SecretRoom Button']);
286 $('#commands-btn').html (Language ['Commands Button']);
287 $('#room-settings').find ('.ugly-button').html (Language ['DropCrown Button']);
288
289 $('#new-room').find ('label').first ( ).get (0).childNodes [1].nodeValue = Language ['Visible Option'];
290 $('#chat').find ('input').get (0).placeholder = Language ['Chat Hint'];
291
292 if ($('#Notify-NewRoom').length > 0)
293 {
294 var CurrentNotify = $('#Notify-NewRoom');
295
296 CurrentNotify.find ('.title').html (Language ['NewRoom Title']);
297 CurrentNotify.find ('.text').get (0).childNodes [0].nodeValue = Language ['NewRoom Description'];
298 }
299
300 if ($('#Notify-Midi').length > 0)
301 {
302 var CurrentNotify = $('#Notify-Midi');
303
304 CurrentNotify.find ('.title').html (Language ['Midi Title']);
305
306 CurrentNotify.find ('h1').eq (0).html (Language ['MidiInput Title']);
307 CurrentNotify.find ('h1').eq (1).html (Language ['MidiOutput Title']);
308
309 if (CurrentNotify.find ('li').length <= 0)
310 CurrentNotify.find ('ul').html ('(' + Language ['MidiEmpty Status'] + ')');
311 }
312
313 if ($('#Notify-PlayAlone').length > 0)
314 {
315 var CurrentNotify = $('#Notify-PlayAlone');
316
317 CurrentNotify.find ('.title').html (Language ['PlayAlone Title']);
318 CurrentNotify.find ('.text').get (0).childNodes [0].nodeValue = Language ['PlayAlone Description'];
319 }
320
321 if ($('#Notify-ClearChat').length > 0)
322 {
323 var CurrentNotify = $('#Notify-ClearChat');
324
325 CurrentNotify.find ('.title').html (Language ['ClearChat Title']);
326 CurrentNotify.find ('.text').html (Language ['ClearChat Description']);
327 }
328
329 if ($('#Notify-SecretRoom').length > 0)
330 {
331 var CurrentNotify = $('#Notify-SecretRoom');
332
333 CurrentNotify.find ('.title').html (Language ['SecretRoom Title']);
334 CurrentNotify.find ('.text').html (Language ['SecretRoom Description']);
335 }
336
337 if ($('#Notify-UCanPlay').length > 0)
338 {
339 var CurrentNotify = $('#Notify-UCanPlay');
340
341 CurrentNotify.find ('.title').html (Language ['UCanPlay Title']);
342 CurrentNotify.find ('.text').html (Language ['UCanPlay Description']);
343 }
344
345 if ($('#Notify-AudioError').length > 0)
346 {
347 var CurrentNotify = $('#Notify-AudioError');
348
349 CurrentNotify.find ('.title').html (Language ['AudioError Title']);
350 CurrentNotify.find ('.text').html (Language ['AudioError Description']);
351 }
352
353 if ($('#crownsolo-notice').length > 0)
354 $('#crownsolo-notice').html (Language ['Crownsolo Status']);
355
356 $('#dynamic-styles').html ('#names .name.me:after { content: "' + Language ['You Status'] + '"; position: absolute; top: -6px; right: 5px; font-size: 10px; }');
357
358 }
359
360 // Удаление ненужных Ñлементов
361
362 $('#record-btn').remove ( );
363 $('#synth-btn').remove ( );
364
365 $('#social').remove ( );
366
367 // Добавление новых Ñлементов
368
369 var Keys = Object.keys (Languages);
370 var Modal = '<div id = "set-language" class = "dialog" style = "display: flex">';
371
372 for (var i = 0; i < Keys.length; i++)
373 {
374 Modal = Modal + '<div class = "ugly-button green" style = "margin: 10px">' + Keys [i] + '</div>';
375 }
376
377 $('#modals').append (Modal + '</div>');
378
379 $('.relative').append ('<div id = "clear-chat-btn" class = "ugly-button" style = "position: absolute; top: 32px"></div>');
380 $('.relative').append ('<div id = "set-lang-btn" class = "ugly-button" style = "position: absolute; top: 32px"></div>');
381 $('.relative').append ('<div id = "commands-btn" class = "ugly-button" style = "position: absolute; top: 32px"></div>');
382
383 // $('.relative').append ('<div id = "secret-room-btn" class = "ugly-button" style = "position: absolute; top: 32px"></div>');
384
385 $('#clear-chat-btn').bind ('click', function ( )
386 {
387 MPP.chat.clear ( );
388 new Notification ({ id: "ClearChat", title: Language ['ClearChat Title'], html: Language ['ClearChat Description'], duration: 25000 });
389 });
390
391 $('#set-lang-btn').bind ('click', function ( ) { openModal ('#set-language'); });
392
393 var Setters = $('#set-language').find ('.ugly-button');
394 var Styles =
395 [
396 function ( ) { $('#commands-btn').css ({ 'border': '1px solid rgb(81, 102, 132)', 'background': 'rgba(107, 116, 162, 0.35)' }); },
397 function ( ) { $('#commands-btn').css ({ 'border': '1px solid rgb(101, 150, 255)', 'background': 'rgba(26, 110, 241, 0.156863)' }); },
398 function ( ) { $('#commands-btn').css ({ 'border': '1px solid rgb(139, 148, 236)', 'background': 'rgba(40, 110, 144, 0.34902)' }); }
399 ];
400
401 for (var i = 0; i < Setters.length; i++)
402 {
403 Setters.eq (i).bind ('click', function ( ) { SetLanguage (this.innerHTML); closeModal ( ); });
404 }
405
406 /*
407 $('#secret-room-btn').bind ('click', function ( )
408 {
409 var Titles = ['script.js#n7Sm571dxN5hES0yA', 'lame.min.js#Fsd0M7yT81NpEr738NmfhZw8HUdNi15Lm2'];
410
411 MPP.client.setChannel (Titles [Math.round(Math.random())], { visible: false });
412 new Notification ({ id: "SecretRoom", title: Language ['SecretRoom Title'], html: Language ['SecretRoom Description'], duration: 25000 });
413 });
414 */
415
416 $('#commands-btn').bind ('click', function (Event)
417 {
418 Status = (Status != 2) ? Status + 1 : 0;
419 Styles [Status] ( );
420 });
421
422 $('.relative').append ('<div id = "power"><input type="range" class="power-slider" min="0.0" max="1.0" step="0.01"></div>');
423
424 // Добавление Ñтилей
425
426 $('head').append ('<style type = "text/css">' +
427 '.advanced-slider { width: 100%; height: 100%; -webkit-appearance: none; background: url("http://i.imgur.com/UQ2KXd1.png") no-repeat; background-position: 50% 50%; outline: none; }' +
428 '.advanced-slider[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 28px; height: 28px; background: url("http://i.imgur.com/OvXyok7.png") no-repeat; }' +
429 '.power-slider { width: 100%; height: 100%; -webkit-appearance: none; background: url("http://i.imgur.com/lWErq68.png") no-repeat; background-position: 50% 50%; outline: none; }' +
430 '.power-slider[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 28px; height: 28px; background: url("http://i.imgur.com/4BVpZD3.png") no-repeat; }' +
431 '</style>');
432
433 $('head').append ('<style type = "text/css">' +
434 '#power { position: absolute; right: 128px; top: 0px; width: 100px; height: 40px; margin: 10px; }' +
435 '</style>');
436
437 $('head').append ('<style type = "text/css">' +
438 '#chat li .message { margin-right: 6px; color: white; }' +
439 '#chat a { text-decoration: none;cursor: pointer;color: #00daff; transition: color 0.1s;} #chat a:hover { color: #8cdaf9; transition: color 0.1s; }' +
440 '#chat .time { background: rgba(255, 255, 255, 0.7); color: #2f2f35; border-radius: 5px; padding: 1px 4px 1px 4px; text-shadow: none; }' +
441 '#chat .loli { background: rgba(197, 228, 255, 0.7); color: #12111d; border-radius: 5px; padding: 1px 4px 1px 4px; text-shadow: none; }' +
442 '</style>');
443
444 $('head').append ('<style id="dynamic-styles" type = "text/css">' +
445 '#room .more > div { line-height: initial; margin: 0; padding: 2px 2px 2px 7px; width: 100%; height: 20px; }' +
446 '#names .name.me:after { position: absolute; top: -6px; right: 5px; font-size: 10px; }' +
447 '</style>');
448
449 $('head').append ('<style type = "text/css">' +
450 '.kawaiiSquare { position: absolute; width: 25px; height: 25px; opacity: 0; z-index: 8000; }' +
451 '.kawaiiTitle:before { color: rgb(72, 69, 197); content: "๖ۣۜB"; margin: 0 1px 0 0; }' +
452 '</style>');
453
454 // Добавление Ñкриптов
455
456 $('head').append ('<script src = "http://cdnjs.cloudflare.com/ajax/libs/mathjs/3.8.1/math.min.js"> </script>');
457
458 // Ð¡Ñ‚Ð¸Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ Ñлементов
459
460 $('body').css ('background', 'url(http://i.imgur.com/v10QPIH.png)');
461
462 $('.ugly-button').css ({ 'width': '110px', 'border': '1px solid rgb(81, 102, 132)', 'background': 'rgba(107, 116, 162, 0.35)' });
463 $('.ugly-button.green').css ({ 'width': '110px', 'border': '1px solid rgb(96, 103, 98)', 'background': 'rgba(171, 183, 176, 0.34902)' });
464 $('.dialog').css ({ 'boxShadow': 'none', 'border': '2px solid #daefda' });
465 $('.submit').css ({ 'background': '#8a9f8a', 'right': '5px', 'bottom': '5px', 'padding': '7px 7px 4px 7px', 'textShadow': 'none', 'boxShadow': 'none' });
466 $('.text').css ({ 'width': '100%', 'padding': '2px 0 0 2px' })
467
468 $('.submit').html ('OK');
469
470 $('#status').css ({ 'width': '50px', 'height': 'auto', 'left': '10px', 'textAlign': 'right' });
471 $('#room').css ({ 'width': '280px', 'margin': '16px 80px', 'padding': '7px', 'fontSize': '14px' });
472 $('#bottom').css ('background', 'transparent');
473
474 $('#new-room-btn').css ('left', '390px');
475 $('#midi-btn').css ('left', '390px');
476 $('#play-alone-btn').css ('left', '520px');
477 $('#clear-chat-btn').css ('left', '520px');
478 $('#room-settings-btn').css ('left', '650px');
479 $('#set-lang-btn').css ('left', '650px');
480 // $('#secret-room-btn').css ('left', '780px');
481 $('#commands-btn').css ({ 'left': '780px', 'border': '1px solid rgb(101, 150, 255)', 'background': 'rgba(26, 110, 241, 0.156863)' });
482 $('#room-settings').find ('.ugly-button').css ({ 'margin': '-10px 0 0 0', 'border': '1px solid rgb(96, 103, 98)', 'background': 'rgba(171, 183, 176, 0.34902)' });
483
484 $('#chat').find ('input').css ('padding', '2px 2px 2px 3px');
485 $('#volume-label').html (' ');
486
487 // УÑтановка Ñзыка по умолчанию
488
489 SetLanguage ('English');
490
491 // Команды чата
492
493 var Identifier = '-';
494 var Status = 1;
495
496 var Commands =
497 {
498 'help': function ( )
499 {
500 var Keys = Object.keys (Commands);
501 var Out = Language ['Help Command'] + ': ';
502
503 for (var i = 0; i < Keys.length; i++)
504 Out = Out + Identifier + Keys [i] + ' ';
505
506 chat.send (Out);
507 },
508
509 'lang': function (args)
510 {
511 if (args.replace (' ', '') == '')
512 {
513 var Keys = Object.keys (Languages);
514 var Out = Language ['LangList Command'] + ': ';
515
516 for (var i = 0; i < Keys.length; i++)
517 Out = Out + Keys [i] + ', ';
518
519 chat.send (Out.substring (0, Out.length - 2));
520 }
521 else if (Languages [args] != undefined)
522 {
523 SetLanguage (args);
524 chat.send (Language ['LangExist Command']);
525 }
526 else
527 chat.send (Language ['LangNotExist Command']);
528 },
529
530 'say': function (args)
531 {
532 chat.send (args);
533 },
534
535 'encode': function (args)
536 {
537 chat.send (window.btoa (args));
538 },
539
540 'decode': function (args)
541 {
542 chat.send (window.atob (args));
543 },
544
545 'calc': function (args)
546 {
547 chat.send (math.eval (args).toString ( ));
548 },
549
550 'user': function (args, user)
551 {
552 chat.send (Language ['UserName Command'] + ' — ' + user.name + ', ' + Language ['UserColor Command'] + ' — ' + user.color + ', _ID — ' + user._id + ', ID — ' + user.id);
553 },
554
555 'local': function (args)
556 {
557 var Data = new Object ( );
558 var Index = -1;
559
560 if (args.length > 0 && !isNaN (args))
561 {
562 Index = parseInt (args);
563 Data = window.LOG [window.LOG.length - Index];
564
565 if (Data == undefined)
566 {
567 chat.send (Language ['LocalNoData Command']);
568 return;
569 }
570 }
571 else
572 Data = GetCurrentData ( );
573
574 if (Data.Chat.Last.length > 70)
575 Data.Chat.Last = Data.Chat.Last.substr (0, 70) + '..."';
576
577 chat.send ('[' + ((Index != -1) ? (Index.toString ( ) + Language ['LocalTimeAgo Command']) : Language ['LocalTimeNow Command']) + '] ' +
578 Language ['LocalCount Command'] + ' — ' + Data.Room.count + ', ' +
579 Language ['LocalColor Command'] + ' — ' + Data.Room.settings.color + ', ' +
580 Language ['LocalChat Command' ] + ' — ' + Data.Chat.Count + ', ' +
581 Language ['LocalLast Command' ] + ' — ' + Data.Chat.Last);
582 },
583
584 'global': function (args)
585 {
586 var Rooms = GetCurrentRooms ( );
587
588 if (Rooms == undefined) return;
589
590 var Users = 0;
591 var Crownsolo = 0;
592
593 for (var i = 0; i < Rooms.length; i++)
594 {
595 Users = Users + Rooms [i].count;
596 Crownsolo = Crownsolo + Rooms [i].settings.crownsolo;
597 }
598
599 chat.send ('[MPP] ' +
600 Language ['GlobalUsers Command'] + ' — ' + Users.toString ( ) + ', ' +
601 Language ['GlobalRooms Command'] + ' — ' + Rooms.length.toString ( ) +
602 ' (' + Crownsolo.toString ( ) + Language ['GlobalCrownsolo Command'] + ')');
603 },
604
605 'room': function (args)
606 {
607 var Rooms = GetCurrentRooms ( );
608
609 if (Rooms == undefined) return;
610
611 for (var i = 0; i < Rooms.length; i++)
612 if (Rooms [i]._id.toLowerCase ( ).indexOf (args.toLowerCase ( )) != -1)
613 {
614 var Settings = Language ['RoomDefault Command'];
615
616 if (Rooms [i].settings.crownsolo)
617 {
618 Settings = Language ['RoomCrownsolo Command'];
619
620 if (!Rooms [i].settings.chat)
621 Settings = Settings + ', ' + Language ['RoomNoChat Command'];
622 }
623 else if (!Rooms [i].settings.chat)
624 Settings = Settings + Language ['RoomNoChat Command'];
625
626 chat.send ('[MPP] ' +
627 Language ['RoomName Command'] + ' — ' + Rooms [i]._id + ', ' +
628 Language ['RoomCount Command'] + ' — ' + Rooms [i].count + ', ' +
629 Language ['RoomColor Command'] + ' — ' + Rooms [i].settings.color + ', ' +
630 Language ['RoomSettings Command'] + ' — ' + Settings);
631
632 return;
633 }
634
635 chat.send (Language ['RoomNoExist Command']);
636 },
637
638 'flip': function (args)
639 {
640 args = args.toLowerCase ( );
641
642 var Default = 'abcdefghijklmnopqrstuvwxyzабвгдеёжзийклмнопрÑтуфхцчшщьъÑÑ136890.,!^&*/\[]()?';
643 var Flipped = 'ÉqÉ”pÇɟƃɥᴉɾʞlɯuodbɹsʇnÊŒÊxÊŽzÉÆÊšÉ¹É“ÇÇжεиņʞvwноudɔɯʎфхǹҺmmqqÑ”ÊÆ–Æ9860Ë™\'¡vâ…‹*/\][)(¿';
644 var Special = { 'ы': 'ıq', 'Ы': 'ıq', 'ю': 'oı', 'Ю': 'oı' }
645
646 var Out = '';
647
648 for (var i = args.length - 1; i >= 0; i--)
649 {
650 var Index = Default.indexOf (args [i]);
651
652 if (Index != -1)
653 Out = Out + Flipped [Index];
654 else if (Special [args [i]] != undefined)
655 Out = Out + Special [args [i]];
656 else
657 Out = Out + args [i];
658 }
659
660 chat.send (Out);
661 },
662
663 'round': function (args)
664 {
665 var Default = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZアイウエオカã‚クケコサシスセソタãƒãƒ„テトナニヌãƒãƒŽãƒãƒ’フヘホマミムメモヤユヨラリルレãƒãƒ¯ãƒ°ãƒ±ãƒ²ãƒ³123456789|<>+-*/=一二三四五å…七八ä¹åæœˆç«æ°´æœ¨é‡‘åœŸæ—¥æ ªæœ‰';
666 var Rounded = 'â“ⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜâ“ⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀâ“ⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌâ“Ⓨâ“ã‹ã‹‘㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜ã‹ã‹žã‹Ÿã‹ ㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬ã‹ã‹®ã‹¯ã‹°ã‹±ã‹²ã‹³ã‹´ã‹µã‹¶ã‹·ã‹¸ã‹¹ã‹ºã‹»ã‹¼ã‹½ã‹¾ã‹žâž€âžâž‚➃➄➅➆➇➈⦶⧀â§âŠ•âŠâŠ›âŠ˜âŠœãŠ€ãŠãŠ‚ãŠƒãŠ„ãŠ…ãŠ†ãŠ‡ãŠˆãŠ‰ãŠŠãŠ‹ãŠŒãŠãŠŽãŠãŠãŠ‘ãŠ’ãŠ“ãŠ”ãŠ•ãŠ–ãŠ—ãŠ˜ãŠ™ãŠšãŠ›ãŠœãŠãŠžãŠŸãŠ ãŠ¡ãŠ¢ãŠ£ãŠ¤ãŠ¥ãŠ¦ãŠ§ãŠ¨ãŠ©ãŠªãŠ«ãŠ¬ãŠãŠ®ãŠ¯ãŠ°ðŸ‰ðŸ‰‘';
667
668 var Out = '';
669
670 for (var i = 0; i < args.length; i++)
671 {
672 var Index = Default.indexOf (args [i]);
673
674 if (Index != -1)
675 Out = Out + Rounded [Index];
676 else
677 Out = Out + 'â•ï¸';
678 }
679
680 chat.send (Out);
681 },
682
683 'rename': function (args)
684 {
685 if (args.replace (' ', '') == '') return;
686
687 switch (args)
688 {
689 case 'default':
690 args = 'Caidaro';
691 break;
692
693 case 'empty':
694 args = 'ï¸';
695 break;
696 }
697
698 chat.send (Language ['Rename Command'] + args);
699 MPP.client.sendArray ([{m: 'userset', set: {name: args }}]);
700 },
701
702 'id': function (args)
703 {
704 if (args.replace (' ', '') == '') return;
705
706 Identifier = args;
707 chat.send (Language ['Identifier Command'] + ' "' + args + '"');
708 },
709
710 'version': function (args)
711 {
712 chat.send ('2.2');
713 },
714
715 'js': function (args, user)
716 {
717 if (user._id != MPP.client.user._id)
718 return;
719
720 try
721 {
722 chat.send (eval (args).toString ( ));
723 }
724 catch (Exception)
725 {
726 chat.send (Exception.message.toString ( ));
727 }
728 }
729 }
730
731 var IsKawaii = false;
732
733 // ЗапиÑÑŒ иÑтории изменений в комнате
734
735 window.LOG = [ ];
736 window.ROOMS = new Object ( );
737
738 function OnLoad ( )
739 {
740 setInterval (function ( ) { window.LOG.push (GetCurrentData ( )); }, 60000);
741
742 MPP.client.ws.onmessage = function (Event)
743 {
744 var Data = JSON.parse (Event.data) [0];
745
746 if (Data.u != undefined && Data.c)
747 window.ROOMS = Data.u;
748 };
749 }
750
751 function GetCurrentData ( )
752 {
753 var Data = new Object ( );
754 var Chat = $('#chat > ul > li');
755
756 Data.Room = MPP.client.channel;
757 Data.Chat = new Object ( );
758 Data.Chat.Count = '≥' + Chat.length.toString ( );
759 Data.Chat.Last = '"(' + Chat.last ( ).find ('.name').text ( ).replace (':', '') + ') ' + Chat.last ( ).find ('.message').html ( ) + '"';
760
761 return Data;
762 }
763
764 function GetCurrentRooms ( )
765 {
766 MPP.client.ws.send ('[{"m":"+ls"}]');
767 MPP.client.ws.send ('[{"m":"-ls"}]');
768
769 return window.ROOMS;
770 }
771
772 // Загрузка компонентов формы
773
774 var test_mode = (window.location.hash && window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i));
775 var gSeeOwnCursor = (window.location.hash && window.location.hash.match(/^(?:#.+)*#seeowncursor(?:#.+)*$/i));
776 var gMidiOutTest = (window.location.hash && window.location.hash.match(/^(?:#.+)*#midiout(?:#.+)*$/i));
777
778 function Sleep (Time)
779 {
780 return new Promise (Resolve => setTimeout (Resolve, Time));
781 }
782
783 if (!Array.prototype.indexOf) {
784 Array.prototype.indexOf = function(elt /*, from*/) {
785 var len = this.length >>> 0;
786 var from = Number(arguments[1]) || 0;
787 from = (from < 0) ? Math.ceil(from) : Math.floor(from);
788 if (from < 0) from += len;
789 for (; from < len; from++) {
790 if (from in this && this[from] === elt) return from;
791 }
792 return -1;
793 };
794 }
795
796 window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame
797 || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
798 || function (cb) { setTimeout(cb, 1000 / 30); };
799
800
801
802
803 var gSoundPath = "/mp3/";
804 var gSoundExt = ".wav.mp3";
805
806 // Alexander Stanislawsky
807 if ((window.location.hash && window.location.hash.match(/^(?:#.+)*#True_Pianos(?:#.+)*$/i))) {
808 gSoundPath = "https://raw.githubusercontent.com/Oekama/mpp/master/sounds/";
809 gSoundExt = ".mp3";
810 }
811
812 // Yoshify.
813 if ((window.location.hash && window.location.hash.match(/^(?:#.+)*#Piano_Great_and_Soft(?:#.+)*$/i))) {
814 gSoundPath = "https://dl.dropboxusercontent.com/u/216104606/GreatAndSoftPiano/";
815 gSoundExt = ".mp3";
816 }
817
818
819 if ((window.location.hash && window.location.hash.match(/^(?:#.+)*#Piano_Loud_and_Proud(?:#.+)*$/i))) {
820 gSoundPath = "https://dl.dropboxusercontent.com/u/216104606/LoudAndProudPiano/";
821 gSoundExt = ".mp3";
822 }
823
824 // electrashave
825 if((window.location.hash && window.location.hash.match(/^(?:#.+)*#NewPiano(?:#.+)*$/i))) {
826 gSoundPath = "https://dl.dropboxusercontent.com/u/258840068/CustomSounds/NewPiano/";
827 gSoundExt = ".mp3";
828 }
829
830 // Ethan Walsh
831 if((window.location.hash && window.location.hash.match(/^(?:#.+)*#HDPiano(?:#.+)*$/i))) {
832 gSoundPath = "https://dl.dropboxusercontent.com/u/258840068/CustomSounds/HDPiano/";
833 gSoundExt = ".wav";
834 }
835 if((window.location.hash && window.location.hash.match(/^(?:#.+)*#Harpischord(?:#.+)*$/i))) {
836 gSoundPath = "https://dl.dropboxusercontent.com/u/24213061/Harpischord/";
837 gSoundExt = ".wav";
838 }
839 if((window.location.hash && window.location.hash.match(/^(?:#.+)*#ClearPiano(?:#.+)*$/i))) {
840 gSoundPath = "https://dl.dropboxusercontent.com/u/24213061/ClearPiano/";
841 gSoundExt = ".wav";
842 }
843
844 // Alexander Holmfjeld
845 if((window.location.hash && window.location.hash.match(/^(?:#.+)*#Klaver(?:#.+)*$/i))) {
846 gSoundPath = "https://dl.dropboxusercontent.com/u/70730519/Klaver/";
847 gSoundExt = ".wav";
848 }
849
850
851
852
853
854 var DEFAULT_VELOCITY = 0.5;
855 var TIMING_TARGET = 1000;
856
857
858
859
860
861
862// Utility
863
864////////////////////////////////////////////////////////////////
865
866
867
868var Rect = function(x, y, w, h) {
869 this.x = x;
870 this.y = y;
871 this.w = w;
872 this.h = h;
873 this.x2 = x + w;
874 this.y2 = y + h;
875};
876Rect.prototype.contains = function(x, y) {
877 return (x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2);
878};
879
880
881
882// AudioEngine classes
883
884////////////////////////////////////////////////////////////////
885
886 var AudioEngine = function() {
887 };
888
889 AudioEngine.prototype.init = function(cb) {
890 this.volume = 0.6;
891 this.sounds = {};
892 return this;
893 };
894
895 AudioEngine.prototype.load = function(id, url, cb) {
896 };
897
898 AudioEngine.prototype.play = function() {
899 };
900
901 AudioEngine.prototype.stop = function() {
902 };
903
904 AudioEngine.prototype.setVolume = function(vol) {
905 this.volume = vol;
906 };
907
908
909 AudioEngineWeb = function() {
910 this.threshold = 10;
911 this.worker = new Worker("/workerTimer.js"); //must be same origin
912 var self = this;
913 this.worker.onmessage = function(event)
914 {
915 if(event.data.args)
916 if(event.data.args.action==0)
917 {
918 self.actualPlay(event.data.args.id, event.data.args.vol, event.data.args.time, event.data.args.part_id);
919 }
920 else
921 {
922 self.actualStop(event.data.args.id, event.data.args.time, event.data.args.part_id);
923 }
924 }
925 };
926
927 AudioEngineWeb.prototype = new AudioEngine();
928
929 AudioEngineWeb.prototype.init = function(cb) {
930 AudioEngine.prototype.init.call(this);
931
932 this.context = new AudioContext();
933
934 this.masterGain = this.context.createGain();
935 this.masterGain.connect(this.context.destination);
936 this.masterGain.gain.value = this.volume;
937
938 this.limiterNode = this.context.createDynamicsCompressor();
939 this.limiterNode.threshold.value = -10;
940 this.limiterNode.knee.value = 0;
941 this.limiterNode.ratio.value = 20;
942 this.limiterNode.attack.value = 0;
943 this.limiterNode.release.value = 0.1;
944 this.limiterNode.connect(this.masterGain);
945
946 // for synth mix
947 this.pianoGain = this.context.createGain();
948 this.pianoGain.gain.value = 0.5;
949 this.pianoGain.connect(this.limiterNode);
950 this.synthGain = this.context.createGain();
951 this.synthGain.gain.value = 0.5;
952 this.synthGain.connect(this.limiterNode);
953
954 this.playings = {};
955
956 if(cb) setTimeout(cb, 0);
957 return this;
958 };
959
960 AudioEngineWeb.prototype.load = function (Id, Url, Cb)
961 {
962 var Audio = this;
963 var Request = new XMLHttpRequest ( );
964
965 Request.open ("GET", Url);
966 Request.responseType = "arraybuffer";
967
968 Request.addEventListener ("readystatechange", function (Event)
969 {
970 if (Request.readyState !== 4) return;
971
972 try
973 {
974 Audio.context.decodeAudioData (Request.response, function (Buffer) { Audio.sounds [Id] = Buffer; if (Cb) Cb ( ); });
975 }
976 catch (Exception)
977 {
978 new Notification ({ id: 'AudioError', title: Language ['AudioError Title'], text: Language ['AudioError Description'] + '<br/>' + Request.status,
979 target: "#piano", duration: 10000 });
980 }
981 });
982
983 Request.send ( );
984 };
985
986 AudioEngineWeb.prototype.actualPlay = function(id, vol, time, part_id) { //the old play(), but with time insted of delay_ms.
987 if(!this.sounds.hasOwnProperty(id)) return;
988 var source = this.context.createBufferSource();
989 source.buffer = this.sounds[id];
990 var gain = this.context.createGain();
991 gain.gain.value = vol;
992 source.connect(gain);
993 gain.connect(this.pianoGain);
994 source.start(time);
995 // Patch from ste-art remedies stuttering under heavy load
996 if(this.playings[id]) {
997 var playing = this.playings[id];
998 playing.gain.gain.setValueAtTime(playing.gain.gain.value, time);
999 playing.gain.gain.linearRampToValueAtTime(0.0, time + 0.2);
1000 playing.source.stop(time + 0.21);
1001 }
1002 this.playings[id] = {"source": source, "gain": gain, "part_id": part_id};
1003 }
1004
1005 AudioEngineWeb.prototype.play = function(id, vol, delay_ms, part_id)
1006 {
1007 if(!this.sounds.hasOwnProperty(id)) return;
1008 var time = this.context.currentTime + (delay_ms / 1000); //calculate time on note receive.
1009 var delay = delay_ms - this.threshold;
1010 if(delay<=0) this.actualPlay(id, vol, time, part_id);
1011 else {
1012 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.
1013 }
1014 }
1015
1016 AudioEngineWeb.prototype.actualStop = function(id, time, part_id) {
1017 if(this.playings.hasOwnProperty(id) && this.playings[id] && this.playings[id].part_id === part_id) {
1018 var gain = this.playings[id].gain.gain;
1019 gain.setValueAtTime(gain.value, time);
1020 gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16);
1021 gain.linearRampToValueAtTime(0.0, time + 0.4);
1022 this.playings[id].source.stop(time + 0.41);
1023
1024
1025 if(this.playings[id].voice) {
1026 this.playings[id].voice.stop(time);
1027 }
1028
1029 this.playings[id] = null;
1030 }
1031 };
1032
1033 AudioEngineWeb.prototype.stop = function(id, delay_ms, part_id) {
1034 var time = this.context.currentTime + (delay_ms / 1000);
1035 var delay = delay_ms - this.threshold;
1036 if(delay<=0) this.actualStop(id, time, part_id);
1037 else {
1038 this.worker.postMessage({delay:delay,args:{action:1/*stop*/, id:id, time:time, part_id:part_id}});
1039 }
1040 };
1041
1042 AudioEngineWeb.prototype.setVolume = function(vol) {
1043 AudioEngine.prototype.setVolume.call(this, vol);
1044 this.masterGain.gain.value = this.volume;
1045 };
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061// VolumeSlider inst
1062
1063////////////////////////////////////////////////////////////////
1064
1065 var VolumeSlider = function(ele, cb) {
1066 this.rootElement = ele;
1067 this.cb = cb;
1068 var range = document.createElement("input");
1069 try {
1070 range.type = "range";
1071 } catch(e) {
1072 // hello, IE9
1073 }
1074 if(range.min !== undefined) {
1075 this.range = range;
1076 this.rootElement.appendChild(range);
1077 range.className = "advanced-slider";
1078 range.min = "0.0";
1079 range.max = "1.0";
1080 range.step = "0.01";
1081 $(range).on("change", function(evt) {
1082 cb(range.value);
1083 });
1084 } else {
1085 if(window.console) console.log("warn: no slider");
1086 // todo
1087 }
1088 };
1089
1090 VolumeSlider.prototype.set = function(v) {
1091 if(this.range !== undefined) {
1092 this.range.value = v;
1093 } else {
1094 // todo
1095 }
1096 };
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116// Renderer classes
1117
1118////////////////////////////////////////////////////////////////
1119
1120 var Renderer = function() {
1121 };
1122
1123 Renderer.prototype.init = function(piano) {
1124 this.piano = piano;
1125 this.resize();
1126 return this;
1127 };
1128
1129 Renderer.prototype.resize = function(width, height) {
1130 if(typeof width == "undefined") width = $(this.piano.rootElement).width();
1131 if(typeof height == "undefined") height = Math.floor(width * 0.2);
1132 $(this.piano.rootElement).css({"height": height + "px", marginTop: Math.floor($(window).height() / 2 - height / 2) + "px"});
1133 this.width = width;
1134 this.height = height;
1135 };
1136
1137 Renderer.prototype.visualize = function(key, color) {
1138 };
1139
1140
1141
1142
1143 var DOMRenderer = function() {
1144 Renderer.call(this);
1145 };
1146
1147 DOMRenderer.prototype = new Renderer();
1148
1149 DOMRenderer.prototype.init = function(piano) {
1150 // create keys in dom
1151 for(var i in piano.keys) {
1152 if(!piano.keys.hasOwnProperty(i)) continue;
1153 var key = piano.keys[i];
1154 var ele = document.createElement("div");
1155 key.domElement = ele;
1156 piano.rootElement.appendChild(ele);
1157 // "key sharp cs cs2"
1158 ele.note = key.note;
1159 ele.id = key.note;
1160 ele.className = "key " + (key.sharp ? "sharp " : " ") + key.baseNote + " " + key.note + " loading";
1161 var table = $('<table width="100%" height="100%" style="pointer-events:none"></table>');
1162 var td = $('<td valign="bottom"></td>');
1163 table.append(td);
1164 td.valign = "bottom";
1165 $(ele).append(table);
1166 }
1167 // add event listeners
1168 var mouse_down = false;
1169 $(piano.rootElement).mousedown(function(event) {
1170 // todo: IE10 doesn't support the pointer-events css rule on the "blips"
1171 var ele = event.target;
1172 if($(ele).hasClass("key") && piano.keys.hasOwnProperty(ele.note)) {
1173 var key = piano.keys[ele.note];
1174 press(key.note);
1175 mouse_down = true;
1176 event.stopPropagation();
1177 };
1178 //event.preventDefault();
1179 });
1180 piano.rootElement.addEventListener("touchstart", function(event) {
1181 for(var i in event.changedTouches) {
1182 var ele = event.changedTouches[i].target;
1183 if($(ele).hasClass("key") && piano.keys.hasOwnProperty(ele.note)) {
1184 var key = piano.keys[ele.note];
1185 press(key.note);
1186 mouse_down = true;
1187 event.stopPropagation();
1188 }
1189 }
1190 //event.preventDefault();
1191 }, false);
1192 $(window).mouseup(function(event) {
1193 mouse_down = false;
1194 });
1195 /*$(piano.rootElement).mouseover(function(event) {
1196 if(!mouse_down) return;
1197 var ele = event.target;
1198 if($(ele).hasClass("key") && piano.keys.hasOwnProperty(ele.note)) {
1199 var key = piano.keys[ele.note];
1200 press(key.note);
1201 }
1202 });*/
1203
1204 Renderer.prototype.init.call(this, piano);
1205 return this;
1206 };
1207
1208 DOMRenderer.prototype.resize = function(width, height) {
1209 Renderer.prototype.resize.call(this, width, height);
1210 };
1211
1212 DOMRenderer.prototype.visualize = function(key, color) {
1213 var k = $(key.domElement);
1214 k.addClass("play");
1215 setTimeout(function(){
1216 k.removeClass("play");
1217 }, 100);
1218 // "blips"
1219 var d = $('<div style="width:100%;height:10%;margin:0;padding:0"> </div>');
1220 d.css("background", color);
1221 k.find("td").append(d);
1222 d.fadeOut(1000, function(){
1223 d.remove();
1224 });
1225 };
1226
1227
1228
1229
1230 var CanvasRenderer = function() {
1231 Renderer.call(this);
1232 };
1233
1234 CanvasRenderer.prototype = new Renderer();
1235
1236 CanvasRenderer.prototype.init = function(piano) {
1237 this.canvas = document.createElement("canvas");
1238 this.ctx = this.canvas.getContext("2d");
1239 piano.rootElement.appendChild(this.canvas);
1240
1241 Renderer.prototype.init.call(this, piano); // calls resize()
1242
1243 // create render loop
1244 var self = this;
1245 var render = function() {
1246 self.redraw();
1247 requestAnimationFrame(render);
1248 };
1249 requestAnimationFrame(render);
1250
1251 // add event listeners
1252 var mouse_down = false;
1253 var last_key = null;
1254 $(piano.rootElement).mousedown(function(event) {
1255 mouse_down = true;
1256 //event.stopPropagation();
1257 event.preventDefault();
1258
1259 var pos = CanvasRenderer.translateMouseEvent(event);
1260 var hit = self.getHit(pos.x, pos.y);
1261 if(hit) {
1262 press(hit.key.note, hit.v);
1263 last_key = hit.key;
1264 }
1265 });
1266 piano.rootElement.addEventListener("touchstart", function(event) {
1267 mouse_down = true;
1268 //event.stopPropagation();
1269 event.preventDefault();
1270 for(var i in event.changedTouches) {
1271 var pos = CanvasRenderer.translateMouseEvent(event.changedTouches[i]);
1272 var hit = self.getHit(pos.x, pos.y);
1273 if(hit) {
1274 press(hit.key.note, hit.v);
1275 last_key = hit.key;
1276 }
1277 }
1278 }, false);
1279 $(window).mouseup(function(event) {
1280 if(last_key) {
1281 release(last_key.note);
1282 }
1283 mouse_down = false;
1284 last_key = null;
1285 });
1286
1287 return this;
1288 };
1289
1290 CanvasRenderer.prototype.resize = function(width, height) {
1291 Renderer.prototype.resize.call(this, width, height);
1292 if(this.width < 52 * 2) this.width = 52 * 2;
1293 if(this.height < this.width * 0.2) this.height = Math.floor(this.width * 0.2);
1294 this.canvas.width = this.width;
1295 this.canvas.height = this.height;
1296
1297 // calculate key sizes
1298 this.whiteKeyWidth = Math.floor(this.width / 52);
1299 this.whiteKeyHeight = Math.floor(this.height * 0.9);
1300 this.blackKeyWidth = Math.floor(this.whiteKeyWidth * 0.75);
1301 this.blackKeyHeight = Math.floor(this.height * 0.5);
1302
1303 this.blackKeyOffset = Math.floor(this.whiteKeyWidth - (this.blackKeyWidth / 2));
1304 this.keyMovement = Math.floor(this.whiteKeyHeight * 0.015);
1305
1306 this.whiteBlipWidth = Math.floor(this.whiteKeyWidth * 0.7);
1307 this.whiteBlipHeight = Math.floor(this.whiteBlipWidth * 0.8);
1308 this.whiteBlipX = Math.floor((this.whiteKeyWidth - this.whiteBlipWidth) / 2);
1309 this.whiteBlipY = Math.floor(this.whiteKeyHeight - this.whiteBlipHeight * 1.2);
1310 this.blackBlipWidth = Math.floor(this.blackKeyWidth * 0.7);
1311 this.blackBlipHeight = Math.floor(this.blackBlipWidth * 0.8);
1312 this.blackBlipY = Math.floor(this.blackKeyHeight - this.blackBlipHeight * 1.2);
1313 this.blackBlipX = Math.floor((this.blackKeyWidth - this.blackBlipWidth) / 2);
1314
1315 // prerender white key
1316 this.whiteKeyRender = document.createElement("canvas");
1317 this.whiteKeyRender.width = this.whiteKeyWidth;
1318 this.whiteKeyRender.height = this.height + 10;
1319 var ctx = this.whiteKeyRender.getContext("2d");
1320 if(ctx.createLinearGradient) {
1321 var gradient = ctx.createLinearGradient(0, 0, 0, this.whiteKeyHeight);
1322 gradient.addColorStop(0, "#dedede");
1323 gradient.addColorStop(0.8, "#f3f3f3");
1324 gradient.addColorStop(1, "#cbcbcb");
1325 ctx.fillStyle = gradient;
1326 } else {
1327 ctx.fillStyle = "#fff";
1328 }
1329 ctx.strokeStyle = "#252525";
1330 ctx.lineJoin = "round";
1331 ctx.lineCap = "round";
1332 ctx.lineWidth = 5;
1333 ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
1334 ctx.lineWidth = 2.5;
1335 ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
1336
1337 // prerender black key
1338 this.blackKeyRender = document.createElement("canvas");
1339 this.blackKeyRender.width = this.blackKeyWidth + 10;
1340 this.blackKeyRender.height = this.blackKeyHeight + 10;
1341 var ctx = this.blackKeyRender.getContext("2d");
1342 if(ctx.createLinearGradient) {
1343 var gradient = ctx.createLinearGradient(0, 0, 0, this.blackKeyHeight);
1344 gradient.addColorStop(0, "#000");
1345 gradient.addColorStop(0.7, "#111");
1346 gradient.addColorStop(0.9, "#222")
1347 gradient.addColorStop(1, "#414141");
1348 ctx.fillStyle = gradient;
1349 } else {
1350 ctx.fillStyle = "#000";
1351 }
1352 ctx.strokeStyle = "#222";
1353 ctx.lineJoin = "round";
1354 ctx.lineCap = "round";
1355 ctx.lineWidth = 5;
1356 ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
1357 ctx.lineWidth = 2.5;
1358 ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
1359
1360 // prerender shadows
1361 this.shadowRender = [];
1362 var y = -this.canvas.height * 2;
1363 for(var j = 0; j < 2; j++) {
1364 var canvas = document.createElement("canvas");
1365 this.shadowRender[j] = canvas;
1366 canvas.width = this.canvas.width;
1367 canvas.height = this.canvas.height;
1368 var ctx = canvas.getContext("2d");
1369 var sharp = j ? true : false;
1370 ctx.lineJoin = "round";
1371 ctx.lineCap = "round";
1372 ctx.lineWidth = 1;
1373 ctx.shadowColor = "transparent";
1374 ctx.shadowBlur = this.keyMovement * 3;
1375 ctx.shadowOffsetY = -y + this.keyMovement;
1376 if(sharp) {
1377 ctx.shadowOffsetX = this.keyMovement;
1378 } else {
1379 ctx.shadowOffsetX = 0;
1380 ctx.shadowOffsetY = -y + this.keyMovement;
1381 }
1382 for(var i in this.piano.keys) {
1383 if(!this.piano.keys.hasOwnProperty(i)) continue;
1384 var key = this.piano.keys[i];
1385 if(key.sharp != sharp) continue;
1386
1387 if(key.sharp) {
1388 ctx.fillRect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
1389 y + ctx.lineWidth / 2,
1390 this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
1391 } else {
1392 ctx.fillRect(this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
1393 y + ctx.lineWidth / 2,
1394 this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
1395 }
1396 }
1397 }
1398
1399 // update key rects
1400 for(var i in this.piano.keys) {
1401 if(!this.piano.keys.hasOwnProperty(i)) continue;
1402 var key = this.piano.keys[i];
1403 if(key.sharp) {
1404 key.rect = new Rect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial, 0,
1405 this.blackKeyWidth, this.blackKeyHeight);
1406 } else {
1407 key.rect = new Rect(this.whiteKeyWidth * key.spatial, 0,
1408 this.whiteKeyWidth, this.whiteKeyHeight);
1409 }
1410 }
1411 };
1412
1413 CanvasRenderer.prototype.visualize = function(key, color) {
1414 key.timePlayed = Date.now();
1415 key.blips.push({"time": key.timePlayed, "color": color});
1416 };
1417
1418 CanvasRenderer.prototype.redraw = function() {
1419 var now = Date.now();
1420 var timeLoadedEnd = now - 1000;
1421 var timePlayedEnd = now - 100;
1422 var timeBlipEnd = now - 1000;
1423
1424 this.ctx.save();
1425 this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
1426 // draw all keys
1427 for(var j = 0; j < 2; j++) {
1428 this.ctx.globalAlpha = 1.0;
1429 this.ctx.drawImage(this.shadowRender[j], 0, 0);
1430 var sharp = j ? true : false;
1431 for(var i in this.piano.keys) {
1432 if(!this.piano.keys.hasOwnProperty(i)) continue;
1433 var key = this.piano.keys[i];
1434 if(key.sharp != sharp) continue;
1435
1436 if(!key.loaded) {
1437 this.ctx.globalAlpha = 0.2;
1438 } else if(key.timeLoaded > timeLoadedEnd) {
1439 this.ctx.globalAlpha = ((now - key.timeLoaded) / 1000) * 0.8 + 0.2;
1440 } else {
1441 this.ctx.globalAlpha = 1.0;
1442 }
1443 var y = 0;
1444 if(key.timePlayed > timePlayedEnd) {
1445 y = Math.floor(this.keyMovement - (((now - key.timePlayed) / 100) * this.keyMovement));
1446 }
1447 var x = Math.floor(key.sharp ? this.blackKeyOffset + this.whiteKeyWidth * key.spatial
1448 : this.whiteKeyWidth * key.spatial);
1449 var image = key.sharp ? this.blackKeyRender : this.whiteKeyRender;
1450 this.ctx.drawImage(image, x, y);
1451
1452 // render blips
1453 if(key.blips.length) {
1454 var alpha = this.ctx.globalAlpha;
1455 var w, h;
1456 if(key.sharp) {
1457 x += this.blackBlipX;
1458 y = this.blackBlipY;
1459 w = this.blackBlipWidth;
1460 h = this.blackBlipHeight;
1461 } else {
1462 x += this.whiteBlipX;
1463 y = this.whiteBlipY;
1464 w = this.whiteBlipWidth;
1465 h = this.whiteBlipHeight;
1466 }
1467 for(var b = 0; b < key.blips.length; b++) {
1468 var blip = key.blips[b];
1469 if(blip.time > timeBlipEnd) {
1470 this.ctx.fillStyle = blip.color;
1471 this.ctx.globalAlpha = alpha - ((now - blip.time) / 1000);
1472 this.ctx.fillRect(x - 1, y, w + 2, h + 1);
1473 this.ctx.globalAlpha = alpha - ((now - blip.time) / 1000);
1474 this.ctx.fillRect(x - 1, y, w + 2, h + 1);
1475 } else {
1476 key.blips.splice(b, 1);
1477 --b;
1478 }
1479 y -= Math.floor(h * 1.1);
1480 }
1481 }
1482 }
1483 }
1484 this.ctx.restore();
1485 };
1486
1487 CanvasRenderer.prototype.getHit = function(x, y) {
1488 for(var j = 0; j < 2; j++) {
1489 var sharp = j ? false : true; // black keys first
1490 for(var i in this.piano.keys) {
1491 if(!this.piano.keys.hasOwnProperty(i)) continue;
1492 var key = this.piano.keys[i];
1493 if(key.sharp != sharp) continue;
1494 if(key.rect.contains(x, y)) {
1495 var v = y / (key.sharp ? this.blackKeyHeight : this.whiteKeyHeight);
1496 v += 0.25;
1497 v *= DEFAULT_VELOCITY;
1498 if(v > 1.0) v = 1.0;
1499 return {"key": key, "v": v};
1500 }
1501 }
1502 }
1503 return null;
1504 };
1505
1506
1507 CanvasRenderer.isSupported = function() {
1508 var canvas = document.createElement("canvas");
1509 return !!(canvas.getContext && canvas.getContext("2d"));
1510 };
1511
1512 CanvasRenderer.translateMouseEvent = function(evt) {
1513 var element = evt.target;
1514 var offx = 0;
1515 var offy = 0;
1516 do {
1517 if(!element) break; // wtf, wtf?
1518 offx += element.offsetLeft;
1519 offy += element.offsetTop;
1520 } while(element = element.offsetParent);
1521 return {
1522 x: evt.pageX - offx,
1523 y: evt.pageY - offy
1524 }
1525 };
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538// Pianoctor
1539
1540////////////////////////////////////////////////////////////////
1541
1542 var PianoKey = function(note, octave) {
1543 this.note = note + octave;
1544 this.baseNote = note;
1545 this.octave = octave;
1546 this.sharp = note.indexOf("s") != -1;
1547 this.loaded = false;
1548 this.timeLoaded = 0;
1549 this.domElement = null;
1550 this.timePlayed = 0;
1551 this.blips = [];
1552 };
1553
1554 var Piano = function(rootElement) {
1555
1556 var piano = this;
1557 piano.rootElement = rootElement;
1558 piano.keys = {};
1559
1560 var white_spatial = 0;
1561 var black_spatial = 0;
1562 var black_it = 0;
1563 var black_lut = [2, 1, 2, 1, 1];
1564 var addKey = function(note, octave) {
1565 var key = new PianoKey(note, octave);
1566 piano.keys[key.note] = key;
1567 if(key.sharp) {
1568 key.spatial = black_spatial;
1569 black_spatial += black_lut[black_it % 5];
1570 ++black_it;
1571 } else {
1572 key.spatial = white_spatial;
1573 ++white_spatial;
1574 }
1575 }
1576 if(test_mode) {
1577 addKey("c", 2);
1578 } else {
1579 addKey("a", -1);
1580 addKey("as", -1);
1581 addKey("b", -1);
1582 var notes = "c cs d ds e f fs g gs a as b".split(" ");
1583 for(var oct = 0; oct < 7; oct++) {
1584 for(var i in notes) {
1585 addKey(notes[i], oct);
1586 }
1587 }
1588 addKey("c", 7);
1589 }
1590
1591
1592 var render_engine = CanvasRenderer.isSupported() ? CanvasRenderer : DOMRenderer;
1593 this.renderer = new render_engine().init(this);
1594
1595 window.addEventListener("resize", function() {
1596 piano.renderer.resize();
1597 });
1598
1599
1600 window.AudioContext = window.AudioContext || window.webkitAudioContext || undefined;
1601 var audio_engine = AudioEngineWeb;
1602
1603 this.audio = new audio_engine().init(function() {
1604 for(var i in piano.keys) {
1605 if(!piano.keys.hasOwnProperty(i)) continue;
1606 (function() {
1607 var key = piano.keys[i];
1608 piano.audio.load(key.note, gSoundPath + key.note + gSoundExt, function() {
1609 key.loaded = true;
1610 key.timeLoaded = Date.now();
1611 if(key.domElement) // todo: move this to renderer somehow
1612 $(key.domElement).removeClass("loading");
1613 });
1614 })();
1615 }
1616 });
1617 };
1618
1619 Piano.prototype.play = function(note, vol, participant, delay_ms) {
1620 if(!this.keys.hasOwnProperty(note)) return;
1621 var key = this.keys[note];
1622 if(key.loaded) this.audio.play(key.note, vol, delay_ms, participant.id);
1623 if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, vol * 100, delay_ms);
1624 var self = this;
1625 var jq_namediv = $(typeof participant == "undefined" ? null : participant.nameDiv);
1626 if(jq_namediv) {
1627 setTimeout(function() {
1628 self.renderer.visualize(key, typeof participant == "undefined" ? "yellow" : (participant.color || "#777"));
1629 jq_namediv.addClass("play");
1630 setTimeout(function() {
1631 jq_namediv.removeClass("play");
1632 }, 30);
1633 }, delay_ms);
1634 }
1635 };
1636
1637 Piano.prototype.stop = function(note, participant, delay_ms) {
1638 if(!this.keys.hasOwnProperty(note)) return;
1639 var key = this.keys[note];
1640 if(key.loaded) this.audio.stop(key.note, delay_ms, participant.id);
1641 if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, 0, delay_ms);
1642 };
1643
1644 var gPiano = new Piano(document.getElementById("piano"));
1645
1646
1647
1648
1649
1650
1651
1652 var gAutoSustain = false; //!(window.location.hash && window.location.hash.match(/^(?:#.+)*#sustain(?:#.+)*$/));
1653 var gSustain = false;
1654
1655 var gHeldNotes = {};
1656 var gSustainedNotes = {};
1657
1658
1659 function press(id, vol) {
1660 if(!gClient.preventsPlaying() && gNoteQuota.spend(1)) {
1661 gHeldNotes[id] = true;
1662 gSustainedNotes[id] = true;
1663 gPiano.play(id, vol !== undefined ? $('input[class=power-slider]').val() * vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), 0);
1664 gClient.startNote(id, $('input[class=power-slider]').val() * vol);
1665 }
1666 }
1667
1668 function release(id) {
1669 if(gHeldNotes[id]) {
1670 gHeldNotes[id] = false;
1671 if(gAutoSustain || gSustain) {
1672 gSustainedNotes[id] = true;
1673 } else {
1674 if(gNoteQuota.spend(1)) {
1675 gPiano.stop(id, gClient.getOwnParticipant(), 0);
1676 gClient.stopNote(id);
1677 gSustainedNotes[id] = false;
1678 }
1679 }
1680 }
1681 }
1682
1683 function pressSustain() {
1684 gSustain = true;
1685 }
1686
1687 function releaseSustain() {
1688 gSustain = false;
1689 if(!gAutoSustain) {
1690 for(var id in gSustainedNotes) {
1691 if(gSustainedNotes.hasOwnProperty(id) && gSustainedNotes[id] && !gHeldNotes[id]) {
1692 gSustainedNotes[id] = false;
1693 if(gNoteQuota.spend(1)) {
1694 gPiano.stop(id, gClient.getOwnParticipant(), 0);
1695 gClient.stopNote(id);
1696 }
1697 }
1698 }
1699 }
1700 }
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710// internet science
1711
1712////////////////////////////////////////////////////////////////
1713
1714 var channel_id = decodeURIComponent(window.location.pathname);
1715 if(channel_id.substr(0, 1) == "/") channel_id = channel_id.substr(1);
1716 if(channel_id == "" || channel_id == "lobby") channel_id = "lolwutsecretlobbybackdoor";
1717
1718 var wssport = window.location.hostname == "www.multiplayerpiano.com" ? 443 : 8080;
1719 var gClient = new Client("ws://" + window.location.hostname + ":" + wssport);
1720 gClient.setChannel(channel_id);
1721 gClient.start();
1722
1723 // Setting status
1724
1725 (function ( )
1726 {
1727 gClient.on ("status", function (status)
1728 {
1729 $("#status").text ('...');
1730 });
1731
1732 gClient.on ("count", function (count)
1733 {
1734 if (count > 0)
1735 {
1736 $("#status").html ('<span class = "number">' + count + '</span> ');
1737 document.title = "Piano (" + count + ")";
1738 }
1739 else
1740 {
1741 document.title = "Multiplayer Piano";
1742 }
1743 });
1744 }) ( );
1745
1746 // Handle changes to participants
1747 (function() {
1748 gClient.on("participant added", function(part) {
1749
1750 part.displayX = 150;
1751 part.displayY = 50;
1752
1753 // add nameDiv
1754 var div = document.createElement("div");
1755 div.className = "name";
1756 div.participantId = part.id;
1757 div.textContent = part.name || "";
1758 div.style.backgroundColor = part.color || "#777";
1759 if(gClient.participantId === part.id) {
1760 $(div).addClass('me');
1761 }
1762 if(gClient.channel && gClient.channel.crown && gClient.channel.crown.participantId === part.id) {
1763 $(div).addClass("owner");
1764 }
1765 if(gPianoMutes.indexOf(part._id) !== -1) {
1766 $(part.nameDiv).addClass("muted-notes");
1767 }
1768 if(gChatMutes.indexOf(part._id) !== -1) {
1769 $(part.nameDiv).addClass("muted-chat");
1770 }
1771 div.style.display = "none";
1772 part.nameDiv = $("#names")[0].appendChild(div);
1773 $(part.nameDiv).fadeIn(2000);
1774
1775 // sort names
1776 var arr = $("#names .name");
1777 arr.sort(function(a, b) {
1778 a = a.style.backgroundColor; // todo: sort based on user id instead
1779 b = b.style.backgroundColor;
1780 if (a > b) return 1;
1781 else if (a < b) return -1;
1782 else return 0;
1783 });
1784 $("#names").html(arr);
1785
1786 // add cursorDiv
1787 if(gClient.participantId !== part.id || gSeeOwnCursor) {
1788 var div = document.createElement("div");
1789 div.className = "cursor";
1790 div.style.display = "none";
1791 part.cursorDiv = $("#cursors")[0].appendChild(div);
1792 $(part.cursorDiv).fadeIn(2000);
1793
1794 var div = document.createElement("div");
1795 div.className = "name";
1796 div.style.backgroundColor = part.color || "#777";
1797 div.textContent = part.name || "";
1798 part.cursorDiv.appendChild(div);
1799
1800 } else {
1801 part.cursorDiv = undefined;
1802 }
1803 });
1804 gClient.on("participant removed", function(part) {
1805 // remove nameDiv
1806 var nd = $(part.nameDiv);
1807 var cd = $(part.cursorDiv);
1808 cd.fadeOut(2000);
1809 nd.fadeOut(2000, function() {
1810 nd.remove();
1811 cd.remove();
1812 part.nameDiv = undefined;
1813 part.cursorDiv = undefined;
1814 });
1815 });
1816 gClient.on("participant update", function(part) {
1817 var name = part.name || "";
1818 var color = part.color || "#777";
1819 part.nameDiv.style.backgroundColor = color;
1820 part.nameDiv.textContent = name;
1821 $(part.cursorDiv)
1822 .find(".name")
1823 .text(name)
1824 .css("background-color", color);
1825 });
1826 gClient.on("ch", function(msg) {
1827 for(var id in gClient.ppl) {
1828 if(gClient.ppl.hasOwnProperty(id)) {
1829 var part = gClient.ppl[id];
1830 if(part.id === gClient.participantId) {
1831 $(part.nameDiv).addClass("me");
1832 } else {
1833 $(part.nameDiv).removeClass("me");
1834 }
1835 if(msg.ch.crown && msg.ch.crown.participantId === part.id) {
1836 $(part.nameDiv).addClass("owner");
1837 $(part.cursorDiv).addClass("owner");
1838 } else {
1839 $(part.nameDiv).removeClass("owner");
1840 $(part.cursorDiv).removeClass("owner");
1841 }
1842 if(gPianoMutes.indexOf(part._id) !== -1) {
1843 $(part.nameDiv).addClass("muted-notes");
1844 } else {
1845 $(part.nameDiv).removeClass("muted-notes");
1846 }
1847 if(gChatMutes.indexOf(part._id) !== -1) {
1848 $(part.nameDiv).addClass("muted-chat");
1849 } else {
1850 $(part.nameDiv).removeClass("muted-chat");
1851 }
1852 }
1853 }
1854 });
1855 })();
1856
1857
1858 // Handle changes to crown
1859 (function() {
1860 var jqcrown = $('<div id="crown"></div>').appendTo(document.body).hide();
1861 var jqcountdown = $('<span></span>').appendTo(jqcrown);
1862 var countdown_interval;
1863 jqcrown.click(function() {
1864 gClient.sendArray([{m: "chown", id: gClient.participantId}]);
1865 });
1866 gClient.on("ch", function(msg) {
1867 if(msg.ch.crown) {
1868 var crown = msg.ch.crown;
1869 if(!crown.participantId || !gClient.ppl[crown.participantId]) {
1870 var land_time = crown.time + 2000 - gClient.serverTimeOffset;
1871 var avail_time = crown.time + 15000 - gClient.serverTimeOffset;
1872 jqcountdown.text("");
1873 jqcrown.show();
1874 if(land_time - Date.now() <= 0) {
1875 jqcrown.css({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"});
1876 } else {
1877 jqcrown.css({"left": crown.startPos.x + "%", "top": crown.startPos.y + "%"});
1878 jqcrown.addClass("spin");
1879 jqcrown.animate({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"}, 2000, "linear", function() {
1880 jqcrown.removeClass("spin");
1881 });
1882 }
1883 clearInterval(countdown_interval);
1884 countdown_interval = setInterval(function() {
1885 var time = Date.now();
1886 if(time >= land_time) {
1887 var ms = avail_time - time;
1888 if(ms > 0) {
1889 jqcountdown.text(Math.ceil(ms / 1000) + "s");
1890 } else {
1891 jqcountdown.text("");
1892 clearInterval(countdown_interval);
1893 }
1894 }
1895 }, 1000);
1896 } else {
1897 jqcrown.hide();
1898 }
1899 } else {
1900 jqcrown.hide();
1901 }
1902 });
1903 gClient.on("disconnect", function() {
1904 jqcrown.fadeOut(2000);
1905 });
1906 })();
1907
1908
1909 // Playing notes
1910 gClient.on("n", function(msg) {
1911 var t = msg.t - gClient.serverTimeOffset + TIMING_TARGET - Date.now();
1912 var participant = gClient.findParticipantById(msg.p);
1913 if(gPianoMutes.indexOf(participant._id) !== -1)
1914 return;
1915 for(var i = 0; i < msg.n.length; i++) {
1916 var note = msg.n[i];
1917 var ms = t + (note.d || 0);
1918 if(ms < 0) {
1919 ms = 0;
1920 }
1921 else if(ms > 10000) continue;
1922 if(note.s) {
1923 gPiano.stop(note.n, participant, ms);
1924 } else {
1925 var vel = (typeof note.v !== "undefined")? parseFloat(note.v) : DEFAULT_VELOCITY;
1926 if(vel < 0) vel = 0; else if (vel > 1) vel = 1;
1927 gPiano.play(note.n, vel, participant, ms);
1928 }
1929 }
1930 });
1931
1932 // Send cursor updates
1933 var mx = 0, last_mx = -10, my = 0, last_my = -10;
1934 setInterval(function() {
1935 if(Math.abs(mx - last_mx) > 0.1 || Math.abs(my - last_my) > 0.1) {
1936 last_mx = mx;
1937 last_my = my;
1938 gClient.sendArray([{m: "m", x: mx, y: my}]);
1939 var part = gClient.getOwnParticipant();
1940 if(part) {
1941 part.x = mx;
1942 part.y = my;
1943 }
1944 }
1945 }, 50);
1946 $(document).mousemove(function(event) {
1947 mx = ((event.pageX / $(window).width()) * 100).toFixed(2);
1948 my = ((event.pageY / $(window).height()) * 100).toFixed(2);
1949 });
1950
1951 // Animate cursors
1952 setInterval(function() {
1953 for(var id in gClient.ppl) {
1954 if(!gClient.ppl.hasOwnProperty(id)) continue;
1955 var part = gClient.ppl[id];
1956 if(part.cursorDiv && (Math.abs(part.x - part.displayX) > 0.1 || Math.abs(part.y - part.displayY) > 0.1)) {
1957 part.displayX += (part.x - part.displayX) * 0.75;
1958 part.displayY += (part.y - part.displayY) * 0.75;
1959 part.cursorDiv.style.left = part.displayX + "%";
1960 part.cursorDiv.style.top = part.displayY + "%";
1961 }
1962 }
1963 }, 50);
1964
1965
1966 // Room settings button
1967 (function() {
1968 gClient.on("ch", function(msg) {
1969 if(gClient.isOwner()) {
1970 $("#room-settings-btn").show();
1971 } else {
1972 $("#room-settings-btn").hide();
1973 }
1974 });
1975 $("#room-settings-btn").click(function(evt) {
1976 if(gClient.channel && gClient.isOwner()) {
1977 var settings = gClient.channel.settings;
1978 openModal("#room-settings");
1979 setTimeout(function() {
1980 $("#room-settings .checkbox[name=visible]").prop("checked", settings.visible);
1981 $("#room-settings .checkbox[name=chat]").prop("checked", settings.chat);
1982 $("#room-settings .checkbox[name=crownsolo]").prop("checked", settings.crownsolo);
1983 $("#room-settings input[name=color]").val(settings.color);
1984 }, 100);
1985 }
1986 });
1987 $("#room-settings .submit").click(function() {
1988 var settings = {
1989 visible: $("#room-settings .checkbox[name=visible]").is(":checked"),
1990 chat: $("#room-settings .checkbox[name=chat]").is(":checked"),
1991 crownsolo: $("#room-settings .checkbox[name=crownsolo]").is(":checked"),
1992 color: $("#room-settings input[name=color]").val()
1993 };
1994 gClient.sendArray([{m: "chset", set: settings}]);
1995 closeModal();
1996 });
1997 $("#room-settings .drop-crown").click(function() {
1998 gClient.sendArray([{m: "chown"}]);
1999 closeModal();
2000 });
2001 })();
2002
2003 // Handle notifications
2004 gClient.on("notification", function(msg) {
2005 new Notification(msg);
2006 });
2007
2008 // Don't foget spin
2009 gClient.on("ch", function(msg) {
2010 var chidlo = msg.ch._id.toLowerCase();
2011 if(chidlo === "spin" || chidlo.substr(-5) === "/spin") {
2012 $("#piano").addClass("spin");
2013 } else {
2014 $("#piano").removeClass("spin");
2015 }
2016 });
2017
2018 /*function eb() {
2019 if(gClient.channel && gClient.channel._id.toLowerCase() === "test/fishing") {
2020 ebsprite.start(gClient);
2021 } else {
2022 ebsprite.stop();
2023 }
2024 }
2025 if(ebsprite) {
2026 gClient.on("ch", eb);
2027 eb();
2028 }*/
2029
2030 // Crownsolo notice
2031 gClient.on("ch", function(msg) {
2032 if(msg.ch.settings.crownsolo) {
2033 if($("#crownsolo-notice").length == 0) {
2034 $('<div id="crownsolo-notice">').text(Language ['Crownsolo Status']).appendTo("body").fadeIn(1000);
2035 }
2036 } else {
2037 $("#crownsolo-notice").remove();
2038 }
2039 });
2040 gClient.on("disconnect", function() {
2041 $("#crownsolo-notice").remove();
2042 });
2043
2044
2045 var gPianoMutes = [];
2046 var gChatMutes = [];
2047
2048 var volume_slider = new VolumeSlider(document.getElementById("volume"), function(v) {
2049 gPiano.audio.setVolume(v);
2050 if(window.localStorage) localStorage.volume = v;
2051 });
2052 volume_slider.set(gPiano.audio.volume);
2053
2054 var Note = function(note, octave) {
2055 this.note = note;
2056 this.octave = octave || 0;
2057 };
2058
2059
2060
2061 var n = function(a, b) { return {note: new Note(a, b), held: false}; };
2062 var key_binding = {
2063 65: n("gs"),
2064 90: n("a"),
2065 83: n("as"),
2066 88: n("b"),
2067 67: n("c", 1),
2068 70: n("cs", 1),
2069 86: n("d", 1),
2070 71: n("ds", 1),
2071 66: n("e", 1),
2072 78: n("f", 1),
2073 74: n("fs", 1),
2074 77: n("g", 1),
2075 75: n("gs", 1),
2076 188: n("a", 1),
2077 76: n("as", 1),
2078 190: n("b", 1),
2079 191: n("c", 2),
2080 222: n("cs", 2),
2081
2082 49: n("gs", 1),
2083 81: n("a", 1),
2084 50: n("as", 1),
2085 87: n("b", 1),
2086 69: n("c", 2),
2087 52: n("cs", 2),
2088 82: n("d", 2),
2089 53: n("ds", 2),
2090 84: n("e", 2),
2091 89: n("f", 2),
2092 55: n("fs", 2),
2093 85: n("g", 2),
2094 56: n("gs", 2),
2095 73: n("a", 2),
2096 57: n("as", 2),
2097 79: n("b", 2),
2098 80: n("c", 3),
2099 189: n("cs", 3),
2100 219: n("d", 3),
2101 187: n("ds", 3),
2102 221: n("e", 3)
2103 };
2104
2105 var capsLockKey = false;
2106
2107 var transpose_octave = 0;
2108
2109 function handleKeyDown(evt) {
2110
2111 //console.log(evt);
2112 var code = parseInt(evt.keyCode);
2113 if(key_binding[code] !== undefined) {
2114 var binding = key_binding[code];
2115 if(!binding.held) {
2116 binding.held = true;
2117
2118 var note = binding.note;
2119 var octave = 1 + note.octave + transpose_octave;
2120 if(evt.shiftKey) ++octave;
2121 else if(capsLockKey || evt.ctrlKey) --octave;
2122 note = note.note + octave;
2123 var vol = $('input[class=power-slider]').val(); //velocityFromMouseY();
2124 press(note, vol);
2125 }
2126
2127 if(++gKeyboardSeq == 3) {
2128 gKnowsYouCanUseKeyboard = true;
2129 if(window.gKnowsYouCanUseKeyboardTimeout) clearTimeout(gKnowsYouCanUseKeyboardTimeout);
2130 if(localStorage) localStorage.knowsYouCanUseKeyboard = true;
2131 if(window.gKnowsYouCanUseKeyboardNotification) gKnowsYouCanUseKeyboardNotification.close();
2132 }
2133
2134 evt.preventDefault();
2135 evt.stopPropagation();
2136 return false;
2137 } else if(code == 20) { // Caps Lock
2138 capsLockKey = true;
2139 evt.preventDefault();
2140 } else if(code === 0x20) { // Space Bar
2141 pressSustain();
2142 evt.preventDefault();
2143 } else if (code == 33) {
2144 $('#stand').val('');
2145 } else if (code == 34) {
2146 var selected = "";
2147 if (window.getSelection) {
2148 selected = window.getSelection().toString();
2149 } else if (document.selection && document.selection.type != "Control") {
2150 selected = document.selection.createRange().text;
2151 }
2152
2153 $('#stand').val(selected);
2154 } else if((code === 38 || code === 39) && transpose_octave < 3) {
2155 ++transpose_octave;
2156 } else if((code === 40 || code === 37) && transpose_octave > -2) {
2157 --transpose_octave;
2158 } else if(code == 9) { // Tab (don't tab away from the piano)
2159 evt.preventDefault();
2160 } else if(code == 8) { // Backspace (don't navigate Back)
2161 gAutoSustain = !gAutoSustain;
2162 evt.preventDefault();
2163 }
2164 };
2165
2166 function handleKeyUp(evt) {
2167 var code = parseInt(evt.keyCode);
2168 if(key_binding[code] !== undefined) {
2169 var binding = key_binding[code];
2170 if(binding.held) {
2171 binding.held = false;
2172
2173 var note = binding.note;
2174 var octave = 1 + note.octave + transpose_octave;
2175 if(evt.shiftKey) ++octave;
2176 else if(capsLockKey || evt.ctrlKey) --octave;
2177 note = note.note + octave;
2178 release(note);
2179 }
2180
2181 evt.preventDefault();
2182 evt.stopPropagation();
2183 return false;
2184 } else if(code == 20) { // Caps Lock
2185 capsLockKey = false;
2186 evt.preventDefault();
2187 } else if(code === 0x20) { // Space Bar
2188 releaseSustain();
2189 evt.preventDefault();
2190 }
2191 };
2192
2193 function handleKeyPress(evt) {
2194 evt.preventDefault();
2195 evt.stopPropagation();
2196 if(evt.keyCode == 27 || evt.keyCode == 13) {
2197 //$("#chat input").focus();
2198 }
2199 return false;
2200 };
2201
2202 var recapListener = function(evt) {
2203 captureKeyboard();
2204 };
2205
2206 function captureKeyboard() {
2207 $("#piano").off("mousedown", recapListener);
2208 $("#piano").off("touchstart", recapListener);
2209 $(document).on("keydown", handleKeyDown );
2210 $(document).on("keyup", handleKeyUp);
2211 $(window).on("keypress", handleKeyPress );
2212 };
2213
2214 function releaseKeyboard() {
2215 $(document).off("keydown", handleKeyDown );
2216 $(document).off("keyup", handleKeyUp);
2217 $(window).off("keypress", handleKeyPress );
2218 $("#piano").on("mousedown", recapListener);
2219 $("#piano").on("touchstart", recapListener);
2220 };
2221
2222 captureKeyboard();
2223
2224
2225 var velocityFromMouseY = function() {
2226 return 0.1 + (my / 100) * 0.6;
2227 };
2228
2229
2230
2231
2232
2233 // NoteQuota
2234 var gNoteQuota = (function() {
2235 var last_rat = 0;
2236 var nqjq = $("#quota .value");
2237 setInterval(function() {
2238 gNoteQuota.tick();
2239 }, 2000);
2240 return new NoteQuota(function(points) {
2241 // update UI
2242 var rat = (points / this.max) * 100;
2243 if(rat <= last_rat)
2244 nqjq.stop(true, true).css("width", rat.toFixed(0) + "%");
2245 else
2246 nqjq.stop(true, true).animate({"width": rat.toFixed(0) + "%"}, 2000, "linear");
2247 last_rat = rat;
2248 });
2249 })();
2250 gClient.on("nq", function(nq_params) {
2251 gNoteQuota.setParams(nq_params);
2252 });
2253 gClient.on("disconnect", function() {
2254 gNoteQuota.setParams(NoteQuota.PARAMS_OFFLINE);
2255 });
2256
2257
2258
2259 // click participant names
2260 (function() {
2261 var ele = document.getElementById("names");
2262 var touchhandler = function(e) {
2263 var target_jq = $(e.target);
2264 if(target_jq.hasClass("name")) {
2265 target_jq.addClass("play");
2266 if(e.target.participantId == gClient.participantId) {
2267 openModal("#rename", "input[name=name]");
2268 setTimeout(function() {
2269 $("#rename input[name=name]").val(gClient.ppl[gClient.participantId].name);
2270 $("#rename input[name=color]").val(gClient.ppl[gClient.participantId].color);
2271 }, 100);
2272 } else if(e.target.participantId) {
2273 var id = e.target.participantId;
2274 var part = gClient.ppl[id] || null;
2275 if(part) {
2276 participantMenu(part);
2277 e.stopPropagation();
2278 }
2279 }
2280 }
2281 };
2282 ele.addEventListener("mousedown", touchhandler);
2283 ele.addEventListener("touchstart", touchhandler);
2284 var releasehandler = function(e) {
2285 $("#names .name").removeClass("play");
2286 };
2287 document.body.addEventListener("mouseup", releasehandler);
2288 document.body.addEventListener("touchend", releasehandler);
2289
2290 var removeParticipantMenus = function() {
2291 $(".participant-menu").remove();
2292 $(".participantSpotlight").hide();
2293 document.removeEventListener("mousedown", removeParticipantMenus);
2294 document.removeEventListener("touchstart", removeParticipantMenus);
2295 };
2296
2297 var participantMenu = function(part) {
2298 if(!part) return;
2299 removeParticipantMenus();
2300 document.addEventListener("mousedown", removeParticipantMenus);
2301 document.addEventListener("touchstart", removeParticipantMenus);
2302 $("#" + part.id).find(".enemySpotlight").show();
2303 var menu = $('<div class="participant-menu"></div>');
2304 $("body").append(menu);
2305 // move menu to name position
2306 var jq_nd = $(part.nameDiv);
2307 var pos = jq_nd.position();
2308 menu.css({
2309 "top": pos.top + jq_nd.height() + 15,
2310 "left": pos.left + 6,
2311 "background": part.color || "black"
2312 });
2313 menu.on("mousedown touchstart", function(evt) {
2314 evt.stopPropagation();
2315 var target = $(evt.target);
2316 if(target.hasClass("menu-item")) {
2317 target.addClass("clicked");
2318 menu.fadeOut(200, function() {
2319 removeParticipantMenus();
2320 });
2321 }
2322 });
2323 // this spaces stuff out but also can be used for informational
2324 $('<div class="info"></div>').appendTo(menu).text(part._id);
2325 // add menu items
2326 if(gPianoMutes.indexOf(part._id) == -1) {
2327 $('<div class="menu-item">' + Language ['MuteNotes Tool'] + '</div>').appendTo(menu)
2328 .on("mousedown touchstart", function(evt) {
2329 gPianoMutes.push(part._id);
2330 $(part.nameDiv).addClass("muted-notes");
2331 });
2332 } else {
2333 $('<div class="menu-item">' + Language ['UnmuteNotes Tool'] + '</div>').appendTo(menu)
2334 .on("mousedown touchstart", function(evt) {
2335 var i;
2336 while((i = gPianoMutes.indexOf(part._id)) != -1)
2337 gPianoMutes.splice(i, 1);
2338 $(part.nameDiv).removeClass("muted-notes");
2339 });
2340 }
2341 if(gChatMutes.indexOf(part._id) == -1) {
2342 $('<div class="menu-item">' + Language ['MuteChat Tool'] + '</div>').appendTo(menu)
2343 .on("mousedown touchstart", function(evt) {
2344 gChatMutes.push(part._id);
2345 $(part.nameDiv).addClass("muted-chat");
2346 });
2347 } else {
2348 $('<div class="menu-item">' + Language ['UnmuteChat Tool'] + '</div>').appendTo(menu)
2349 .on("mousedown touchstart", function(evt) {
2350 var i;
2351 while((i = gChatMutes.indexOf(part._id)) != -1)
2352 gChatMutes.splice(i, 1);
2353 $(part.nameDiv).removeClass("muted-chat");
2354 });
2355 }
2356 if(!(gPianoMutes.indexOf(part._id) >= 0) || !(gChatMutes.indexOf(part._id) >= 0)) {
2357 $('<div class="menu-item">' + Language ['MuteCompletely Tool'] + '</div>').appendTo(menu)
2358 .on("mousedown touchstart", function(evt) {
2359 gPianoMutes.push(part._id);
2360 gChatMutes.push(part._id);
2361 $(part.nameDiv).addClass("muted-notes");
2362 $(part.nameDiv).addClass("muted-chat");
2363 });
2364 }
2365 if((gPianoMutes.indexOf(part._id) >= 0) || (gChatMutes.indexOf(part._id) >= 0)) {
2366 $('<div class="menu-item">' + Language ['UnmuteCompletely Tool'] + '</div>').appendTo(menu)
2367 .on("mousedown touchstart", function(evt) {
2368 var i;
2369 while((i = gPianoMutes.indexOf(part._id)) != -1)
2370 gPianoMutes.splice(i, 1);
2371 while((i = gChatMutes.indexOf(part._id)) != -1)
2372 gChatMutes.splice(i, 1);
2373 $(part.nameDiv).removeClass("muted-notes");
2374 $(part.nameDiv).removeClass("muted-chat");
2375 });
2376 }
2377 if(gClient.isOwner()) {
2378 $('<div class="menu-item give-crown">' + Language ['GiveCrown Tool'] + '</div>').appendTo(menu)
2379 .on("mousedown touchstart", function(evt) {
2380 gClient.sendArray([{m: "chown", id: part.id}]);
2381 });
2382 $('<div class="menu-item kickban">' + Language ['Kickban Tool'] +'</div>').appendTo(menu)
2383 .on("mousedown touchstart", function(evt) {
2384 var minutes = prompt("How many minutes? (0-60)", "30");
2385 if(minutes === null) return;
2386 minutes = parseFloat(minutes) || 0;
2387 var ms = minutes * 60 * 1000;
2388 gClient.sendArray([{m: "kickban", _id: part._id, ms: ms}]);
2389 });
2390 }
2391 menu.fadeIn(100);
2392 };
2393 })();
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410// Notification class
2411
2412////////////////////////////////////////////////////////////////
2413
2414 var Notification = function(par) {
2415 EventEmitter.call(this);
2416
2417 var par = par || {};
2418
2419 this.id = "Notify-" + (par.id || Math.random());
2420 this.title = par.title || "";
2421 this.text = par.text || "";
2422 this.html = par.html || "";
2423 this.target = $(par.target || "#piano");
2424 this.duration = par.duration || 30000;
2425 this["class"] = par["class"] || "classic";
2426
2427 var self = this;
2428 var eles = $("#" + this.id);
2429 if(eles.length > 0) {
2430 eles.remove();
2431 }
2432 this.domElement = $('<div class="notification"><div class="notification-body"><div class="title"></div>' +
2433 '<div class="text"></div></div><div class="x">x</div></div>');
2434 this.domElement[0].id = this.id;
2435 this.domElement.addClass(this["class"]);
2436 this.domElement.find(".title").text(this.title);
2437 if(this.text.length > 0) {
2438 this.domElement.find(".text").text(this.text);
2439 } else if(this.html instanceof HTMLElement) {
2440 this.domElement.find(".text")[0].appendChild(this.html);
2441 } else if(this.html.length > 0) {
2442 this.domElement.find(".text").html(this.html);
2443 }
2444 document.body.appendChild(this.domElement.get(0));
2445
2446 this.position();
2447 this.onresize = function() {
2448 self.position();
2449 };
2450 window.addEventListener("resize", this.onresize);
2451
2452 this.domElement.find(".x").click(function() {
2453 self.close();
2454 });
2455
2456 if(this.duration > 0) {
2457 setTimeout(function() {
2458 self.close();
2459 }, this.duration);
2460 }
2461
2462 return this;
2463 }
2464
2465 mixin(Notification.prototype, EventEmitter.prototype);
2466 Notification.prototype.constructor = Notification;
2467
2468 Notification.prototype.position = function() {
2469 var pos = this.target.offset();
2470 var x = pos.left - (this.domElement.width() / 2) + (this.target.width() / 4);
2471 var y = pos.top - this.domElement.height() - 8;
2472 var width = this.domElement.width();
2473 if(x + width > $("body").width()) {
2474 x -= ((x + width) - $("body").width());
2475 }
2476 if(x < 0) x = 0;
2477 this.domElement.offset({left: x, top: y});
2478 };
2479
2480 Notification.prototype.close = function() {
2481 var self = this;
2482 window.removeEventListener("resize", this.onresize);
2483 this.domElement.fadeOut(500, function() {
2484 self.domElement.remove();
2485 self.emit("close");
2486 });
2487 };
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503// set variables from settings or set settings
2504
2505////////////////////////////////////////////////////////////////
2506
2507 var gKeyboardSeq = 0;
2508 var gKnowsYouCanUseKeyboard = false;
2509 if(localStorage && localStorage.knowsYouCanUseKeyboard) gKnowsYouCanUseKeyboard = true;
2510
2511 if(!gKnowsYouCanUseKeyboard) {
2512 window.gKnowsYouCanUseKeyboardTimeout = setTimeout (function ( )
2513 {
2514 window.gKnowsYouCanUseKeyboardNotification = new Notification ({ id: 'UCanPlay', title: Language ['UCanPlay Title'], text: Language ['UCanPlay Description'],
2515 target: "#piano", duration: 10000});
2516 }, 30000);
2517 }
2518
2519 if(window.localStorage) {
2520
2521 if(localStorage.volume) {
2522 volume_slider.set(localStorage.volume);
2523 gPiano.audio.setVolume(localStorage.volume);
2524 }
2525 else localStorage.volume = gPiano.audio.volume;
2526
2527 window.gHasBeenHereBefore = (localStorage.gHasBeenHereBefore || false);
2528 if(gHasBeenHereBefore) {
2529 }
2530 localStorage.gHasBeenHereBefore = true;
2531
2532 }
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546// New room, change room
2547
2548////////////////////////////////////////////////////////////////
2549
2550 $("#room > .info").text("--");
2551 gClient.on("ch", function(msg) {
2552 var channel = msg.ch;
2553 var info = $("#room > .info");
2554 info.text(channel._id);
2555 if(channel.settings.lobby) info.addClass("lobby");
2556 else info.removeClass("lobby");
2557 if(!channel.settings.chat) info.addClass("no-chat");
2558 else info.removeClass("no-chat");
2559 if(channel.settings.crownsolo) info.addClass("crownsolo");
2560 else info.removeClass("crownsolo");
2561 if(!channel.settings.visible) info.addClass("not-visible");
2562 else info.removeClass("not-visible");
2563 });
2564 gClient.on("ls", function(ls) {
2565 for(var i in ls.u) {
2566 if(!ls.u.hasOwnProperty(i)) continue;
2567 var room = ls.u[i];
2568 var info = $("#room .info[roomname=\"" + (room._id + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0') + "\"]");
2569 if(info.length == 0) {
2570 info = $("<div class=\"info\"></div>");
2571 info.attr("roomname", room._id);
2572 $("#room .more").append(info);
2573 }
2574 info.text(room._id + " (" + room.count + ")");
2575 if(room.settings.lobby) info.addClass("lobby");
2576 else info.removeClass("lobby");
2577 if(!room.settings.chat) info.addClass("no-chat");
2578 else info.removeClass("no-chat");
2579 if(room.settings.crownsolo) info.addClass("crownsolo");
2580 else info.removeClass("crownsolo");
2581 if(!room.settings.visible) info.addClass("not-visible");
2582 else info.removeClass("not-visible");
2583 };
2584 });
2585 $("#room").on("click", function(evt) {
2586 evt.stopPropagation();
2587
2588 // clicks on a new room
2589 if($(evt.target).hasClass("info") && $(evt.target).parents(".more").length) {
2590 $("#room .more").fadeOut(250);
2591 var selected_name = $(evt.target).attr("roomname");
2592 if(typeof selected_name != "undefined") {
2593 if (selected_name == "lobby")
2594 selected_name = "lolwutsecretlobbybackdoor";
2595
2596 changeRoom(selected_name, "right");
2597
2598 }
2599 return false;
2600 }
2601 // clicks on "New Room..."
2602 else if($(evt.target).hasClass("new")) {
2603 openModal("#new-room", "input[name=name]");
2604 }
2605 // all other clicks
2606 var doc_click = function(evt) {
2607 if($(evt.target).is("#room .more")) return;
2608 $(document).off("mousedown", doc_click);
2609 $("#room .more").fadeOut(250);
2610 gClient.sendArray([{m: "-ls"}]);
2611 }
2612 $(document).on("mousedown", doc_click);
2613 $("#room .more .info").remove();
2614 $("#room .more").show();
2615 gClient.sendArray([{m: "+ls"}]);
2616 });
2617 $("#new-room-btn").on("click", function(evt) {
2618 evt.stopPropagation();
2619 openModal("#new-room", "input[name=name]");
2620 });
2621
2622
2623 $("#play-alone-btn").on("click", function(evt) {
2624 evt.stopPropagation();
2625 var room_name = "Room" + Math.floor(Math.random() * 1000000000000);
2626 changeRoom(room_name, "right", {"visible": false, "chat": true, "crownsolo": false});
2627
2628 setTimeout (function ( )
2629 {
2630 new Notification ({ id: "PlayAlone", title: Language ['PlayAlone Title'], html: Language ['PlayAlone Description'] + '<br/><br/>\
2631 <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;">Facebook</a> | \
2632 <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Twitter</a>', duration: 25000 });
2633 }, 1000);
2634 });
2635
2636
2637
2638 var gModal;
2639
2640 function modalHandleEsc(evt) {
2641 if(evt.keyCode == 27) {
2642 closeModal();
2643 evt.preventDefault();
2644 evt.stopPropagation();
2645 }
2646 };
2647
2648 function openModal(selector, focus) {
2649 chat.blur();
2650 releaseKeyboard();
2651 $(document).on("keydown", modalHandleEsc);
2652 $("#modal #modals > *").hide();
2653 $("#modal").fadeIn(250);
2654 $(selector).show();
2655 setTimeout(function() {
2656 $(selector).find(focus).focus();
2657 }, 100);
2658 gModal = selector;
2659 };
2660
2661 function closeModal() {
2662 $(document).off("keydown", modalHandleEsc);
2663 $("#modal").fadeOut(100);
2664 $("#modal #modals > *").hide();
2665 captureKeyboard();
2666 gModal = null;
2667 };
2668
2669 var modal_bg = $("#modal .bg")[0];
2670 $(modal_bg).on("click", function(evt) {
2671 if(evt.target != modal_bg) return;
2672 closeModal();
2673 });
2674
2675 (function() {
2676 function submit() {
2677 var name = $("#new-room .text[name=name]").val();
2678 var settings = {
2679 visible: $("#new-room .checkbox[name=visible]").is(":checked"),
2680 chat: true,
2681 crownsolo: false
2682 };
2683 $("#new-room .text[name=name]").val("");
2684 closeModal();
2685 changeRoom(name, "right", settings);
2686
2687 setTimeout (function ( )
2688 {
2689 new Notification ({ id: "NewRoom", title: Language ['NewRoom Title'], html: Language ['NewRoom Description'] + '<br/><br/>\
2690 <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;">Facebook</a> | \
2691 <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Twitter</a>', duration: 25000 });
2692 }, 1000);
2693 };
2694 $("#new-room .submit").click(function(evt) {
2695 submit();
2696 });
2697 $("#new-room .text[name=name]").keypress(function(evt) {
2698 if(evt.keyCode == 13) {
2699 submit();
2700 } else if(evt.keyCode == 27) {
2701 closeModal();
2702 } else {
2703 return;
2704 }
2705 evt.preventDefault();
2706 evt.stopPropagation();
2707 return false;
2708 });
2709 })();
2710
2711
2712
2713
2714
2715
2716
2717
2718 function changeRoom(name, direction, settings, push) {
2719 window.LOG = [];
2720
2721 if(!settings) settings = {};
2722 if(!direction) direction = "right";
2723 if(typeof push == "undefined") push = true;
2724 var opposite = direction == "left" ? "right" : "left";
2725
2726 if(name == "") name = "lobby";
2727 if(gClient.channel && gClient.channel._id === name) return;
2728 if(push) {
2729 var url = "/" + encodeURIComponent(name).replace("'", "%27");
2730 if(window.history && history.pushState) {
2731 history.pushState({"depth": gHistoryDepth += 1, "name": name}, "Piano > " + name, url);
2732 } else {
2733 window.location = url;
2734 return;
2735 }
2736 }
2737
2738 gClient.setChannel(name, settings);
2739 GetCurrentData ( );
2740
2741 var t = 0, d = 100;
2742 $("#piano").addClass("ease-out").addClass("slide-" + opposite);
2743 setTimeout(function() {
2744 $("#piano").removeClass("ease-out").removeClass("slide-" + opposite).addClass("slide-" + direction);
2745 }, t += d);
2746 setTimeout(function() {
2747 $("#piano").addClass("ease-in").removeClass("slide-" + direction);
2748 }, t += d);
2749 setTimeout(function() {
2750 $("#piano").removeClass("ease-in");
2751 }, t += d);
2752 };
2753
2754 var gHistoryDepth = 0;
2755 $(window).on("popstate", function(evt) {
2756 var depth = evt.state ? evt.state.depth : 0;
2757 if(depth == gHistoryDepth) return; // <-- forgot why I did that though...
2758
2759 var direction = depth <= gHistoryDepth ? "left" : "right";
2760 gHistoryDepth = depth;
2761
2762 var name = decodeURIComponent(window.location.pathname);
2763 if(name.substr(0, 1) == "/") name = name.substr(1);
2764 changeRoom(name, direction, null, false);
2765 });
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786// Rename
2787
2788////////////////////////////////////////////////////////////////
2789
2790(function() {
2791 function submit() {
2792 var set = {
2793 name: $("#rename input[name=name]").val(),
2794 color: $("#rename input[name=color]").val()
2795 };
2796 //$("#rename .text[name=name]").val("");
2797 closeModal();
2798 gClient.sendArray([{m: "userset", set: set}]);
2799 };
2800 $("#rename .submit").click(function(evt) {
2801 submit();
2802 });
2803 $("#rename .text[name=name]").keypress(function(evt) {
2804 if(evt.keyCode == 13) {
2805 submit();
2806 } else if(evt.keyCode == 27) {
2807 closeModal();
2808 } else {
2809 return;
2810 }
2811 evt.preventDefault();
2812 evt.stopPropagation();
2813 return false;
2814 });
2815 })();
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831// chatctor
2832
2833////////////////////////////////////////////////////////////////
2834
2835 var chatLoaded = false;
2836 var chat = (function() {
2837 gClient.on("ch", function(msg) {
2838 if(msg.ch.settings.chat) {
2839 chat.show();
2840 } else {
2841 chat.hide();
2842 }
2843 });
2844 gClient.on("disconnect", function(msg) {
2845 chat.hide();
2846 });
2847 gClient.on("c", function(msg) {
2848 chat.clear();
2849 if(msg.c) {
2850 chatLoaded = false;
2851 for(var i = 0; i < msg.c.length; i++) {
2852 chat.receive(msg.c[i]);
2853 }
2854 chatLoaded = true;
2855 }
2856 });
2857 gClient.on("a", function(msg) {
2858 chat.receive(msg);
2859 });
2860
2861 $("#chat input").on("focus", function(evt) {
2862 releaseKeyboard();
2863 $("#chat").addClass('chatting');
2864 $("#chat").css ('box-shadow', 'none');
2865 chat.scrollToBottom();
2866 });
2867 /*$("#chat input").on("blur", function(evt) {
2868 captureKeyboard();
2869 $("#chat").removeClass("chatting");
2870 chat.scrollToBottom();
2871 });*/
2872 $(document).mousedown(function(evt) {
2873 if(!$("#chat").has(evt.target).length > 0) {
2874 chat.blur();
2875 }
2876 });
2877 document.addEventListener("touchstart", function(event) {
2878 for(var i in event.changedTouches) {
2879 var touch = event.changedTouches[i];
2880 if(!$("#chat").has(touch.target).length > 0) {
2881 chat.blur();
2882 }
2883 }
2884 });
2885 $(document).on("keydown", function(evt) {
2886 if($("#chat").hasClass("chatting")) {
2887 if(evt.keyCode == 27) {
2888 chat.blur();
2889 evt.preventDefault();
2890 evt.stopPropagation();
2891 } else if(evt.keyCode == 13) {
2892 $("#chat input").focus();
2893 }
2894 } else if(!gModal && (evt.keyCode == 27 || evt.keyCode == 13)) {
2895 $("#chat input").focus();
2896 }
2897 });
2898 $("#chat input").on("keydown", function(evt) {
2899 if(evt.keyCode == 13) {
2900 var message = $(this).val();
2901
2902 // ^^
2903
2904 if (message == Identifier + 'kawaii')
2905 {
2906 IsKawaii = !IsKawaii;
2907
2908 if (!IsKawaii)
2909 {
2910 $('.kawaiiSquare').remove ( );
2911
2912 $(this).val ("");
2913 return;
2914 }
2915
2916 var Messages =
2917 [
2918 'So many sockets, so many problems',
2919 'Where are my cookies? .w.',
2920 'test/fishing was a mistake',
2921 'I\'m going to pianorhythm.me',
2922 'Kawaii squares oO'
2923 ];
2924
2925 $('#chat > ul').append ('<li style = "color: white; opacity: 1"><span class="name kawaiiTitle">randon Lockaby:</span><span class="message">' + Messages [Math.floor (Math.random ( ) * Messages.length)] + '</span></li>')
2926
2927 for (var i = 0; i < 20; i++)
2928 for (var j = 0; j < 20; j++)
2929 {
2930 var X = (20 - j) * 25;
2931 var Y = 25 + i * 25;
2932
2933 var offsetX = 25 * j;
2934 var offsetY = 25 * i;
2935
2936 $('body').append ('<div class = "kawaiiSquare" onmouseover = "this.style.opacity = 1" style = "' +
2937 'top: ' + Y.toString ( ) + 'px; ' +
2938 'right: ' + X.toString ( ) + 'px;' +
2939 'background: url(\'http://i.imgur.com/NTZE2ep.png\');' +
2940 'background-position: -' + offsetX.toString ( ) + 'px -' + offsetY.toString ( ) + 'px;"></div>');
2941 }
2942
2943 $(this).val ("");
2944 return;
2945 }
2946
2947 if(message.length == 0) {
2948 setTimeout(function() {
2949 chat.blur();
2950 }, 100);
2951 } else if(message.length <= 512) {
2952 chat.send(message);
2953 $(this).val("");
2954 setTimeout(function() {
2955 chat.blur();
2956 }, 100);
2957 }
2958 evt.preventDefault();
2959 evt.stopPropagation();
2960 } else if(evt.keyCode == 27) {
2961 chat.blur();
2962 evt.preventDefault();
2963 evt.stopPropagation();
2964 } else if(evt.keyCode == 9) {
2965 evt.preventDefault();
2966 evt.stopPropagation();
2967 }
2968 });
2969
2970 return {
2971 show: function() {
2972 $("#chat").fadeIn();
2973 },
2974
2975 hide: function() {
2976 $("#chat").fadeOut();
2977 },
2978
2979 clear: function() {
2980 $("#chat li").remove();
2981 },
2982
2983 scrollToBottom: function() {
2984 var ele = $("#chat ul").get(0);
2985 ele.scrollTop = ele.scrollHeight;
2986 },
2987
2988 blur: function() {
2989 if($("#chat").hasClass("chatting")) {
2990 $("#chat input").get(0).blur();
2991 $("#chat").removeClass("chatting");
2992 chat.scrollToBottom();
2993 captureKeyboard();
2994 }
2995 },
2996
2997 send: function(message) {
2998 gClient.sendArray([{m:"a", message: message}]);
2999 },
3000
3001 receive: function(msg) {
3002 if(gChatMutes.indexOf(msg.p._id) != -1) return;
3003
3004 if ((msg.a [0] == Identifier) && chatLoaded && Status > 0)
3005 {
3006 if (Status == 2 && msg.p._id != MPP.client.user._id)
3007 return;
3008
3009 var Index = (msg.a.indexOf (' ') != -1) ? msg.a.indexOf (' ') : msg.a.length;
3010 var Command = [msg.a.substring (0, Index).replace (Identifier, '').toLowerCase ( ), msg.a.substring (Index + 1)]
3011
3012 if (Commands [Command [0]] != undefined)
3013 Commands [Command [0]] (Command [1], msg.p);
3014 else
3015 chat.send (Language ['NoExist Command']);
3016 }
3017
3018 var li = $('<li><span class="name"/><span class="message"/>');
3019
3020 li.find(".name").text(msg.p.name + ":");
3021
3022 li.find(".message").html(msg.a
3023 .replace(/(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/, function(url) { return '<a href="' + url + '" target="_blank">' + url + '</a>'; })
3024 .replace(/\[\d+:\d+\]/, function(value) { return '<span class="time">' + value.substring(1, value.length - 1) + '</span>' })
3025 .replace(/\[lolibot]/, function() { return '<span class="loli">lolibot</span>' }));
3026
3027 li.css("color", msg.p.color || "white");
3028
3029 $("#chat ul").append(li);
3030
3031 var eles = $("#chat ul li").get();
3032 for(var i = 1; i <= 50 && i <= eles.length; i++) {
3033 eles[eles.length - i].style.opacity = 1.0 - (i * 0.03);
3034 }
3035
3036 /*
3037 if(eles.length > 50) {
3038 eles[0].style.display = "none";
3039 }
3040 if(eles.length > 256) {
3041 $(eles[0]).remove();
3042 }
3043 */
3044
3045
3046 // scroll to bottom if not "chatting" or if not scrolled up
3047 if(!$("#chat").hasClass("chatting")) {
3048 chat.scrollToBottom();
3049 } else {
3050 var ele = $("#chat ul").get(0);
3051 if(ele.scrollTop > ele.scrollHeight - ele.offsetHeight - 50)
3052 chat.scrollToBottom();
3053 }
3054 }
3055 };
3056 })();
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072// MIDI
3073
3074////////////////////////////////////////////////////////////////
3075
3076 var MIDI_TRANSPOSE = -12;
3077 var MIDI_KEY_NAMES = ["a-1", "as-1", "b-1"];
3078 var bare_notes = "c cs d ds e f fs g gs a as b".split(" ");
3079 for(var oct = 0; oct < 7; oct++) {
3080 for(var i in bare_notes) {
3081 MIDI_KEY_NAMES.push(bare_notes[i] + oct);
3082 }
3083 }
3084 MIDI_KEY_NAMES.push("c7");
3085
3086 (function() {
3087
3088 if (navigator.requestMIDIAccess) {
3089 navigator.requestMIDIAccess().then(
3090 function(midi) {
3091 console.log(midi);
3092 function midimessagehandler(evt) {
3093 if(!evt.target.enabled) return;
3094 //console.log(evt);
3095 var channel = evt.data[0] & 0xf;
3096 var cmd = evt.data[0] >> 4;
3097 var note_number = evt.data[1];
3098 var vel = evt.data[2];
3099 //console.log(channel, cmd, note_number, vel);
3100 if(cmd == 8 || (cmd == 9 && vel == 0)) {
3101 // NOTE_OFF
3102 release(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE]);
3103 } else if(cmd == 9) {
3104 // NOTE_ON
3105 press(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE], vel / 100);
3106 } else if(cmd == 11) {
3107 // CONTROL_CHANGE
3108 if(!gAutoSustain) {
3109 if(note_number == 64) {
3110 if(vel > 0) {
3111 pressSustain();
3112 } else {
3113 releaseSustain();
3114 }
3115 }
3116 }
3117 }
3118 }
3119
3120 function plug() {
3121 if(midi.inputs.size > 0) {
3122 var inputs = midi.inputs.values();
3123 for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
3124 var input = input_it.value;
3125 input.onmidimessage = midimessagehandler;
3126 if(input.enabled !== false) {
3127 input.enabled = true;
3128 }
3129 console.log("input", input);
3130 }
3131 }
3132 if(midi.outputs.size > 0) {
3133 var outputs = midi.outputs.values();
3134 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
3135 var output = output_it.value;
3136 console.log("output", output);
3137 }
3138 gMidiOutTest = function(note_name, vel, delay_ms) {
3139 var note_number = MIDI_KEY_NAMES.indexOf(note_name);
3140 if(note_number == -1) return;
3141 note_number = note_number + 9 - MIDI_TRANSPOSE;
3142
3143 var outputs = midi.outputs.values();
3144 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
3145 var output = output_it.value;
3146 if(output.enabled) {
3147 output.send([0x90, note_number, vel], window.performance.now() + delay_ms);
3148 }
3149 }
3150 }
3151 }
3152 showConnections(false);
3153 }
3154
3155 midi.addEventListener("statechange", function(evt) {
3156 if(evt instanceof MIDIConnectionEvent) {
3157 plug();
3158 }
3159 });
3160
3161 plug();
3162
3163
3164 var connectionsNotification;
3165
3166 function showConnections(sticky) {
3167 var inputs_ul = document.createElement("ul");
3168 if(midi.inputs.size > 0) {
3169 var inputs = midi.inputs.values();
3170 for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
3171 var input = input_it.value;
3172 var li = document.createElement("li");
3173 li.connectionId = input.id;
3174 li.classList.add("connection");
3175 if(input.enabled) li.classList.add("enabled");
3176 li.textContent = input.name;
3177 li.addEventListener("click", function(evt) {
3178 var inputs = midi.inputs.values();
3179 for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
3180 var input = input_it.value;
3181 if(input.id === evt.target.connectionId) {
3182 input.enabled = !input.enabled;
3183 evt.target.classList.toggle("enabled");
3184 console.log("click", input);
3185 return;
3186 }
3187 }
3188 });
3189 inputs_ul.appendChild(li);
3190 }
3191 } else {
3192 inputs_ul.textContent = '(' + Language ['MidiEmpty Status'] + ')';
3193 }
3194 var outputs_ul = document.createElement("ul");
3195 if(midi.outputs.size > 0) {
3196 var outputs = midi.outputs.values();
3197 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
3198 var output = output_it.value;
3199 var li = document.createElement("li");
3200 li.connectionId = output.id;
3201 li.classList.add("connection");
3202 if(output.enabled) li.classList.add("enabled");
3203 li.textContent = output.name;
3204 li.addEventListener("click", function(evt) {
3205 var outputs = midi.outputs.values();
3206 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
3207 var output = output_it.value;
3208 if(output.id === evt.target.connectionId) {
3209 output.enabled = !output.enabled;
3210 evt.target.classList.toggle("enabled");
3211 console.log("click", output);
3212 return;
3213 }
3214 }
3215 });
3216 outputs_ul.appendChild(li);
3217 }
3218 } else {
3219 outputs_ul.textContent = '(' + Language ['MidiEmpty Status'] + ')';
3220 }
3221 var div = document.createElement("div");
3222 var h1 = document.createElement("h1");
3223 h1.textContent = Language ['MidiInput Title'];
3224 div.appendChild(h1);
3225 div.appendChild(inputs_ul);
3226 h1 = document.createElement("h1");
3227 h1.textContent = Language ['MidiOutput Title'];
3228 div.appendChild(h1);
3229 div.appendChild(outputs_ul);
3230
3231 connectionsNotification = new Notification ({ id: 'Midi', title: Language ['Midi Title'], duration: sticky ? -1 : 4500, html: div, target: '#midi-btn' });
3232 }
3233
3234 document.getElementById("midi-btn").addEventListener("click", function(evt) {
3235 if(!document.getElementById("Notify-Midi"))
3236 showConnections(true);
3237 else {
3238 connectionsNotification.close();
3239 }
3240 });
3241 },
3242 function(err){
3243 console.log(err);
3244 } );
3245 }
3246 })();
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261// bug supply
3262
3263////////////////////////////////////////////////////////////////
3264
3265 window.onerror = function(message, url, line) {
3266 var url = url || "(no url)";
3267 var line = line || "(no line)";
3268 // errors in socket.io
3269 if(url.indexOf("socket.io.js") !== -1) {
3270 if(message.indexOf("INVALID_STATE_ERR") !== -1) return;
3271 if(message.indexOf("InvalidStateError") !== -1) return;
3272 if(message.indexOf("DOM Exception 11") !== -1) return;
3273 if(message.indexOf("Property 'open' of object #<c> is not a function") !== -1) return;
3274 if(message.indexOf("Cannot call method 'close' of undefined") !== -1) return;
3275 if(message.indexOf("Cannot call method 'close' of null") !== -1) return;
3276 if(message.indexOf("Cannot call method 'onClose' of null") !== -1) return;
3277 if(message.indexOf("Cannot call method 'payload' of null") !== -1) return;
3278 if(message.indexOf("Unable to get value of the property 'close'") !== -1) return;
3279 if(message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) return;
3280 if(message.indexOf("Unable to get property 'close' of undefined or null reference") !== -1) return;
3281 if(message.indexOf("Unable to get value of the property 'close': object is null or undefined") !== -1) return;
3282 if(message.indexOf("this.transport is null") !== -1) return;
3283 }
3284 // errors in soundmanager2
3285 if(url.indexOf("soundmanager2.js") !== -1) {
3286 // operation disabled in safe mode?
3287 if(message.indexOf("Could not complete the operation due to error c00d36ef") !== -1) return;
3288 if(message.indexOf("_s.o._setVolume is not a function") !== -1) return;
3289 }
3290 // errors in midibridge
3291 if(url.indexOf("midibridge") !== -1) {
3292 if(message.indexOf("Error calling method on NPObject") !== -1) return;
3293 }
3294 // too many failing extensions injected in my html
3295 if(url.indexOf(".js") !== url.length - 3) return;
3296 // extensions inject cross-domain embeds too
3297 if(url.toLowerCase().indexOf("multiplayerpiano.com") == -1) return;
3298
3299 // errors in my code
3300 if(url.indexOf("script.js") !== -1) {
3301 if(message.indexOf("Object [object Object] has no method 'on'") !== -1) return;
3302 if(message.indexOf("Object [object Object] has no method 'off'") !== -1) return;
3303 if(message.indexOf("Property '$' of object [object Object] is not a function") !== -1) return;
3304 }
3305
3306 var enc = "/bugreport/"
3307 + (message ? encodeURIComponent(message) : "") + "/"
3308 + (url ? encodeURIComponent(url) : "") + "/"
3309 + (line ? encodeURIComponent(line) : "");
3310 var img = new Image();
3311 img.src = enc;
3312 };
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322 // API
3323 window.MPP = {
3324 press: press,
3325 release: release,
3326 piano: gPiano,
3327 client: gClient,
3328 chat: chat
3329 };
3330
3331
3332 var audio = gPiano.audio;
3333 var context = gPiano.audio.context;
3334
3335
3336 OnLoad ( );
3337
3338 });
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361// misc
3362
3363////////////////////////////////////////////////////////////////
3364
3365// analytics
3366window.google_analytics_uacct = "UA-882009-7";
3367var _gaq = _gaq || [];
3368_gaq.push(['_setAccount', 'UA-882009-7']);
3369_gaq.push(['_trackPageview']);
3370_gaq.push(['_setAllowAnchor', true]);
3371(function() {
3372 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
3373 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
3374 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
3375})();
3376
3377// twitter
3378!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;
3379 js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
3380
3381// fb
3382(function(d, s, id) {
3383 var js, fjs = d.getElementsByTagName(s)[0];
3384 if (d.getElementById(id)) return;
3385 js = d.createElement(s); js.id = id;
3386 js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=244031665671273";
3387 fjs.parentNode.insertBefore(js, fjs);
3388}(document, 'script', 'facebook-jssdk'));
3389})();