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