1 (function (window, document) {
3 // we fetch the elements each time because docusaurus removes the previous
4 // element references on page navigation
5 function getElements() {
7 layout: document.getElementById('layout'),
8 menu: document.getElementById('menu'),
9 menuLink: document.getElementById('menuLink')
13 function toggleClass(element, className) {
14 var classes = element.className.split(/\s+/);
15 var length = classes.length;
18 for (; i < length; i++) {
19 if (classes[i] === className) {
24 // The className is not found
25 if (length === classes.length) {
26 classes.push(className);
29 element.className = classes.join(' ');
32 function toggleAll() {
33 var active = 'active';
34 var elements = getElements();
36 toggleClass(elements.layout, active);
37 toggleClass(elements.menu, active);
38 toggleClass(elements.menuLink, active);
41 function handleEvent(e) {
42 var elements = getElements();
44 if (e.target.id === elements.menuLink.id) {
47 } else if (elements.menu.className.indexOf('active') !== -1) {
52 document.addEventListener('click', handleEvent);
54 }(this, this.document));
58 var daynames = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']
62 return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"})
65 function getAnchor() {
66 return window.location.hash;
69 function DrawHeader(project) {
70 var menu_header = document.getElementById('_ui_menu_header')
71 menu_header.innerHTML = project.name
72 document.title = project.name + '/' + project.version
75 function ParseJsonQ(url, callback) {
76 var req = new XMLHttpRequest();
78 req.onreadystatechange = function () {
79 if (this.readyState != 4) return;
80 if (this.status != 200 && this.status != 500 && this.status != 404) {
81 setTimeout(ParseJsonQ(url, callback),30000);
84 var json = JSON.parse(this.responseText)
88 req.open("GET", url, true);
93 function UpdateElement(id, value) {
94 var element = document.getElementById("_ui_element_"+id)
96 var ui_class = element.dataset.ui_class;
99 element.innerText = value
106 element.value = value
109 element.checked = value
112 for (i=0; i<7; i++) {
113 var subname = '_ui_elpart_'+i+'_'+id
114 var subelement = document.getElementById(subname)
116 subelement.className = "weekday-selected"
118 subelement.className = "weekday"
125 function UpdateValues(json) {
126 for (var key in json) {
127 var obj = document.getElementById("_ui_element_"+key)
129 UpdateElement(key, json[key])
131 parameters[key] = json[key]
133 var notification = document.getElementById('_ui_notification');
134 if (parameters['_changed']) {
135 notification.innerHTML = '<input style="margin:1em .5em 0;" type="button" id="save" value="Сохранить внесенные изменения" class="pure-button pure-button-primary" onclick="sendAction(\'save\')">'
136 notification.removeAttribute('hidden')
138 notification.innerHTML = ''
139 notification.hidden = true
143 function sendUpdate(id) {
144 var input = document.getElementById('_ui_element_'+id)
145 var ui_class = input.dataset.ui_class;
151 if (input.checkValidity() && input.value != parameters[id]) {
152 ParseJsonQ('/config/set?name=' + id + '&value=' + encodeURIComponent(input.value), function(json) {
158 ParseJsonQ('/config/set?name=' + id + '&value=' + encodeURIComponent(input.selectedOptions[0].value), function(json) {
163 ParseJsonQ('/config/set?name=' + id + '&value=' + (input.checked?'true':'false'), function(json) {
168 ParseJsonQ('/config/set?name=' + id + '&value=' + input.dataset.value, function(json) {
175 function sendAction(name) {
176 ParseJsonQ('/action?name=' + name, function(json) {
177 if (json.result == 'FAILED') {
188 function ShowPwd(id) {
189 var x = document.getElementById('_ui_element_' + id)
190 if (x.type === "password") {
197 function OpenSelect(id) {
198 var selector = document.getElementById('_ui_elemmodal_'+id);
199 selector.removeAttribute("hidden")
202 function CloseSelect(id) {
203 var selector = document.getElementById('_ui_elemmodal_'+id);
204 selector.hidden = true
207 function SelectWiFi(id, ssid) {
209 var x = document.getElementById('_ui_element_' + id)
210 UpdateElement(x,ssid);
214 function getWiFi(id) {
215 var list = document.getElementById('_ui_elemselect_'+id)
217 var req = new XMLHttpRequest();
219 req.onreadystatechange = function () {
220 if (this.readyState != 4) return;
221 if (this.status != 200 && this.status != 500 && this.status != 404) {
222 setTimeout(getWiFi(id),30000);
225 var json = JSON.parse(this.responseText)
226 var table = '<table cellpadding="5" border="0" align="center"><thead class="table-header"><tr><td style="padding: 1rem">SSID</td><td style="padding: 1rem">BSSID</td><td style="padding: 1rem">RSSI</td><td style="padding: 1rem">Канал</td><td style="padding: 1rem">Защита</td></tr></thead></tbody style="border-bottom: lightgrey 1px solid">'
228 setTimeout(getWiFi(id),5000);
231 var encryption = json[idx].secure == 2? "TKIP" : json[idx].secure == 5? "WEP" : json[idx].secure == 4? "CCMP" : json[idx].secure == 7? "нет" : json[idx].secure == 8? "Автоматически" : "Не определено";
232 table += '<tr onclick="SelectWiFi(\''+id+'\',\''+json[idx].ssid+'\')"><td style="padding: 1rem">'+json[idx].ssid+'</td><td style="padding: 1rem">'+json[idx].bssid+'</td><td style="padding: 1rem">'+json[idx].rssi+'</td><td style="padding: 1rem">'+json[idx].channel+'</td><td style="padding: 1rem">'+encryption+'</td></tr>'
235 table += '</tbody></table>'
236 list.innerHTML = table;
239 req.open("GET", "/wifi/scan", true);
244 function ClickDay(id, day) {
245 value = parameters[id].split('')
246 day_value = value[day]
247 day_value = (day_value=='0')?'1':'0'
248 value[day] = day_value
249 element = document.getElementById('_ui_element_' + id)
250 value = value.join('')
251 element.dataset.value = value;
255 function ElementHTML(element) {
257 if (parameters[element.id] || !isNaN(parameters[element.id])) {
258 value = parameters[element.id]
259 } else if (element.value) {
260 value = element.value
264 switch (element.type) {
266 return '<div class="pure-u-1 pure-u-md-1-3"><hr></div>'
268 return '<div class="pure-u-1 pure-u-md-1-3"><div align="center"><input style="margin:1em .5em 0;" type="button" id="'
269 + element.id + '" value="' + encode(element.label) + '" class="pure-button pure-button-primary" onclick="sendAction(\'' + element.id + '\')" /></div></div>'
271 return '<div class="pure-u-1 pure-u-md-1-3"><label for="_ui_element_' + element.id + '">' + encode(element.label) + '</label>'
272 + '<input data-ui_class="password" type="password" id="_ui_element_' + element.id + '" value="' + encode(value)
273 + '" class="pure-u-1" style="display:inline-block" maxlength="99" oninput="sendUpdate(\'' + element.id + '\')" />'
274 + '<div class="hint" onclick="ShowPwd(\'' + element.id + '\')">👁</div></div>'
277 if (element.pattern) {
278 pattern = ' pattern="' + encode(element.pattern) + '"'
280 return '<div class="pure-u-1 pure-u-md-1-3"><label for="_ui_element_' + element.id + '">' + encode(element.label) + '</label>'
281 + '<input data-ui_class="input" type="text" id="_ui_element_' + element.id + '" value="' + encode(value) +'"'+ pattern
282 + ' class="pure-u-1" maxlength="99" oninput="sendUpdate(\'' + element.id + '\')" /></div>'
285 if (element.pattern) {
286 pattern = ' pattern="' + encode(element.pattern) + '"'
288 return '<div class="pure-u-1 pure-u-md-1-3"><label for="_ui_element_' + element.id + '">' + encode(element.label) + '</label>'
289 + '<input data-ui_class="input" type="text" id="_ui_element_' + element.id + '" value="' + encode(value) +'"'+ pattern
290 + ' class="pure-u-1" style="display:inline-block" maxlength="99" oninput="sendUpdate(\'' + element.id + '\')" />'
291 + '<div class="hint" onclick="OpenSelect(\'' + element.id+ '\'); getWiFi(\'' + element.id + '\')">📶</div>'
292 + '<div class="modal" id="_ui_elemmodal_' + element.id + '" hidden>'
293 + '<div class="modal-content">'
294 + '<div id="_ui_elemselect_' + element.id + '"></div>'
295 + '<div class="pure-u-1 pure-u-md-1-3"><div align="center"><input style="margin:1em .5em 0;" type="button" id="_ui_button_'
296 + element.id + '" value="Закрыть" class="pure-button pure-button-primary" onclick="CloseSelect(\'' + element.id + '\')"></div>'
297 + '</div></div></div>'
299 return '<div class="pure-u-1 pure-u-md-1-3"><label class="switch socket" for="_ui_element_' + element.id + '">'
300 + '<input class="switch" data-ui_class="checkbox" type="checkbox" id="_ui_element_' + element.id + '"' + (parameters[element.id]?' checked':'') + ' onchange="sendUpdate(\'' + element.id + '\')" />'
301 + '<span class="switch slider">'+ encode(element.label) + '</span>'
304 var options = '<div class="pure-u-1 pure-u-md-1-3"><label for="_ui_element_' + element.id + '">' + encode(element.label) + '</label>'
305 + '<select class="pure-u-24-24" data-ui_class="select" id="_ui_element_' + element.id + '" onchange="sendUpdate(\'' + element.id + '\')">'
306 for (const option of element.options) {
307 var list_option = '<option value="' + encode(option.value) + '" ';
308 if (option.value == parameters[element.id]) {
309 list_option += 'selected '
311 list_option += '>' + encode(option.text) + '</option>'
312 options += list_option
314 options += '</select></div>'
317 days = '<div class="pure-u-1 pure-u-md-1-3"><label for="_ui_element_' + element.id + '">' + encode(element.label) + '</label>'
318 + '<table data-ui_class="week" data-value="' + value + '" id="_ui_element_' + element.id + '" cellpadding="5" border="0" align="left"><tbody><tr>'
319 for (i=0; i<7; i++) {
320 a_enabled = (value[i] == "1")
321 days = days + '<td><div class="weekday' + (a_enabled?"-selected":"") + '" id="_ui_elpart_'+i+'_'+element.id+'" onclick="ClickDay(\'' + element.id + '\', ' + i + ')">'+ daynames[i] + '</div></td>'
323 days += '</tr></tbody></table></div>'
326 return '<div class="pure-u-1 pure-u-md-1-3"><h2 id="_ui_element_'+ element.id +'" ' + (element.color?'style="color:'+ element.color+'" ':'')+ '>' + encode(value) + '</h2></div>'
328 return '<div class="pure-u-1 pure-u-md-1-3"><label for="_ui_element_' + element.id + '">' + encode(element.label) + '</label>'
329 + '<input data-ui_class="number" type="number" '+ (!isNaN(element.min)?'min="'+element.min+'" ':'') + (!isNaN(element.max)?'max="'+element.max+'" ':'') + (!isNaN(element.step)?'step="'+element.step+'" ':'')
330 + 'id="_ui_element_' + element.id + '" value="' + encode(value) +'"'+ pattern
331 + ' class="pure-u-1" maxlength="99" oninput="sendUpdate(\'' + element.id + '\')" /></div>'
333 return '<div class="pure-u-1 pure-u-md-1-3"><label for="_ui_element_' + element.id + '">' + encode(element.label) + '</label>'
334 + '<input data-ui_class="range" type="range" '+ (!isNaN(element.min)?'min="'+element.min+'" ':'') + (!isNaN(element.max)?'max="'+element.max+'" ':'') + (!isNaN(element.step)?'step="'+element.step+'" ':'')
335 + 'id="_ui_element_' + element.id + '" value="' + encode(value) +'"'+ pattern
336 + ' class="pure-u-1" maxlength="99" oninput="sendUpdate(\'' + element.id + '\')" /></div>'
339 return '<div class="pure-u-1 pure-u-md-1-3"><table cellpadding="5" border="0" align="center"><tbody><tr><td style="padding-right: 10px; width: 50%;" align="right"><pre>'
340 + encode(element.label)+ '</pre></td><td style="padding-left: 10px;" id="cT"><pre id="_ui_element_'
341 + element.id + '" data-ui_class="table" ' + (element.color?'style="color:'+ element.color+'" ':'')+ '>' + encode(value) + '</pre></td></tr></tbody></table></div>'
345 function DrawPage(id) {
347 for (const page of pages) {
348 var menu_link = document.getElementById('_ui_pglink_' + page.id)
350 menu_link.classList.remove("pure-menu-selected")
352 menu_link.classList.add("pure-menu-selected")
357 var page_header = document.getElementById("_ui_page_header")
358 page_header.innerHTML = pages[idx].title
359 var page_content = document.getElementById("_ui_page_content");
361 for (const element of pages[idx].elements) {
362 content = content + ElementHTML(element) + '\n'
364 page_content.innerHTML = content
365 window.location.hash = id
368 function DrawNavigator(project, pages) {
369 var menu = document.getElementById('_ui_menu_list');
371 for (const page of pages) {
372 list = list + '<li id="_ui_pglink_' + page.id
373 + '" class="pure-menu-item"><a class="pure-menu-link" onclick="DrawPage(\''+ page.id +'\')" href="#' + page.id + '">'
374 + page.title+'</a></li>'
376 menu.innerHTML = list
379 function DrawContacts(contacts) {
380 if (!contacts) return;
381 var contact_list = '<hr><h4 class="pure-u1">Контакты</h4>'
382 for (const contact of contacts) {
383 const url = new URL(contact)
386 switch (url.protocol) {
389 ref = '⌂ '+url.hostname
392 ref = '✉ '+url.pathname
395 ref = '🖅 '+url.pathname
397 contact_list += '<a href="'+contact+'">'+ref+'</a>'
399 var footer = document.getElementById('_ui_contacts');
400 footer.innerHTML = contact_list
403 function DrawUI(ui) {
404 DrawHeader(ui.project)
406 DrawNavigator(ui.project, pages)
407 DrawContacts(ui.project.contacts)
408 var anchor = getAnchor()
412 DrawPage(pages[0].id)
417 ParseJsonQ("/ui", DrawUI);
423 if (!!window.EventSource) {
424 var source = new EventSource('/events');
426 source.onerror = function(e) {
427 if (source.readyState == 2) {
428 setTimeout(initES, 5000);
432 source.addEventListener('keepalive', function(e) {
433 UpdateValues(JSON.parse(e.data));
436 source.addEventListener('update', function(e) {
437 UpdateValues(JSON.parse(e.data));
444 function DrawConfig(cfg) {
449 ParseJsonQ("/config/get", DrawConfig);