1 // Written by Mike Frysinger <vapier@gmail.com>. Released into the public domain.
3 var url_base, user, pass;
5 function fetchpage(url, callback) {
6 url = url_base + '/' + url;
8 var xhr = new XMLHttpRequest();
11 xhr.onreadystatechange = function(state) {
12 if (xhr.readyState == 4) {
13 if (xhr.status == 200) {
18 'Could not connect;<br>check your ' +
19 '<a id="open-settings" href="">settings</a>'
21 document.getElementById('open-settings').onclick = open_settings_page;
22 console.log('connect error', state);
26 xhr.onerror = function(error) {
28 setstatus('onerror; see console');
29 console.log('xhr error:', error);
32 console.log('fetching', url)
33 xhr.withCredentials = true;
34 xhr.open('GET', url, true, user, pass);
35 xhr.responseType = 'document';
36 // The user/pass options above don't seem to work, so do it ourselves.
37 xhr.setRequestHeader('Authorization', 'Basic ' + btoa(user + ':' + pass));
40 setstatus('Exception; see console');
41 console.log('exception:', e);
45 function get_css_var(key) { return getComputedStyle(document.documentElement).getPropertyValue(`--${key}`); }
48 const data = o.toUpperCase();
55 // For example, cycle uses CCL.
60 function toggleit(button) {
61 const outlet_num = button.id.split(':')[0];
62 var old_status = button.data;
63 var new_status = onoff(button.data);
64 var url = 'outlet?' + outlet_num + '=' + new_status;
66 if (!button.id.endsWith('cycle')) {
67 const cycler = document.getElementById(`${button.id}:cycle`);
68 cycler.style.display = new_status === 'ON' ? 'block' : 'none';
71 fetchpage(url, function(xhr, state) {
72 console.log('switch ' + outlet_num + ': ' + old_status + ' -> ' + new_status);
73 if (!button.id.endsWith('cycle')) {
74 button.value = 'Switch ' + old_status;
75 button.data = new_status;
83 function toggle_confirmed() {
84 clearTimeout(this.timeout);
85 this.onclick = toggle_confirm;
89 function toggle_confirm() {
91 this.onclick = toggle_confirmed;
92 this.oldvalue = this.value;
93 this.value = 'Confirm!?';
94 this.timeout = setTimeout(function() {
95 button.value = button.oldvalue;
96 button.onclick = toggle_confirm;
100 /* Toggle the selected theme. */
101 function toggle_theme() {
102 const theme = get_css_var('theme') == 'light' ? 'dark' : 'light';
103 const css = $('link#theme-override');
104 css.href = `css/${theme}.css`;
105 storage.set({theme});
109 function initpopup(xhr, state) {
110 var tbl = document.getElementById('buttons');
111 var row, cell, button;
113 console.log(xhr, state);
115 // There is no clean API for extracting the current state.
119 <th bgcolor="#DDDDFF" align=left>
120 Controller: !!!Web Power Switch 6
125 var th, ths = state.currentTarget.responseXML.querySelectorAll('th');
126 for (var i = 0; th = ths[i]; ++i) {
127 if (th.bgColor != '#DDDDFF')
130 var controller_name = th.innerText.trim();
131 if (controller_name.slice(0, 12) != 'Controller: ')
134 row = tbl.insertRow(-1);
135 cell = row.insertCell(-1);
137 cell.align = 'center';
139 const a = document.createElement('a');
142 a.text = controller_name.slice(12).trim();
145 const button = document.createElement('button');
146 button.name = 'theme';
147 button.onclick = toggle_theme;
148 cell.appendChild(button);
151 var tr, trs = state.currentTarget.responseXML.querySelectorAll('tr');
152 for (var i = 0; tr = trs[i]; ++i) {
153 if (tr.bgColor != '#F4F4F4')
156 var outlet_num = tr.children[0].innerText.trim();
157 var outlet_name = tr.children[1].innerText.trim();
158 var current_status = tr.children[2].innerText.trim();
159 var new_status = tr.children[3].innerText.trim();
160 var confirmable = tr.children[3].children[0].hasAttribute('onclick');
162 row = tbl.insertRow(-1);
163 cell = row.insertCell(-1);
164 if (outlet_name === '')
165 cell.innerHTML = '<i>unnamed</i>';
167 cell.innerText = outlet_name + ':';
168 cell = row.insertCell(-1);
169 button = document.createElement('input');
170 button.type = 'button';
171 button.id = outlet_num;
172 button.value = new_status;
173 button.data = current_status;
174 button.onclick = confirmable ? toggle_confirm : toggle;
175 cell.appendChild(button);
177 // The switch only allows cycling when it's on.
178 cell = row.insertCell(-1);
179 button = document.createElement('input');
180 button.type = 'button';
181 button.id = `${outlet_num}:cycle`;
182 button.value = '🗘';
184 button.title = 'Power cycle';
185 button.onclick = confirmable ? toggle_confirm : toggle;
186 if (current_status.toUpperCase() === 'OFF') {
187 button.style.display = 'none';
189 button.style.fontSize = 'smaller';
190 cell.appendChild(button);
196 function setstatus(msg) {
197 var status = document.getElementById('status');
198 status.innerHTML = msg;
199 status.style.visibility = msg ? '' : 'hidden';
200 status.style.float = msg ? '' : 'left';
201 status.style.position = msg ? '' : 'absolute';
204 function open_settings_page() {
205 chrome.runtime.openOptionsPage();
208 document.addEventListener('DOMContentLoaded', function() {
209 storage.get(settings_keys, function(settings_storage) {
210 const settings = Object.assign({}, settings_defaults, settings_storage);
211 url_base = settings['url'];
212 user = settings['user'];
213 pass = settings['pass'];
214 chrome.permissions.contains({
215 origins: [url_base + '/*']
216 }, function(granted) {
218 fetchpage('index.htm', initpopup);
221 'Missing permissions;<br>please visit the ' +
222 '<a id="open-settings" href="">settings page</a>' +
223 '<br>to grant access.<br>' +
224 '<center><input id=retry type=submit value=Retry></center>'
226 document.getElementById('open-settings').onclick = open_settings_page;
227 // Work around http://crbug.com/125706.
228 document.getElementById('retry').onclick = function() {
229 chrome.permissions.request({origins: [url_base + '/*']});
230 fetchpage('index.htm', initpopup);