]> git.wh0rd.org - chrome-ext/wake-on-lan.git/blame - js/main.js
add support for saving configs
[chrome-ext/wake-on-lan.git] / js / main.js
CommitLineData
5b4622f6
MF
1// Written by Mike Frysinger <vapier@gmail.com>.
2// Released into the public domain.
3
4function status(msg) {
5 $$('[name=status]').innerText = msg;
6}
7
0e1cff99
MF
8function popup_msg(ele, msg) {
9 var popup = $$('[name=popup_msg]');
10 var pos = ele.getBoundingClientRect();
11 popup.innerText = msg;
12 // Might want to add some generalized "center in element" logic.
13 popup.style.top = 5 + pos.top + 'px';
14 popup.style.left = pos.left + 'px';
15 popup.style.display = '';
16 setTimeout(() => { popup.style.display = 'none'; }, 3000);
17}
18
5b4622f6
MF
19// Create a packet following the spec:
20// https://en.wikipedia.org/wiki/Wake-on-LAN#Magic_packet
21function magicpacket(mac, pass) {
22 var data = new ArrayBuffer(6 + 16 * 6 + 6 + 6);
23 var bytes = new Uint8Array(data);
24 var i, j, base = 0;
25
26 // First 6 bytes should be all 0xFF.
27 for (i = 0; i < 6; ++i)
28 bytes[base + i] = 0xff;
29 base += 6;
30
31 // Then the MAC address is repeated 16 times.
32 for (i = 0; i < 6; ++i)
33 for (j = 0; j < 16 * 6; j += 6)
34 bytes[base + j + i] = mac[i];
35 base += 16 * 6;
36
37 // Then 6 bytes before the pass should be 0xFF.
38 for (i = 0; i < 6; ++i)
39 bytes[base + i] = 0xff;
40 base += 6;
41
42 // Finally the 6 bytes of the password.
43 for (i = 0; i < 6; ++i)
44 bytes[base + i] = pass[i];
45
46 return data;
47}
48
49function split_data(v) {
50 var data = Array(6);
51 var i, idata;
52
53 window['sync_' + v]();
54
55 for (i = 0; i < 6; ++i) {
56 idata = $$('input[name=' + v + i + ']');
57 if (!/^[0-9a-fA-F]?[0-9a-fA-F]$/.test(idata.value.replace(' ', ''))) {
58 status(v + ' byte ' + i + ' is invalid; must be 2 hex characters');
59 idata.focus();
60 idata.setSelectionRange(0, idata.value.length);
61 return false;
62 }
63 data[i] = parseInt(idata.value, 16);
64 }
65
66 return data;
67}
68
69function send() {
70 status('initializing');
71
72 var form = $$('form[name=settings]');
73 var shost = '0.0.0.0';
74 var dhost = form.host.value;
75 var port = parseInt(form.port.value);
76
77 // Get the MAC address & password to convert to packet data.
78 var mac = split_data('mac');
79 var pass = split_data('pass');
80 var data = magicpacket(mac, pass);
81 console.log('packet', new Uint8Array(data));
82
83 var checkresult = function(s, step, result) {
84 if (result < 0) {
85 status('error in ' + step + ': ' + net_error_list[result]);
86 chrome.sockets.udp.close(s, nullcb);
87 return false;
88 }
89 return true;
90 };
91
92 // Create the socket ...
93 chrome.sockets.udp.create({}, function(createInfo) {
94 var s = createInfo.socketId;
95
96 console.log('[create] socketInfo', createInfo);
97 status('binding ' + shost);
98
99 // Bind it locally ...
100 chrome.sockets.udp.bind(s, shost, 0, function(result) {
101 console.log('[bind] result', result);
102
103 if (!checkresult(s, 'bind', result))
104 return false;
105
106 status('enabling broadcast');
107
108 // Turn on broadcast support ...
109 chrome.sockets.udp.setBroadcast(s, true, function(result) {
110 console.log('[setBroadcast] result', result);
111
112 if (!checkresult(s, 'broadcast', result))
113 return false;
114
115 status('sending to ' + dhost + ':' + port);
116
117 // Send the backet ...
118 chrome.sockets.udp.send(s, data, dhost, port, function(sendInfo) {
119 console.log('[send] sendInfo', sendInfo);
120
121 if (!checkresult(s, 'send', sendInfo.resultCode))
122 return false;
123
124 status('closing');
125
126 // Shut it down ...
127 chrome.sockets.udp.close(s, function() {
128 status('sent to ' + dhost + ':' + port);
129 store_settings();
130 });
131 });
132 });
133 });
134 });
135
136 // Keep the form from submitting.
137 return false;
138}
139
140function sync_it(v) {
141 var smany = $$('span[name=' + v + '-many]');
142 var sone = $$('span[name=' + v + '-one]');
143
144 // Sync the two sets of fields.
145 var i;
146 if (smany.hidden) {
147 var idata = $$('input[name=' + v + ']');
148 var data = idata.value.split(':');
149
150 if (data.length != 6) {
151 data = idata.value.replace(/[ :]/g, '');
152 if (data.length != 6 * 2) {
153 status('invalid ' + v + '; please fix');
154 return false;
155 }
156 data = data.match(/../g);
157 } else {
158 for (i = 0; i < 6; ++i)
159 if (data[i].length > 2) {
160 status('invalid ' + v + ' please fix');
161 return false;
162 }
163 }
164
165 for (i = 0; i < 6; ++i)
166 $$('input[name=' + v + i + ']').value = data[i];
167 } else {
168 var data = '';
169
170 for (i = 0; i < 6; ++i) {
171 data += $$('input[name=' + v + i + ']').value;
172 if (i < 5)
173 data += ':';
174 }
175
176 $$('input[name=' + v + ']').value = data;
177 }
178}
179function sync_mac() { return sync_it('mac'); }
180function sync_pass() { return sync_it('pass'); }
181
182
183function paste_mac() {
184 sync_mac();
185
186 var smany = $$('span[name=mac-many]');
187 var sone = $$('span[name=mac-one]');
188 smany.hidden = !smany.hidden;
189 sone.hidden = !sone.hidden;
190
191 return false;
192}
193
194function paste_pass() {
195 sync_pass();
196
197 var smany = $$('span[name=pass-many]');
198 var sone = $$('span[name=pass-one]');
199 smany.hidden = !smany.hidden;
200 sone.hidden = !sone.hidden;
201
202 return false;
203}
204
205/*
206 * Storage logic.
207 */
0e1cff99
MF
208
209var computers = [];
210
5b4622f6 211var settings_keys = [
0e1cff99
MF
212 'computers',
213 'last_selected',
214 'theme',
215];
216var old_settings_keys = [
5b4622f6
MF
217 'host',
218 'mac',
219 'pass',
220 'port',
221];
222
e54503ce 223var default_theme = 'dark';
0e1cff99 224var default_name = 'Default';
e54503ce
MF
225var default_host = '192.168.0.255';
226var default_port = '40000';
227var default_mac = '20:00:00:00:00:00';
228var default_pass = '00:00:00:00:00:00';
229
0e1cff99
MF
230/*
231 * Set form data based on selected computer settings.
232 * Uses defaults if not available.
233 */
234function load_computer(idx) {
235 var computer = computers[idx] || {};
236 chrome.storage.local.set({'last_selected': idx});
5b4622f6 237
0e1cff99
MF
238 var form = $$('form[name=settings]');
239
240 form.computer.value = computer['name'] || default_name;
241 form.host.value = computer['host'] || default_host;
242 form.port.value = computer['port'] || default_port;
243 // We assume we only get called during init.
244 paste_mac();
245 form.mac.value = computer['mac'] || default_mac;
246 paste_mac();
247 paste_pass();
248 form.pass.value = computer['pass'] || default_pass;
249 paste_pass();
250}
251
252function load_settings() {
5b4622f6 253 chrome.storage.local.get(settings_keys, function(settings) {
e54503ce 254 set_theme(settings['theme'] || default_theme);
0e1cff99
MF
255 if ('computers' in settings) {
256 computers = settings['computers'] || [];
257 populate_computers();
258 load_computer(settings['last_selected'] || 0);
259 } else {
260 // Migrate old settings.
261 chrome.storage.local.get(old_settings_keys, function(settings) {
262 computers[0] = settings;
263 populate_computers();
264 load_computer(0);
265 store_settings();
266 chrome.storage.local.remove(old_settings_keys);
267 });
268 }
5b4622f6
MF
269 });
270}
271
0e1cff99
MF
272/*
273 * Update the currently selected computer then write out the whole thing.
274 */
5b4622f6 275function store_settings() {
5b4622f6
MF
276 sync_mac();
277 sync_pass();
0e1cff99
MF
278
279 var form = $$('form[name=settings]');
280 var select = $$('select[name=computer]');
281 var idx = select.selectedIndex;
282 computers[idx] = {
283 'name': form.computer.value,
5b4622f6
MF
284 'host': form.host.value,
285 'mac': form.mac.value,
286 'pass': form.pass.value,
287 'port': form.port.value,
288 };
0e1cff99
MF
289
290 chrome.storage.local.set({
291 'computers': computers,
292 });
293}
294
295/*
296 * If they try deleting all entries, make sure we re-add the default.
297 */
298function check_empty_computers(select) {
299 if (select.length == 0) {
300 var option = document.createElement('option');
301 option.text = 'Default';
302 select.add(option, 0);
303 load_computer(0);
304 }
305}
306
307/*
308 * Fill out the computer drop down with existing config options.
309 */
310function populate_computers() {
311 var select = $$('select[name=computer]');
312 select.length = 0;
313
314 for (var i = 0; i < computers.length; i++) {
315 var option = document.createElement('option');
316 var computer = computers[i] || {};
317 option.text = computer['name'] || default_name;
318 select.add(option, i);
319 }
320
321 check_empty_computers(select);
322}
323
324/*
325 * When a computer config is selected, load the corresponding settings.
326 */
327function select_computer() {
328 load_computer($$('select[name=computer]').selectedIndex);
329}
330
331/*
332 * Toggle between the computer drop down & new name input field.
333 */
334function toggle_add_fields(hide_obj, show_obj) {
335 hide_obj.disabled = true;
336 hide_obj.style.display = 'none';
337 show_obj.disabled = false;
338 show_obj.style.display = 'inline';
339 show_obj.focus();
340}
341
342/*
343 * Del curent slected computer
344 */
345function del_computer() {
346 var select = $$('select[name=computer]');
347
348 var idx = select.selectedIndex;
349 // Delete the currently selected index.
350 computers.splice(idx, 1);
351 select.remove(idx);
352
353 // Make sure the list isn't entirely empty now.
354 check_empty_computers(select);
355
356 // Load/select the next entry in the list.
357 if (idx == select.length && idx > 0)
358 --idx;
359 load_computer(idx);
360
361 store_settings();
362}
363
364
365/*
366 * Shows an input box to enter new computer name.
367 */
368function add_computer_start() {
369 var select = $$('select[name=computer]');
370 var text = $$('input[name=computer_name]');
371
372 // If the box isn't visible, show it. Otherwise, they want to
373 // actually add the current settings so create a new entry.
374 if (select.style.display == 'none')
375 add_computer();
376 else
377 toggle_add_fields(select, text);
378}
379
380/*
381 * Wait for the enter key in the add text field.
382 */
383function add_computer_check(e) {
384 if (e.key == 'Enter') {
385 add_computer();
386 e.preventDefault();
387 }
388}
389
390/*
391 * Try to actually create a new computer entry.
392 */
393function add_computer() {
394 var select = $$('select[name=computer]');
395 var form = $$('form[name=settings]');
396 var text = $$('input[name=computer_name]');
397
398 var name = text.value.trim();
399 // Make sure they've added a valid name first.
400 // Options fields don't allow leading/trailing whitespace.
401 if (name == '') {
402 text.value = '';
403 toggle_add_fields(text, select);
404 return;
405 }
406
407 // Make sure they don't try to add a duplicate name.
408 for (var i = 0; i < select.length; ++i) {
409 if (select.options[i].value == name) {
410 popup_msg(text, 'ERROR: computer name already exists!');
411 return;
412 }
413 }
414 text.value = '';
415
416 var option = document.createElement('option');
417 option.text = name;
418 select.add(option, -1);
419
420 // Let the load_computer logic fill out the default values for us.
421 var idx = select.length - 1;
422 computers[idx] = {
423 'name': name,
424 };
425 load_computer(select.length - 1);
426
427 toggle_add_fields(text, select);
428
429 store_settings();
5b4622f6
MF
430}
431
432/*
433 * Startup.
434 */
435window.onload = function() {
0e1cff99
MF
436 $$('input[name=send]').onclick = send;
437 $$('select[name=computer]').onchange = select_computer;
5b4622f6
MF
438 $$('a[name=mac-paste]').onclick = paste_mac;
439 $$('a[name=pass-paste]').onclick = paste_pass;
0e1cff99
MF
440 $$('input[name=del_computer]').onclick = del_computer;
441 $$('input[name=add_computer]').onclick = add_computer_start;
442 $$('input[name=computer_name]').onkeypress = add_computer_check;
6293bf85 443 $$('input[name=theme]').onclick = toggle_theme;
5b4622f6
MF
444
445 load_settings();
446};