]>
Commit | Line | Data |
---|---|---|
50dafc4c MF |
1 | // Written by Mike Frysinger <vapier@gmail.com>. Released into the public domain. Suck it. |
2 | ||
3 | /* Globals to allow easy manipulation via javascript console */ | |
4 | var mpc; | |
5 | var tcpclient; | |
0c661219 | 6 | var refresh_id = NaN; |
50dafc4c MF |
7 | |
8 | function TcpClientSender(tcpclient) { | |
9 | this.tcpclient = tcpclient; | |
10 | } | |
11 | TcpClientSender.prototype.send = function(data, cb) { | |
12 | this.tcpclient.sendMessage(data, cb); | |
13 | } | |
0a3a435e MF |
14 | TcpClientSender.prototype.poll = function() { |
15 | this.tcpclient.poll(); | |
16 | } | |
8d64e317 MF |
17 | TcpClientSender.prototype.reconnect = function() { |
18 | this.tcpclient.disconnect(); | |
19 | this.tcpclient.connect(); | |
20 | } | |
50dafc4c MF |
21 | |
22 | function tramp_mpc_recv(data) { | |
23 | mpc.recv(data); | |
24 | } | |
25 | ||
26 | function sync_storage(sync) { | |
27 | return sync ? chrome.storage.sync : chrome.storage.local; | |
28 | } | |
29 | ||
30 | window.onload = function() { | |
31 | var local_keys = [ | |
32 | 'sync', | |
33 | ]; | |
34 | var sync_keys = [ | |
0c661219 | 35 | 'host', 'port', 'refresh', |
50dafc4c MF |
36 | ]; |
37 | var options = { | |
38 | 'host': '192.168.0.2', | |
39 | 'port': 6600, | |
40 | 'sync': true, | |
0c661219 | 41 | 'refresh': 5, |
50dafc4c MF |
42 | }; |
43 | ||
44 | chrome.storage.local.get(local_keys, function(settings) { | |
45 | local_keys.forEach(function(key) { | |
46 | if (key in settings) | |
47 | options[key] = settings[key] | |
48 | }); | |
49 | ||
50 | var storage = sync_storage(options['sync']); | |
51 | storage.get(sync_keys, function(settings) { | |
52 | sync_keys.forEach(function(key) { | |
53 | if (key in settings) | |
54 | options[key] = settings[key]; | |
55 | }); | |
56 | ||
57 | init_ui(local_keys, sync_keys, options); | |
58 | mpc_connect(); | |
59 | }); | |
60 | }); | |
61 | }; | |
62 | ||
ee26ebe4 | 63 | window.onkeypress = function(e) { |
ccfc12de MF |
64 | if (e.target != document.body) { |
65 | /* Only allow the shortcuts when the focus is on the body. | |
66 | Otherwise you can't type these numbers into text fields. */ | |
67 | return; | |
68 | } | |
69 | ||
ee26ebe4 MF |
70 | switch (e.keyCode) { |
71 | case 49: // 1 | |
72 | show_page('controls'); | |
73 | break; | |
ccfc12de | 74 | case 50: // 2 |
ee26ebe4 MF |
75 | show_page('metadata'); |
76 | break; | |
ccfc12de | 77 | case 51: // 3 |
ee26ebe4 MF |
78 | show_page('playlist'); |
79 | break; | |
ccfc12de | 80 | case 52: // 4 |
ee26ebe4 MF |
81 | show_page('options'); |
82 | break; | |
83 | } | |
84 | }; | |
85 | ||
50dafc4c MF |
86 | function mpc_refresh() { |
87 | mpc.status(); | |
50dafc4c MF |
88 | } |
89 | ||
90 | function mpc_connect(host, port) { | |
91 | if (typeof(host) != 'string') { | |
92 | host = window['opts_host'].value; | |
93 | port = parseInt(window['opts_port'].value); | |
94 | } | |
95 | ||
96 | if (mpc != undefined) { | |
97 | console.log('disconnecting'); | |
98 | update_ui('disconnect'); | |
99 | delete mpc; | |
100 | tcpclient.disconnect(); | |
101 | delete tcpclient; | |
102 | } | |
103 | ||
104 | update_ui('init'); | |
105 | tcpclient = new TcpClient(host, port); | |
1587214e MF |
106 | tcpclient.connect(function(resultCode) { |
107 | if (resultCode < 0) { | |
108 | update_ui('error', resultCode); | |
109 | return; | |
110 | } | |
111 | ||
50dafc4c MF |
112 | var mpc_sender = new TcpClientSender(tcpclient); |
113 | tcpclient.addResponseListener(tramp_mpc_recv); | |
114 | mpc = new Mpc(mpc_sender, update_ui); | |
115 | console.log('connected to ' + host + ':' + port); | |
4ed31ba8 MF |
116 | console.log('protip: use the "mpc" object to poke mpd directly.\n' + |
117 | 'you can also do mpc.set_debug(3) to see traffic'); | |
50dafc4c | 118 | mpc_refresh(); |
0c661219 | 119 | update_refresh_timer(); |
50dafc4c MF |
120 | }); |
121 | } | |
122 | ||
123 | function tramp_mpc_consume() { | |
124 | var val = zo(!getToggleButton(this)); | |
125 | mpc.consume(val); | |
126 | setToggleButton(this, val); | |
127 | } | |
9cb957ed | 128 | function tramp_mpc_deleteid() { mpc.deleteid(this.title); } |
50dafc4c MF |
129 | function tramp_mpc_next() { mpc.next(); } |
130 | function tramp_mpc_pause() { mpc.pause(); } | |
131 | function tramp_mpc_play() { mpc.play(); } | |
132 | function tramp_mpc_previous() { mpc.previous(); } | |
133 | function tramp_mpc_random() { | |
134 | var val = zo(!getToggleButton(this)); | |
135 | mpc.random(val); | |
136 | setToggleButton(this, val); | |
137 | } | |
138 | function tramp_mpc_repeat() { | |
139 | var val = zo(!getToggleButton(this)); | |
140 | mpc.repeat(val); | |
141 | setToggleButton(this, val); | |
142 | } | |
143 | function tramp_mpc_seekcur() { mpc.seekcur(this.value); } | |
144 | function tramp_mpc_setvol() { mpc.setvol(this.value); } | |
145 | function tramp_mpc_single() { | |
146 | var val = zo(!getToggleButton(this)); | |
147 | mpc.single(val); | |
148 | setToggleButton(this, val); | |
149 | } | |
150 | function tramp_mpc_stop() { mpc.stop(); } | |
151 | ||
152 | function zo(val) { | |
153 | return val ? 1 : 0; | |
154 | } | |
155 | function szo(val) { | |
156 | return val == '0' ? 0 : 1; | |
157 | } | |
158 | function getToggleButton(btn) { | |
159 | return btn.style.borderStyle == 'inset'; | |
160 | } | |
161 | function setToggleButton(btn, val) { | |
162 | if (val === undefined) | |
163 | val = !getToggleButton(btn); | |
164 | btn.style.borderStyle = val ? 'inset' : ''; | |
165 | } | |
166 | ||
167 | function show_page(page) { | |
168 | if (typeof(page) != 'string') | |
169 | page = this.id.split('.')[1]; | |
170 | ||
59f1734f | 171 | // We might not be connected in which case 'mpc' will be undefined. |
ea38f3ed MF |
172 | switch (page) { |
173 | case 'playlist': | |
59f1734f MF |
174 | if (mpc) |
175 | mpc.playlistinfo(); | |
ea38f3ed MF |
176 | // Fallthrough. |
177 | case 'metadata': | |
59f1734f MF |
178 | if (mpc) |
179 | mpc.currentsong(); | |
ea38f3ed MF |
180 | break; |
181 | } | |
182 | ||
50dafc4c MF |
183 | var eles = document.getElementsByClassName('main'); |
184 | for (var i = 0; i < eles.length; ++i) { | |
185 | var ele = eles[i]; | |
186 | var dis = 'none'; | |
187 | var cls = ''; | |
188 | if (ele.id == 'main.' + page) { | |
189 | dis = ''; | |
190 | cls = 'selected'; | |
191 | } | |
192 | ele.style.display = dis; | |
193 | document.getElementById('tab.' + ele.id.split('.')[1]).className = cls; | |
194 | } | |
195 | } | |
196 | ||
0c661219 MF |
197 | function do_refresh() { |
198 | mpc_refresh(); | |
199 | refresh_id = window.setTimeout(do_refresh, window['opts_refresh'].value * 1000); | |
200 | } | |
201 | ||
202 | function update_refresh_timer() { | |
2ff2c6c1 | 203 | if (!isNaN(refresh_id)) |
0c661219 MF |
204 | window.clearTimeout(refresh_id); |
205 | var rate = window['opts_refresh'].value * 1000; | |
206 | if (rate > 0) | |
207 | refresh_id = window.setTimeout(do_refresh, rate); | |
208 | } | |
209 | ||
50dafc4c MF |
210 | function update_local_settings() { |
211 | var setting = {}; | |
212 | setting[this.id] = this.checked; | |
213 | chrome.storage.local.set(setting); | |
214 | } | |
215 | ||
216 | function update_sync_settings() { | |
217 | var setting = {}; | |
218 | setting[this.id] = this.value; | |
219 | var storage = sync_storage(window['opts_sync'].checked); | |
220 | storage.set(setting); | |
0c661219 MF |
221 | |
222 | switch (this.id) { | |
223 | case 'refresh': | |
224 | update_refresh_timer(); | |
225 | break; | |
226 | } | |
50dafc4c MF |
227 | } |
228 | ||
229 | function init_ui(local_keys, sync_keys, options) { | |
ee26ebe4 MF |
230 | var ele, i; |
231 | ||
50dafc4c | 232 | /* Setup footer */ |
ee26ebe4 | 233 | i = 1; |
50dafc4c | 234 | [ |
ee26ebe4 | 235 | 'controls', 'metadata', 'playlist', 'options', |
50dafc4c | 236 | ].forEach(function(id) { |
ee26ebe4 MF |
237 | var ele = document.getElementById('tab.' + id); |
238 | ele.onclick = show_page; | |
239 | ele.title = id + ' [' + i + ']'; | |
240 | ++i; | |
50dafc4c MF |
241 | }); |
242 | ||
243 | /* Setup control tab */ | |
244 | ui_mpc_status = document.getElementById('status'); | |
50dafc4c MF |
245 | [ |
246 | 'consume', 'next', 'pause', 'play', 'previous', 'random', 'repeat', | |
247 | 'seekcur', 'setvol', 'single', 'stop', | |
248 | ].forEach(function(id) { | |
249 | var ele = window['ui_mpc_' + id] = document.getElementById(id); | |
ea13f13a | 250 | ele.onchange = ele.onclick = window['tramp_mpc_' + id]; |
50dafc4c | 251 | ele.title = id; |
ee26ebe4 MF |
252 | if (ele.accessKey) |
253 | ele.title += ' [' + ele.accessKey + ']' | |
50dafc4c | 254 | }); |
ea38f3ed | 255 | window['ui_mpc_currtime'] = document.getElementById('currtime'); |
50dafc4c | 256 | |
e9aee305 MF |
257 | /* Setup metadata tab */ |
258 | [ | |
259 | 'album', 'artist', 'date', 'file', 'title', | |
260 | ].forEach(function(id) { | |
261 | window['ui_mpc_metadata_' + id] = document.getElementById('metadata.' + id); | |
262 | }); | |
263 | ||
ea38f3ed MF |
264 | /* Setup playlist tab */ |
265 | window['ui_mpc_playlist'] = document.getElementById('playlist'); | |
266 | ||
50dafc4c MF |
267 | /* Setup options tab */ |
268 | document.getElementById('connect').onclick = mpc_connect; | |
269 | local_keys.forEach(function(id) { | |
270 | var ele = window['opts_' + id] = document.getElementById(id); | |
271 | ele.checked = options[id]; | |
272 | ele.onchange = update_local_settings; | |
273 | }); | |
274 | sync_keys.forEach(function(id) { | |
275 | var ele = window['opts_' + id] = document.getElementById(id); | |
276 | ele.value = options[id]; | |
277 | ele.oninput = update_sync_settings; | |
278 | }); | |
279 | } | |
280 | ||
ea38f3ed MF |
281 | function pretty_time(time) { |
282 | var sec, min, hrs, ret = ''; | |
283 | time = parseInt(time); | |
284 | sec = time % 60; | |
285 | min = parseInt((time / 60) % 60); | |
286 | hrs = parseInt((time / 3600) % 3600); | |
287 | if (hrs) | |
288 | ret = hrs + ':' + ("00" + min).substr(-2) + ':'; | |
289 | else | |
290 | ret = min + ':'; | |
291 | return ret + ("00" + sec).substr(-2); | |
292 | } | |
293 | ||
9cb957ed | 294 | function playlist_del() { |
17ac93ed | 295 | mpc.deleteid(this.song_id); |
9cb957ed MF |
296 | this.parentNode.remove(); |
297 | } | |
298 | ||
17ac93ed MF |
299 | function playlist_play() { |
300 | mpc.playid(this.song_id); | |
301 | this.parentNode.style.fontWeight = 'bold'; | |
302 | } | |
303 | ||
50dafc4c MF |
304 | function update_ui(state, cmd) { |
305 | if (typeof(state) == 'string') { | |
306 | ui_mpc_status.innerText = ({ | |
307 | 'disconnect': 'Disconnecting...', | |
308 | 'init': 'Connecting...', | |
1587214e | 309 | 'error': 'Connection error ' + cmd, |
50dafc4c MF |
310 | })[state]; |
311 | return; | |
312 | } | |
313 | ||
314 | if (Array.isArray(state)) { | |
315 | /* | |
316 | switch (cmd[0]) { | |
317 | case 'setvol': | |
318 | case 'seekcur': | |
319 | break; | |
320 | default: | |
321 | mpc_refresh(); | |
322 | } | |
323 | */ | |
324 | return; | |
325 | } | |
326 | ||
3a55dda4 | 327 | /* Update the metadata tab only when things have changed. */ |
650b9e7b MF |
328 | var currentsong; |
329 | if ('Currentsong' in state) { | |
330 | currentsong = state.Currentsong; | |
331 | if (ui_mpc_metadata_file.lastUpdate != state.Currentsong.lastUpdate) { | |
332 | ui_mpc_metadata_album.innerText = currentsong.Album; | |
333 | ui_mpc_metadata_artist.innerText = currentsong.Artist; | |
334 | ui_mpc_metadata_title.innerText = currentsong.Title; | |
335 | ui_mpc_metadata_date.innerText = currentsong.Date; | |
336 | ui_mpc_metadata_file.innerText = currentsong.file; | |
337 | } | |
3a55dda4 MF |
338 | } |
339 | ||
340 | /* Update the playlist tab only when things have changed. */ | |
341 | if ('Playlist' in state && ui_mpc_playlist.lastUpdate != state.Playlist.lastUpdate) { | |
342 | var playlist = state.Playlist; | |
343 | ||
344 | ui_mpc_playlist.innerHTML = ''; | |
345 | playlist.forEach(function(song) { | |
346 | var cell, row = ui_mpc_playlist.insertRow(-1); | |
650b9e7b | 347 | if (currentsong && song.Pos == currentsong.Pos) |
3a55dda4 MF |
348 | row.style.fontWeight = 'bold'; |
349 | ||
ea38f3ed | 350 | cell = row.insertCell(-1); |
3a55dda4 MF |
351 | cell.id = 'playlist_del'; |
352 | cell.innerHTML = '¤'; | |
353 | cell.song_id = song.Id; | |
354 | cell.title = 'delete'; | |
355 | cell.onclick = playlist_del; | |
356 | ||
357 | cell = row.insertCell(-1); | |
358 | cell.innerText = song.Pos; | |
359 | cell.style.textAlign = 'right'; | |
360 | cell.song_id = song.Id; | |
361 | cell.title = 'play'; | |
362 | cell.onclick = playlist_play; | |
363 | ||
364 | if ('Artist' in song) { | |
365 | row.insertCell(-1).innerText = song.Artist; | |
366 | row.insertCell(-1).innerText = song.Album; | |
367 | row.insertCell(-1).innerText = song.Title; | |
368 | } else { | |
369 | cell = row.insertCell(-1); | |
370 | cell.innerText = song.file; | |
371 | cell.colSpan = 3; | |
372 | } | |
373 | row.insertCell(-1).innerText = pretty_time(song.Time); | |
374 | }); | |
375 | ||
376 | ui_mpc_playlist.lastUpdate = playlist.lastUpdate; | |
377 | } | |
50dafc4c | 378 | |
ea38f3ed | 379 | /* Update the status tab. */ |
999ae517 MF |
380 | var time, percent; |
381 | if ('time' in state) { | |
a7204fc7 MF |
382 | // When stopped, there is no time field at all. |
383 | time = state.time.split(':'); | |
999ae517 MF |
384 | percent = Math.floor((0.0 + time[0]) * 100 / (0.0 + time[1])); |
385 | } else { | |
a7204fc7 | 386 | time = [0, 0]; |
999ae517 MF |
387 | percent = 0; |
388 | } | |
ea38f3ed MF |
389 | ui_mpc_seekcur.max = time[1]; |
390 | ui_mpc_seekcur.value = time[0]; | |
ea38f3ed MF |
391 | ui_mpc_seekcur.title = 'seekcur (' + percent + '%)'; |
392 | ui_mpc_currtime.innerText = [pretty_time(time[0]), pretty_time(time[1]), percent + '%'].join(' / '); | |
92ef8630 | 393 | |
999ae517 MF |
394 | ui_mpc_setvol.title = 'setvol'; |
395 | if ('volume' in state) { | |
396 | ui_mpc_setvol.value = state.volume; | |
397 | ui_mpc_setvol.title += ' (' + state.volume + '%)'; | |
398 | } | |
50dafc4c | 399 | |
50dafc4c MF |
400 | [ |
401 | 'consume', 'random', 'repeat', 'single', | |
402 | ].forEach(function(id) { | |
403 | setToggleButton(window['ui_mpc_' + id], szo(state[id])); | |
404 | }); | |
405 | ||
406 | ui_mpc_status.innerText = ({ | |
407 | 'play': 'Playing', | |
408 | 'pause': 'Paused', | |
409 | 'stop': 'Stopped', | |
410 | })[state.state]; | |
411 | } |