-
Notifications
You must be signed in to change notification settings - Fork 3
/
app.js
358 lines (293 loc) · 9.66 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
'use strict';
(function() {
var Tonalhub = function(sounds, backgroundMusic) {
this.sounds = sounds;
this.backgroundMusic = backgroundMusic;
};
Tonalhub.prototype = {
/**
* Array of DOM elements representing the week.
*/
weekDomElements: document.getElementsByClassName('week'),
/**
* Button used to play or stop the repo.
*/
playButton: document.getElementById('playButton'),
/**
* Div used to show errors.
*/
errorContainer: document.getElementById('errors'),
/**
* This interval will be the one used to play the sounds
* of the week's commits every 250 ms.
*/
interval: null,
/**
* Attaches the event listener to the play button.
*/
initialize: function() {
var self = this;
var repositoryInput = document.getElementById('repositoryInput');
var userInput = document.getElementById('userInput');
var user = this.getParameterByName('user');
var repository = this.getParameterByName('repository');
var eventHandler = function() {
self.clearErrors();
if (self.playButton.classList.contains('stop'))
self.stop();
else
self.play();
};
this.playButton.addEventListener('click', eventHandler);
repositoryInput.addEventListener('keypress', function(event) {
if (event.keyCode == 13)
eventHandler();
});
userInput.addEventListener('keypress', function(event) {
if (event.keyCode == 13)
eventHandler();
});
if (user && repository) {
userInput.value = user;
repositoryInput.value = repository;
this.play();
}
},
/**
* Gets the user and repo names and calls the function
* to get the commit activity.
*/
play: function() {
var user = document.getElementById('userInput').value;
var repo = document.getElementById('repositoryInput').value;
if (!user || !repo) {
this.displayError('Make sure you have both user and repository filled in.');
return;
}
this.getCommitActivity(user, repo, this.onCommitActivityFetched, this.onCommitActivityError);
},
/**
* Stops sounds and removes all visual grid cues
* from the current repo.
*/
stop: function() {
this.stopAllSounds();
this.removePlayingVisualCues();
this.backgroundMusic && this.backgroundMusic.stop();
this.playButton.classList.remove('stop');
this.clearQueryString();
if (this.interval)
window.clearInterval(this.interval);
},
/**
* Called once the commit activity is successfully fetched
* from github's API
*/
onCommitActivityFetched: function(commitActivity) {
this.playButton.classList.toggle('stop', true);
this.toggleWaitingMode(false);
this.normalize(commitActivity);
this.displayCommitActivity(commitActivity);
this.playCommitActivity(commitActivity);
},
/**
* Called if the commit activity fetching fails. Retries the
* request if we get a 202, which means github is calculating
* the response.
*/
onCommitActivityError: function(errorCode) {
if (errorCode === 202) {
window.setTimeout((function() { this.play(); }).bind(this), 500);
return;
}
this.playButton.classList.toggle('stop', false);
this.toggleWaitingMode(false);
if (errorCode === 404)
this.displayError('It seems like the user or repository doesn\'t exist :(');
else if (errorCode === 500)
this.displayError('Woops! There are some issues with GitHub\'s API');
else if (errorCode === 403)
this.displayError('We went over the query limit for the Github API :( Please try again later.');
else
this.displayError('Something went wrong with the request :(');
},
/**
* Sometimes the year has 53 weeks, when that happens,
* we only take the 52 most recent ones.
*/
normalize: function(commitActivity) {
if (commitActivity.length > 52)
commitActivity = commitActivity.slice(commitActivity.length - 52, 52);
},
/**
* This fills the grid of the commit activity with the
* active days in green.
*/
displayCommitActivity: function(commitActivity) {
for (var i = 0; i < this.weekDomElements.length; i++) {
var dayDomElements = this.weekDomElements[i].getElementsByClassName('day');
var days = commitActivity[i].days;
for (var j = 0; j < dayDomElements.length; j++)
dayDomElements[j].classList.toggle('active', days[j]);
}
},
/**
* Sets the interval up to play the sound for each week.
*/
playCommitActivity: function(commitActivity) {
var index = 0;
var self = this;
this.backgroundMusic && this.backgroundMusic.play();
this.interval = window.setInterval(function() {
self.stopAllSounds();
for (i = 0; i < self.weekDomElements.length; i++)
self.weekDomElements[i].classList.remove('playing');
if (index === commitActivity.length - 1) {
index++;
return;
}
if (index === commitActivity.length) {
window.clearInterval(self.interval);
self.backgroundMusic && self.backgroundMusic.stop();
self.playButton.classList.toggle('stop', false);
return;
}
var week = commitActivity[index];
var soundsForWeek = self.getSoundsForWeek(week);
self.weekDomElements[index].classList.add('playing');
for (var i = 0; i < soundsForWeek.length; i++)
soundsForWeek[i].play();
index++;
}, 250);
},
/**
* If there are any sounds playing, they will be stopped.
*/
stopAllSounds: function() {
for (var i = 0; i < this.sounds.length; i++)
this.sounds[i].stop();
},
/**
* Removes the green dots in the week grid as well as the
* larger dots in case the repo is being played.
*/
removePlayingVisualCues: function() {
for (i = 0; i < this.weekDomElements.length; i++)
this.weekDomElements[i].classList.remove('playing');
for (var i = 0; i < this.weekDomElements.length; i++) {
var dayDomElements = this.weekDomElements[i].getElementsByClassName('day');
for (var j = 0; j < dayDomElements.length; j++)
dayDomElements[j].classList.remove('active');
}
},
/**
* Returns the sounds that should be played given a specific
* week. For example, a full week will play all 7 sounds, whereas
* an empty week will play none.
*/
getSoundsForWeek: function(week) {
var soundsForWeek = [];
for (var i = 0; i < this.sounds.length; i++)
if (week.days[i])
soundsForWeek.push(this.sounds[i]);
return soundsForWeek;
},
/**
* Performs the AJAX request to get the commit activity from
* github's API.
*/
getCommitActivity: function(user, repository, successCallback, errorCallback) {
this.toggleWaitingMode(true);
var nextUrl = window.location.toString();
nextUrl = this.updateQueryString('user', user, nextUrl);
nextUrl = this.updateQueryString('repository', repository, nextUrl);
window.history.replaceState(null, '', nextUrl);
var url = 'https://alemangui.pythonanywhere.com/api/' + user + '/' + repository;
var httpRequest = new XMLHttpRequest();
var self = this;
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState !== XMLHttpRequest.DONE)
return;
if (httpRequest.status === 200)
(successCallback.bind(self))(JSON.parse(httpRequest.response));
else
(errorCallback.bind(self))(httpRequest.status);
};
httpRequest.open('GET', url, true);
httpRequest.send(null);
},
toggleWaitingMode: function(waiting) {
if (waiting) {
this.playButton.disabled = true;
} else {
this.playButton.disabled = false;
}
},
displayError: function(error) {
this.errorContainer.innerHTML = error;
},
clearErrors: function() {
this.errorContainer.innerHTML = '';
},
/**
* Taken from: http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
*/
getParameterByName: function(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
},
/**
* Taken from: http://stackoverflow.com/questions/5999118/add-or-update-query-string-parameter
*/
updateQueryString: function(key, value, url) {
if (!url) url = window.location.href;
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"),
hash;
if (re.test(url)) {
if (typeof value !== 'undefined' && value !== null)
return url.replace(re, '$1' + key + "=" + value + '$2$3');
else {
hash = url.split('#');
url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
if (typeof hash[1] !== 'undefined' && hash[1] !== null)
url += '#' + hash[1];
return url;
}
}
else {
if (typeof value !== 'undefined' && value !== null) {
var separator = url.indexOf('?') !== -1 ? '&' : '?';
hash = url.split('#');
url = hash[0] + separator + key + '=' + value;
if (typeof hash[1] !== 'undefined' && hash[1] !== null)
url += '#' + hash[1];
return url;
}
else
return url;
}
},
clearQueryString: function() {
var url = window.location.toString().split('?')[0];
window.history.replaceState(null, '', url);
}
};
var backgroundPath = Dolby.checkDDPlus() ? './audio/background_Dolby.mp4' : './audio/background.m4a';
var tonalhub = new Tonalhub([
createSynthSound(880.00),
createSynthSound(659.25),
createSynthSound(523.25),
createSynthSound(440.00),
createSynthSound(329.63),
createSynthSound(220.00),
createSynthSound(164.81)
],
new Pizzicato.Sound({ source: 'file', options: { path: backgroundPath, loop: true, volume: 1.4 }})
);
function createSynthSound(frequency) {
return new Pizzicato.Sound({ source: 'wave', options: { frequency: frequency, sustain: 0.2, volume: 0.4 } });
}
tonalhub.initialize();
})();