Files
squeezelite-esp32/components/wifi-manager/webapp/.eslintcache
Sebastien L 15c0e47ae3 Network WIP
2022-01-20 13:43:23 -05:00

1 line
67 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[{"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\custom.js":"1"},{"size":62870,"mtime":1641847593736,"results":"2","hashOfConfig":"3"},{"filePath":"4","messages":"5","errorCount":1,"fatalErrorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"6","usedDeprecatedRules":"7"},"7z2b57","C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\custom.js",["8"],"import he from 'he';\r\nimport { Promise } from 'es6-promise';\r\n\r\nif (!String.prototype.format) {\r\n Object.assign(String.prototype, {\r\n format() {\r\n const args = arguments;\r\n return this.replace(/{(\\d+)}/g, function(match, number) {\r\n return typeof args[number] !== 'undefined' ? args[number] : match;\r\n });\r\n }, \r\n }); \r\n}\r\nif (!String.prototype.encodeHTML) {\r\n Object.assign(String.prototype, {\r\n encodeHTML() {\r\n return he.encode(this).replace(/\\n/g, '<br />')\r\n },\r\n });\r\n}\r\nObject.assign(Date.prototype, {\r\n toLocalShort() {\r\n const opt = { dateStyle: 'short', timeStyle: 'short' };\r\n return this.toLocaleString(undefined, opt);\r\n },\r\n});\r\n\r\n\r\nconst nvsTypes = {\r\n NVS_TYPE_U8: 0x01,\r\n\r\n /*! < Type uint8_t */\r\n NVS_TYPE_I8: 0x11,\r\n\r\n /*! < Type int8_t */\r\n NVS_TYPE_U16: 0x02,\r\n\r\n /*! < Type uint16_t */\r\n NVS_TYPE_I16: 0x12,\r\n\r\n /*! < Type int16_t */\r\n NVS_TYPE_U32: 0x04,\r\n\r\n /*! < Type uint32_t */\r\n NVS_TYPE_I32: 0x14,\r\n\r\n /*! < Type int32_t */\r\n NVS_TYPE_U64: 0x08,\r\n\r\n /*! < Type uint64_t */\r\n NVS_TYPE_I64: 0x18,\r\n\r\n /*! < Type int64_t */\r\n NVS_TYPE_STR: 0x21,\r\n\r\n /*! < Type string */\r\n NVS_TYPE_BLOB: 0x42,\r\n\r\n /*! < Type blob */\r\n NVS_TYPE_ANY: 0xff /*! < Must be last */,\r\n};\r\nconst btIcons = {\r\n bt_playing: 'play-circle-fill',\r\n bt_disconnected: 'bluetooth-fill',\r\n bt_neutral: '',\r\n bt_connected: 'bluetooth-connect-fill',\r\n bt_disabled: '',\r\n play_arrow: 'play-circle-fill',\r\n pause: 'pause-circle-fill',\r\n stop: 'stop-circle-fill',\r\n '': '',\r\n};\r\n\r\nconst btStateIcons = [\r\n { desc: 'Idle', sub: ['bt_neutral'] },\r\n { desc: 'Discovering', sub: ['bt_disconnected'] },\r\n { desc: 'Discovered', sub: ['bt_disconnected'] },\r\n { desc: 'Unconnected', sub: ['bt_disconnected'] },\r\n { desc: 'Connecting', sub: ['bt_disconnected'] },\r\n { \r\n desc: 'Connected',\r\n sub: ['bt_connected', 'play_arrow', 'bt_playing', 'pause', 'stop'],\r\n },\r\n { desc: 'Disconnecting', sub: ['bt_disconnected'] },\r\n];\r\n\r\nconst pillcolors = {\r\n MESSAGING_INFO: 'badge-success',\r\n MESSAGING_WARNING: 'badge-warning',\r\n MESSAGING_ERROR: 'badge-danger',\r\n};\r\nconst connectReturnCode = {\r\n UPDATE_CONNECTION_OK : 0, \r\n\tUPDATE_FAILED_ATTEMPT : 1,\r\n\tUPDATE_USER_DISCONNECT : 2,\r\n UPDATE_LOST_CONNECTION : 3,\r\n UPDATE_FAILED_ATTEMPT_AND_RESTORE : 4\r\n}\r\nconst taskStates = {\r\n 0: 'eRunning',\r\n /*! < A task is querying the state of itself, so must be running. */\r\n 1: 'eReady',\r\n /*! < The task being queried is in a read or pending ready list. */\r\n 2: 'eBlocked',\r\n /*! < The task being queried is in the Blocked state. */\r\n 3: 'eSuspended',\r\n /*! < The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */\r\n 4: 'eDeleted',\r\n};\r\nconst flash_status_codes = {\r\n NONE : 0,\r\n REBOOT_TO_RECOVERY: 2,\r\n SET_FWURL: 5,\r\n FLASHING: 6,\r\n DONE: 7,\r\n UPLOADING: 8,\r\n ERROR: 9\r\n};\r\nlet flash_state=flash_status_codes.FLASH_NONE;\r\nlet flash_ota_dsc='';\r\nlet flash_ota_pct=0;\r\nlet older_recovery=false;\r\nlet presetsloaded=false;\r\nfunction isFlashExecuting(data){\r\n return (flash_state!=flash_status_codes.UPLOADING ) && (data.ota_dsc!='' || data.ota_pct>0);\r\n}\r\nfunction post_config(data){\r\n let confPayload={\r\n timestamp: Date.now(),\r\n config : data\r\n };\r\n $.ajax({\r\n url: '/config.json',\r\n dataType: 'text',\r\n method: 'POST',\r\n cache: false,\r\n contentType: 'application/json; charset=utf-8',\r\n data: JSON.stringify(confPayload),\r\n error: handleExceptionResponse,\r\n });\r\n}\r\nfunction process_ota_event(data){\r\n if(data.ota_dsc){\r\n flash_ota_dsc=data.ota_dsc;\r\n }\r\n if( data.ota_pct != undefined){\r\n flash_ota_pct=data.ota_pct;\r\n }\r\n \r\n if(flash_state==flash_status_codes.ERROR){\r\n return;\r\n }\r\n else if(isFlashExecuting(data)){\r\n flash_state=flash_status_codes.FLASHING;\r\n } \r\n else if(flash_state==flash_status_codes.FLASHING ){\r\n if(flash_ota_pct ==100){\r\n // we were processing OTA, and we've reached 100%\r\n flash_state=flash_status_codes.DONE;\r\n $('#flashfilename').val('');\r\n } \r\n else if(flash_ota_pct<0 && older_recovery){\r\n // we were processing OTA on an older recovery and we missed the \r\n // end of flashing.\r\n console.log('End of flashing from older recovery');\r\n if(data.ota_dsc==''){\r\n flash_ota_dsc = 'OTA Process Completed';\r\n }\r\n flash_state=flash_status_codes.DONE;\r\n }\r\n }\r\n else if(flash_state ==flash_status_codes.UPLOADING){ \r\n if(flash_ota_pct ==100){\r\n // we were processing OTA, and we've reached 100%\r\n // reset the progress bar \r\n flash_ota_pct = 0;\r\n flash_state=flash_status_codes.FLASHING;\r\n } \r\n }\r\n}\r\nfunction set_ota_error(message){\r\n flash_state=flash_status_codes.ERROR;\r\n handle_flash_state({\r\n ota_pct: 0,\r\n ota_dsc: message,\r\n event: flash_events.SET_ERROR\r\n }); \r\n}\r\nfunction show_update_dialog(){\r\n $('#otadiv').modal();\r\n if (flash_ota_pct >= 0) {\r\n update_progress();\r\n }\r\n if (flash_ota_dsc !== '') {\r\n $('span#flash-status').html(flash_ota_dsc);\r\n }\r\n}\r\nconst flash_events={\r\n SET_ERROR: function(data){\r\n if(data.ota_dsc){\r\n flash_ota_dsc=data.ota_dsc;\r\n }\r\n else {\r\n flash_ota_dsc = 'Error';\r\n }\r\n flash_ota_pct=data.ota_pct??0;\r\n $('#fwProgressLabel').parent().addClass('bg-danger');\r\n update_progress();\r\n show_update_dialog();\r\n },\r\n START_OTA : function() {\r\n if (flash_state == flash_status_codes.NONE || flash_state == flash_status_codes.ERROR || flash_state == undefined) {\r\n $('#fwProgressLabel').parent().removeClass('bg-danger');\r\n flash_state=flash_status_codes.REBOOT_TO_RECOVERY;\r\n if(!recovery){\r\n flash_ota_dsc = 'Starting recovery mode...';\r\n // Reboot system to recovery mode\r\n const data = {\r\n timestamp: Date.now(),\r\n };\r\n\r\n $.ajax({\r\n url: '/recovery.json',\r\n dataType: 'text',\r\n method: 'POST',\r\n cache: false,\r\n contentType: 'application/json; charset=utf-8',\r\n data: JSON.stringify(data),\r\n error: function(xhr, _ajaxOptions, thrownError){\r\n set_ota_error(`Unexpected error while trying to restart to recovery. (status=${xhr.status??''}, error=${thrownError??''} ) `);\r\n },\r\n complete: function(response) {\r\n console.log(response.responseText);\r\n },\r\n }); \r\n }\r\n else {\r\n flash_ota_dsc='Starting Update';\r\n }\r\n show_update_dialog();\r\n \r\n }\r\n else {\r\n console.warn('Unexpected status while starting flashing');\r\n } \r\n },\r\n FOUND_RECOVERY: function(data) {\r\n console.log(JSON.stringify(data));\r\n const url=$('#fw-url-input').val();\r\n if(flash_state == flash_status_codes.REBOOT_TO_RECOVERY){\r\n const fileInput = $('#flashfilename')[0].files;\r\n if (fileInput.length > 0) {\r\n flash_ota_dsc = 'Sending file to device.';\r\n flash_state= flash_status_codes.UPLOADING;\r\n const uploadPath = '/flash.json';\r\n const xhttp = new XMLHttpRequest();\r\n // xhrObj.upload.addEventListener(\"loadstart\", loadStartFunction, false); \r\n xhttp.upload.addEventListener(\"progress\", progressFunction, false); \r\n //xhrObj.upload.addEventListener(\"load\", transferCompleteFunction, false); \r\n xhttp.onreadystatechange = function() {\r\n if (xhttp.readyState === 4) {\r\n if(xhttp.status === 0 || xhttp.status === 404) {\r\n set_ota_error(`Upload Failed. Recovery version might not support uploading. Please use web update instead.`);\r\n $('#flashfilename').val('');\r\n }\r\n }\r\n };\r\n xhttp.open('POST', uploadPath, true);\r\n xhttp.send(fileInput[0] );\r\n }\r\n else if(url==''){\r\n flash_state= flash_status_codes.NONE;\r\n }\r\n else {\r\n flash_ota_dsc = 'Saving firmware URL location.';\r\n flash_state= flash_status_codes.SET_FWURL;\r\n let confData= { fwurl: {\r\n value: $('#fw-url-input').val(),\r\n type: 33,\r\n }\r\n };\r\n post_config(confData); \r\n }\r\n show_update_dialog();\r\n }\r\n },\r\n PROCESS_OTA_UPLOAD: function(data){\r\n flash_state= flash_status_codes.UPLOADING;\r\n process_ota_event(data);\r\n show_update_dialog();\r\n },\r\n PROCESS_OTA_STATUS: function(data){\r\n if(data.ota_pct>0){\r\n older_recovery = true;\r\n }\r\n if(flash_state == flash_status_codes.REBOOT_TO_RECOVERY){\r\n data.event = flash_events.FOUND_RECOVERY;\r\n handle_flash_state(data);\r\n }\r\n else if(flash_state==flash_status_codes.DONE && !recovery){\r\n flash_state=flash_status_codes.NONE;\r\n $('#rTable tr.release').removeClass('table-success table-warning');\r\n $('#fw-url-input').val('');\r\n }\r\n\r\n else {\r\n process_ota_event(data);\r\n if(flash_state && (flash_state >flash_status_codes.NONE && flash_ota_pct>=0) ) {\r\n show_update_dialog();\r\n }\r\n } \r\n },\r\n PROCESS_OTA: function(data) {\r\n process_ota_event(data);\r\n if(flash_state && (flash_state >flash_status_codes.NONE && flash_ota_pct>=0) ) {\r\n show_update_dialog();\r\n }\r\n }\r\n};\r\nwindow.hideSurrounding = function(obj){\r\n $(obj).parent().parent().hide();\r\n}\r\nfunction update_progress(){\r\n $('.progress-bar')\r\n .css('width', flash_ota_pct + '%')\r\n .attr('aria-valuenow', flash_ota_pct)\r\n .text(flash_ota_pct+'%')\r\n $('.progress-bar').html((flash_state==flash_status_codes.DONE?100:flash_ota_pct) + '%');\r\n\r\n}\r\nfunction handle_flash_state(data) {\r\n if(data.event) {\r\n data.event(data);\r\n } \r\n else {\r\n console.error('Unexpected error while processing handle_flash_state');\r\n return;\r\n }\r\n\r\n}\r\nwindow.hFlash = function(){\r\n // reset file upload selection if any;\r\n $('#flashfilename').val('');\r\n handle_flash_state({ event: flash_events.START_OTA, url: $('#fw-url-input').val() });\r\n}\r\nwindow.handleReboot = function(link){\r\n \r\n if(link=='reboot_ota'){\r\n $('#reboot_ota_nav').removeClass('active').prop(\"disabled\",true); delayReboot(500,'', 'reboot_ota');\r\n }\r\n else {\r\n $('#reboot_nav').removeClass('active'); delayReboot(500,'',link);\r\n }\r\n}\r\nfunction progressFunction(evt){ \r\n // if (evt.lengthComputable) { \r\n // progressBar.max = evt.total; \r\n // progressBar.value = evt.loaded; \r\n // percentageDiv.innerHTML = Math.round(evt.loaded / evt.total * 100) + \"%\"; \r\n // } \r\n handle_flash_state({\r\n ota_pct: ( Math.round(evt.loaded / evt.total * 100)),\r\n ota_dsc: ('Uploading file to device'),\r\n event: flash_events.PROCESS_OTA_UPLOAD\r\n }); \r\n} \r\nfunction handlebtstate(data) {\r\n let icon = '';\r\n let tt = '';\r\n if (data.bt_status !== undefined && data.bt_sub_status !== undefined) {\r\n const iconsvg = btStateIcons[data.bt_status].sub[data.bt_sub_status];\r\n if (iconsvg) {\r\n icon = `#${btIcons[iconsvg]}`;\r\n tt = btStateIcons[data.bt_status].desc;\r\n } else {\r\n icon = `#${btIcons.bt_connected}`;\r\n tt = 'Output status';\r\n }\r\n }\r\n $('#o_type').title = tt;\r\n $('#o_bt').attr('xlink:href',icon);\r\n\r\n \r\n}\r\nfunction handleTemplateTypeRadio(outtype) {\r\n if (outtype === 'bt') {\r\n $('#bt').prop('checked', true);\r\n $('#o_bt').attr('display', 'inline');\r\n $('#o_spdif').attr('display', 'none');\r\n $('#o_i2s').attr('display', 'none');\r\n output = 'bt';\r\n } else if (outtype === 'spdif') {\r\n $('#spdif').prop('checked', true);\r\n $('#o_bt').attr('display', 'none');\r\n $('#o_spdif').attr('display', 'inline');\r\n $('#o_i2s').attr('display', 'none');\r\n output = 'spdif';\r\n } else {\r\n $('#i2s').prop('checked', true);\r\n $('#o_bt').attr('display', 'none');\r\n $('#o_spdif').attr('display', 'none');\r\n $('#o_i2s').attr('display', 'inline');\r\n output = 'i2s';\r\n }\r\n}\r\n\r\nfunction handleExceptionResponse(xhr, _ajaxOptions, thrownError) {\r\n console.log(xhr.status);\r\n console.log(thrownError);\r\n if (thrownError !== '') {\r\n showLocalMessage(thrownError, 'MESSAGING_ERROR');\r\n }\r\n}\r\nfunction HideCmdMessage(cmdname) {\r\n $('#toast_' + cmdname).css('display', 'none');\r\n $('#toast_' + cmdname)\r\n .removeClass('table-success')\r\n .removeClass('table-warning')\r\n .removeClass('table-danger')\r\n .addClass('table-success');\r\n $('#msg_' + cmdname).html('');\r\n}\r\nfunction showCmdMessage(cmdname, msgtype, msgtext, append = false) {\r\n let color = 'table-success';\r\n if (msgtype === 'MESSAGING_WARNING') {\r\n color = 'table-warning';\r\n } else if (msgtype === 'MESSAGING_ERROR') {\r\n color = 'table-danger';\r\n }\r\n $('#toast_' + cmdname).css('display', 'block');\r\n $('#toast_' + cmdname)\r\n .removeClass('table-success')\r\n .removeClass('table-warning')\r\n .removeClass('table-danger')\r\n .addClass(color);\r\n let escapedtext = msgtext\r\n .substring(0, msgtext.length - 1)\r\n .encodeHTML()\r\n .replace(/\\n/g, '<br />');\r\n escapedtext =\r\n ($('#msg_' + cmdname).html().length > 0 && append\r\n ? $('#msg_' + cmdname).html() + '<br/>'\r\n : '') + escapedtext;\r\n $('#msg_' + cmdname).html(escapedtext);\r\n}\r\n\r\nlet releaseURL =\r\n 'https://api.github.com/repos/sle118/squeezelite-esp32/releases';\r\n \r\nlet recovery = false;\r\nconst commandHeader = 'squeezelite -b 500:2000 -d all=info -C 30 -W';\r\nlet blockAjax = false;\r\n//let blockFlashButton = false;\r\nlet apList = null;\r\n//let selectedSSID = '';\r\n//let checkStatusInterval = null;\r\nlet messagecount = 0;\r\nlet messageseverity = 'MESSAGING_INFO';\r\nlet StatusIntervalActive = false;\r\nlet LastRecoveryState = null;\r\nlet SystemConfig={};\r\nlet LastCommandsState = null;\r\nvar output = '';\r\nlet hostName = '';\r\nlet versionName='Squeezelite-ESP32';\r\nlet prevmessage='';\r\nlet project_name=versionName;\r\nlet platform_name=versionName;\r\nlet btSinkNamesOptSel='#cfg-audio-bt_source-sink_name';\r\nlet ConnectedToSSID={};\r\nlet ConnectingToSSID={};\r\nlet lmsBaseUrl;\r\nlet prevLMSIP='';\r\nconst ConnectingToActions = {\r\n 'CONN' : 0,'MAN' : 1,'STS' : 2,\r\n}\r\n\r\nPromise.prototype.delay = function(duration) {\r\n return this.then(\r\n function(value) {\r\n return new Promise(function(resolve) {\r\n setTimeout(function() {\r\n resolve(value);\r\n }, duration);\r\n });\r\n },\r\n function(reason) {\r\n return new Promise(function(_resolve, reject) {\r\n setTimeout(function() {\r\n reject(reason);\r\n }, duration);\r\n });\r\n }\r\n );\r\n};\r\n\r\nfunction startCheckStatusInterval() {\r\n StatusIntervalActive = true;\r\n setTimeout(checkStatus, 3000);\r\n}\r\n\r\n\r\nfunction RepeatCheckStatusInterval() {\r\n if (StatusIntervalActive) {\r\n startCheckStatusInterval();\r\n }\r\n}\r\n\r\nfunction getConfigJson(slimMode) {\r\n const config = {};\r\n $('input.nvs').each(function(_index, entry) {\r\n if (!slimMode) {\r\n const nvsType = parseInt(entry.attributes.nvs_type.value, 10);\r\n if (entry.id !== '') {\r\n config[entry.id] = {};\r\n if (\r\n nvsType === nvsTypes.NVS_TYPE_U8 ||\r\n nvsType === nvsTypes.NVS_TYPE_I8 ||\r\n nvsType === nvsTypes.NVS_TYPE_U16 ||\r\n nvsType === nvsTypes.NVS_TYPE_I16 ||\r\n nvsType === nvsTypes.NVS_TYPE_U32 ||\r\n nvsType === nvsTypes.NVS_TYPE_I32 ||\r\n nvsType === nvsTypes.NVS_TYPE_U64 ||\r\n nvsType === nvsTypes.NVS_TYPE_I64\r\n ) {\r\n config[entry.id].value = parseInt(entry.value);\r\n } else {\r\n config[entry.id].value = entry.value;\r\n }\r\n config[entry.id].type = nvsType;\r\n }\r\n } else {\r\n config[entry.id] = entry.value;\r\n }\r\n });\r\n const key = $('#nvs-new-key').val();\r\n const val = $('#nvs-new-value').val();\r\n if (key !== '') {\r\n if (!slimMode) {\r\n config[key] = {};\r\n config[key].value = val;\r\n config[key].type = 33;\r\n } else {\r\n config[key] = val;\r\n }\r\n }\r\n return config;\r\n}\r\n\r\n// eslint-disable-next-line no-unused-vars\r\nfunction onFileLoad(elementId, event) {\r\n let data = {};\r\n try {\r\n data = JSON.parse(elementId.srcElement.result);\r\n } catch (e) {\r\n alert('Parsing failed!\\r\\n ' + e);\r\n }\r\n $('input.nvs').each(function(_index, entry) {\r\n if (data[entry.id]) {\r\n if (data[entry.id] !== entry.value) {\r\n console.log(\r\n 'Changed ' + entry.id + ' ' + entry.value + '==>' + data[entry.id]\r\n );\r\n $(this).val(data[entry.id]);\r\n }\r\n }\r\n });\r\n}\r\n\r\n// eslint-disable-next-line no-unused-vars\r\nfunction onChooseFile(event, control) {\r\n if (typeof window.FileReader !== 'function') {\r\n throw \"The file API isn't supported on this browser.\";\r\n }\r\n const input = event.target;\r\n if (!input) {\r\n throw 'The browser does not properly implement the event object';\r\n }\r\n if (!input.files) {\r\n throw 'This browser does not support the `files` property of the file input.';\r\n }\r\n if (!input.files[0]) {\r\n return undefined;\r\n }\r\n \r\n const file = input.files[0];\r\n let fr = new FileReader();\r\n fr.onload = onFileLoad.bind(control)(input, event);\r\n fr.readAsText(file);\r\n input.value = '';\r\n}\r\n\r\n// pull json file from https://gist.githubusercontent.com/sle118/dae585e157b733a639c12dc70f0910c5/raw/b462691f69e2ad31ac95c547af6ec97afb0f53db/squeezelite-esp32-presets.json and\r\n// load the names into cfg-hw-preset-model_name\r\nfunction loadPresets() {\r\n if(presetsloaded) return;\r\n presetsloaded = true;\r\n $.getJSON(\r\n 'https://gist.githubusercontent.com/sle118/dae585e157b733a639c12dc70f0910c5/raw/b462691f69e2ad31ac95c547af6ec97afb0f53db/squeezelite-esp32-presets.json',\r\n function(data) {\r\n $.each(data, function(key, val) {\r\n if(key === 'name') {\r\n $('#cfg-hw-preset-model_name').append(\r\n $('<option></option>').val(val).html(val)\r\n );\r\n }\r\n });\r\n }\r\n ).fail(function(jqxhr, textStatus, error) {\r\n const err = textStatus + ', ' + error;\r\n console.log('Request Failed: ' + err);\r\n $('hw-preset-section').hide();\r\n }\r\n );\r\n}\r\n\r\n \r\n\r\n\r\nfunction delayReboot(duration, cmdname, ota = 'reboot') {\r\n const url = '/'+ota+'.json';\r\n $('tbody#tasks').empty();\r\n $('#tasks_sect').css('visibility', 'collapse');\r\n Promise.resolve({ cmdname: cmdname, url: url })\r\n .delay(duration)\r\n .then(function(data) {\r\n if (data.cmdname.length > 0) {\r\n showCmdMessage(\r\n data.cmdname,\r\n 'MESSAGING_WARNING',\r\n 'System is rebooting.\\n',\r\n true\r\n );\r\n } else {\r\n showLocalMessage('System is rebooting.\\n', 'MESSAGING_WARNING');\r\n }\r\n console.log('now triggering reboot');\r\n $(\"button[onclick*='handleReboot']\").addClass('rebooting');\r\n $.ajax({\r\n url: data.url,\r\n dataType: 'text',\r\n method: 'POST',\r\n cache: false,\r\n contentType: 'application/json; charset=utf-8',\r\n data: JSON.stringify({\r\n timestamp: Date.now(),\r\n }),\r\n error: handleExceptionResponse,\r\n complete: function() {\r\n console.log('reboot call completed');\r\n Promise.resolve(data)\r\n .delay(6000)\r\n .then(function(rdata) {\r\n if (rdata.cmdname.length > 0) {\r\n HideCmdMessage(rdata.cmdname);\r\n }\r\n getCommands();\r\n getConfig();\r\n });\r\n },\r\n });\r\n });\r\n}\r\n// eslint-disable-next-line no-unused-vars\r\nwindow.saveAutoexec1 = function(apply) {\r\n showCmdMessage('cfg-audio-tmpl', 'MESSAGING_INFO', 'Saving.\\n', false);\r\n let commandLine = commandHeader + ' -n \"' + $('#player').val() + '\"';\r\n if (output === 'bt') {\r\n commandLine += ' -o \"BT\" -R -Z 192000';\r\n showCmdMessage(\r\n 'cfg-audio-tmpl',\r\n 'MESSAGING_INFO',\r\n 'Remember to configure the Bluetooth audio device name.\\n',\r\n true\r\n );\r\n } else if (output === 'spdif') {\r\n commandLine += ' -o SPDIF -Z 192000';\r\n } else {\r\n commandLine += ' -o I2S';\r\n }\r\n if ($('#optional').val() !== '') {\r\n commandLine += ' ' + $('#optional').val();\r\n }\r\n const data = {\r\n timestamp: Date.now(),\r\n };\r\n data.config = {\r\n autoexec1: { value: commandLine, type: 33 },\r\n autoexec: {\r\n value: $('#disable-squeezelite').prop('checked') ? '0' : '1',\r\n type: 33,\r\n },\r\n };\r\n\r\n $.ajax({\r\n url: '/config.json',\r\n dataType: 'text',\r\n method: 'POST',\r\n cache: false,\r\n contentType: 'application/json; charset=utf-8',\r\n data: JSON.stringify(data),\r\n error: handleExceptionResponse,\r\n complete: function(response) {\r\n if (\r\n response.responseText.result &&\r\n JSON.parse(response.responseText).result === 'OK'\r\n ) {\r\n showCmdMessage('cfg-audio-tmpl', 'MESSAGING_INFO', 'Done.\\n', true);\r\n if (apply) {\r\n delayReboot(1500, 'cfg-audio-tmpl');\r\n }\r\n } else if (response.responseText.result) {\r\n showCmdMessage(\r\n 'cfg-audio-tmpl',\r\n 'MESSAGING_WARNING',\r\n JSON.parse(response.responseText).Result + '\\n',\r\n true\r\n );\r\n } else {\r\n showCmdMessage(\r\n 'cfg-audio-tmpl',\r\n 'MESSAGING_ERROR',\r\n response.statusText + '\\n'\r\n );\r\n }\r\n console.log(response.responseText);\r\n },\r\n });\r\n console.log('sent data:', JSON.stringify(data));\r\n}\r\nwindow.handleDisconnect = function(){\r\n $.ajax({\r\n url: '/connect.json',\r\n dataType: 'text',\r\n method: 'DELETE',\r\n cache: false,\r\n contentType: 'application/json; charset=utf-8',\r\n data: JSON.stringify({\r\n timestamp: Date.now(),\r\n }),\r\n });\r\n}\r\nfunction setPlatformFilter(val){\r\n if($('.upf').filter(function(){ return $(this).text().toUpperCase()===val.toUpperCase()}).length>0){\r\n $('#splf').val(val).trigger('input');\r\n return true;\r\n }\r\n return false;\r\n}\r\nwindow.handleConnect = function(){\r\n ConnectingToSSID.ssid = $('#manual_ssid').val();\r\n ConnectingToSSID.pwd = $('#manual_pwd').val();\r\n ConnectingToSSID.dhcpname = $('#dhcp-name2').val();\r\n $(\"*[class*='connecting']\").hide();\r\n $('#ssid-wait').text(ConnectingToSSID.ssid);\r\n $('.connecting').show();\r\n\r\n $.ajax({\r\n url: '/connect.json',\r\n dataType: 'text',\r\n method: 'POST',\r\n cache: false,\r\n contentType: 'application/json; charset=utf-8',\r\n data: JSON.stringify({\r\n timestamp: Date.now(),\r\n ssid: ConnectingToSSID.ssid,\r\n pwd: ConnectingToSSID.pwd\r\n }),\r\n error: handleExceptionResponse,\r\n });\r\n\r\n // now we can re-set the intervals regardless of result\r\n startCheckStatusInterval();\r\n\r\n}\r\n$(document).ready(function() {\r\n $('#wifiTable').on('click','tr', function() {\r\n\r\n });\r\n $('#fw-url-input').on('input', function() {\r\n if($(this).val().length>8 && ($(this).val().startsWith('http://') || $(this).val().startsWith('https://'))){\r\n $('#start-flash').show();\r\n } \r\n else {\r\n $('#start-flash').hide();\r\n }\r\n });\r\n $('.upSrch').on('input', function() {\r\n const val = this.value;\r\n $(\"#rTable tr\").removeClass(this.id+'_hide');\r\n if(val.length>0) {\r\n $(`#rTable td:nth-child(${$(this).parent().index()+1})`).filter(function(){ \r\n return !$(this).text().toUpperCase().includes(val.toUpperCase());\r\n }).parent().addClass(this.id+'_hide');\r\n }\r\n $('[class*=\"_hide\"]').hide();\r\n $('#rTable tr').not('[class*=\"_hide\"]').show()\r\n\r\n });\r\n setTimeout(refreshAP,1500);\r\n\r\n \r\n $('#otadiv').on('hidden.bs.modal', function () {\r\n // reset flash status. This should stop the state machine from\r\n // executing steps up to flashing itself.\r\n flash_state=flash_status_codes.NONE;\r\n });\r\n $('#WifiConnectDialog').on('shown.bs.modal', function () {\r\n $(\"*[class*='connecting']\").hide();\r\n if(ConnectingToSSID.Action!==ConnectingToActions.STS){\r\n $('.connecting-init').show();\r\n $('#manual_ssid').trigger('focus'); \r\n }\r\n else {\r\n handleWifiDialog();\r\n }\r\n })\r\n $('#WifiConnectDialog').on('hidden.bs.modal', function () {\r\n $('#WifiConnectDialog input').val('');\r\n })\r\n \r\n $('#uCnfrm').on('shown.bs.modal', function () {\r\n $('#selectedFWURL').text($('#fw-url-input').val());\r\n })\r\n \r\n $('input#show-commands')[0].checked = LastCommandsState === 1;\r\n $('a[href^=\"#tab-commands\"]').hide();\r\n $('#load-nvs').on('click', function() {\r\n $('#nvsfilename').trigger('click');\r\n });\r\n $('#clear-syslog').on('click', function() {\r\n messagecount = 0;\r\n messageseverity = 'MESSAGING_INFO';\r\n $('#msgcnt').text('');\r\n $('#syslogTable').html('');\r\n });\r\n \r\n $('#wifiTable').on('click','tr', function() {\r\n ConnectingToSSID.Action=ConnectingToActions.CONN;\r\n if($(this).children('td:eq(1)').text() == ConnectedToSSID.ssid){\r\n ConnectingToSSID.Action=ConnectingToActions.STS;\r\n return;\r\n }\r\n if(!$(this).is(':last-child')){\r\n ConnectingToSSID.ssid=$(this).children('td:eq(1)').text();\r\n $('#manual_ssid').val(ConnectingToSSID.ssid);\r\n } \r\n else {\r\n ConnectingToSSID.Action=ConnectingToActions.MAN;\r\n ConnectingToSSID.ssid='';\r\n $('#manual_ssid').val(ConnectingToSSID.ssid);\r\n }\r\n });\r\n\r\n\r\n $('#ok-credits').on('click', function() {\r\n $('#credits').slideUp('fast', function() {});\r\n $('#app').slideDown('fast', function() {});\r\n });\r\n\r\n $('#acredits').on('click', function(event) {\r\n event.preventDefault();\r\n $('#app').slideUp('fast', function() {});\r\n $('#credits').slideDown('fast', function() {});\r\n });\r\n\r\n $('input#show-commands').on('click', function() {\r\n this.checked = this.checked ? 1 : 0;\r\n if (this.checked) {\r\n $('a[href^=\"#tab-commands\"]').show();\r\n LastCommandsState = 1;\r\n } else {\r\n LastCommandsState = 0;\r\n $('a[href^=\"#tab-commands\"]').hide();\r\n }\r\n });\r\n\r\n $('input#show-nvs').on('click', function() {\r\n this.checked = this.checked ? 1 : 0;\r\n if (this.checked) {\r\n $('*[href*=\"-nvs\"]').show();\r\n } else {\r\n $('*[href*=\"-nvs\"]').hide();\r\n }\r\n });\r\n \r\n $('#save-as-nvs').on('click', function() {\r\n const config = getConfigJson(true);\r\n const a = document.createElement('a');\r\n a.href = URL.createObjectURL(\r\n new Blob([JSON.stringify(config, null, 2)], {\r\n type: 'text/plain',\r\n })\r\n );\r\n a.setAttribute(\r\n 'download',\r\n 'nvs_config_' + hostName + '_' + Date.now() + 'json'\r\n );\r\n document.body.appendChild(a);\r\n a.click();\r\n document.body.removeChild(a);\r\n });\r\n\r\n $('#save-nvs').on('click', function() {\r\n post_config(getConfigJson(false));\r\n });\r\n \r\n $('#fwUpload').on('click', function() {\r\n const fileInput = document.getElementById('flashfilename').files;\r\n if (fileInput.length === 0) {\r\n alert('No file selected!');\r\n } else {\r\n handle_flash_state({ event: flash_events.START_OTA, file: fileInput[0] });\r\n }\r\n \r\n });\r\n $('[name=output-tmpl]').on('click', function() {\r\n handleTemplateTypeRadio(this.id);\r\n });\r\n\r\n $('#chkUpdates').on('click', function() {\r\n $('#rTable').html('');\r\n $.getJSON(releaseURL, function(data) {\r\n let i = 0;\r\n const branches = [];\r\n data.forEach(function(release) {\r\n const namecomponents = release.name.split('#');\r\n const branch = namecomponents[3];\r\n if (!branches.includes(branch)) {\r\n branches.push(branch);\r\n }\r\n });\r\n let fwb='';\r\n branches.forEach(function(branch) {\r\n fwb += '<option value=\"' + branch + '\">' + branch + '</option>';\r\n });\r\n $('#fwbranch').append(fwb);\r\n\r\n data.forEach(function(release) {\r\n let url = '';\r\n release.assets.forEach(function(asset) {\r\n if (asset.name.match(/\\.bin$/)) {\r\n url = asset.browser_download_url;\r\n }\r\n });\r\n const namecomponents = release.name.split('#');\r\n const ver = namecomponents[0];\r\n const cfg = namecomponents[2];\r\n const branch = namecomponents[3];\r\n var bits = ver.substr(ver.lastIndexOf('-')+1);\r\n bits = (bits =='32' || bits == '16')?bits:'';\r\n\r\n let body = release.body;\r\n body = body.replace(/'/gi, '\"');\r\n body = body.replace(\r\n /[\\s\\S]+(### Revision Log[\\s\\S]+)### ESP-IDF Version Used[\\s\\S]+/,\r\n '$1'\r\n );\r\n body = body.replace(/- \\(.+?\\) /g, '- ');\r\n $('#rTable').append(`<tr class='release ' fwurl='${url}'>\r\n <td data-toggle='tooltip' title='${body}'>${ver}</td><td>${new Date(release.created_at).toLocalShort()}\r\n </td><td class='upf'>${cfg}</td><td>${branch}</td><td>${bits}</td></tr>`\r\n );\r\n });\r\n if (i > 7) {\r\n $('#releaseTable').append(\r\n \"<tr id='showall'>\" +\r\n \"<td colspan='6'>\" +\r\n \"<input type='button' id='showallbutton' class='btn btn-info' value='Show older releases' />\" +\r\n '</td>' +\r\n '</tr>'\r\n );\r\n $('#showallbutton').on('click', function() {\r\n $('tr.hide').removeClass('hide');\r\n $('tr#showall').addClass('hide');\r\n });\r\n }\r\n $('#searchfw').css('display', 'inline');\r\n if(!setPlatformFilter(platform_name)){\r\n setPlatformFilter(project_name)\r\n }\r\n $('#rTable tr.release').on('click', function() {\r\n var url=this.attributes['fwurl'].value;\r\n if (lmsBaseUrl) {\r\n url = url.replace(/.*\\/download\\//, lmsBaseUrl + '/plugins/SqueezeESP32/firmware/');\r\n }\r\n $('#fw-url-input').val(url);\r\n $('#start-flash').show();\r\n $('#rTable tr.release').removeClass('table-success table-warning');\r\n $(this).addClass('table-success table-warning');\r\n });\r\n\r\n }).fail(function() {\r\n alert('failed to fetch release history!');\r\n }); \r\n });\r\n $('#fwcheck').on('click', function() { \r\n $('#releaseTable').html('');\r\n $('#fwbranch').empty();\r\n $.getJSON(releaseURL, function(data) {\r\n let i = 0;\r\n const branches = [];\r\n data.forEach(function(release) {\r\n const namecomponents = release.name.split('#');\r\n const branch = namecomponents[3];\r\n if (!branches.includes(branch)) {\r\n branches.push(branch);\r\n }\r\n });\r\n let fwb;\r\n branches.forEach(function(branch) {\r\n fwb += '<option value=\"' + branch + '\">' + branch + '</option>';\r\n });\r\n $('#fwbranch').append(fwb);\r\n\r\n data.forEach(function(release) {\r\n let url = '';\r\n release.assets.forEach(function(asset) {\r\n if (asset.name.match(/\\.bin$/)) {\r\n url = asset.browser_download_url;\r\n }\r\n });\r\n const namecomponents = release.name.split('#');\r\n const ver = namecomponents[0];\r\n const idf = namecomponents[1];\r\n const cfg = namecomponents[2];\r\n const branch = namecomponents[3];\r\n\r\n let body = release.body;\r\n body = body.replace(/'/gi, '\"');\r\n body = body.replace(\r\n /[\\s\\S]+(### Revision Log[\\s\\S]+)### ESP-IDF Version Used[\\s\\S]+/,\r\n '$1'\r\n );\r\n body = body.replace(/- \\(.+?\\) /g, '- ');\r\n const trclass = i++ > 6 ? ' hide' : '';\r\n $('#releaseTable').append(\r\n \"<tr class='release\" +\r\n trclass +\r\n \"'>\" +\r\n \"<td data-toggle='tooltip' title='\" +\r\n body +\r\n \"'>\" +\r\n ver +\r\n '</td>' +\r\n '<td>' +\r\n new Date(release.created_at).toLocalShort() +\r\n '</td>' +\r\n '<td>' +\r\n cfg +\r\n '</td>' +\r\n '<td>' +\r\n idf +\r\n '</td>' +\r\n '<td>' +\r\n branch +\r\n '</td>' +\r\n \"<td><input type='button' class='btn btn-success' value='Select' data-url='\" +\r\n url +\r\n \"' onclick='setURL(this);' /></td>\" +\r\n '</tr>'\r\n );\r\n });\r\n if (i > 7) {\r\n $('#releaseTable').append(\r\n \"<tr id='showall'>\" +\r\n \"<td colspan='6'>\" +\r\n \"<input type='button' id='showallbutton' class='btn btn-info' value='Show older releases' />\" +\r\n '</td>' +\r\n '</tr>'\r\n );\r\n $('#showallbutton').on('click', function() {\r\n $('tr.hide').removeClass('hide');\r\n $('tr#showall').addClass('hide');\r\n });\r\n }\r\n $('#searchfw').css('display', 'inline');\r\n }).fail(function() {\r\n alert('failed to fetch release history!');\r\n });\r\n });\r\n\r\n $('#updateAP').on('click', function() {\r\n refreshAP();\r\n console.log('refresh AP');\r\n });\r\n\r\n // first time the page loads: attempt to get the connection status and start the wifi scan\r\n getConfig();\r\n getCommands();\r\n\r\n // start timers\r\n startCheckStatusInterval();\r\n \r\n});\r\n\r\n// eslint-disable-next-line no-unused-vars\r\nwindow.setURL = function(button) {\r\n let url = button.dataset.url;\r\n\r\n $('[data-url^=\"http\"]')\r\n .addClass('btn-success')\r\n .removeClass('btn-danger');\r\n $('[data-url=\"' + url + '\"]')\r\n .addClass('btn-danger')\r\n .removeClass('btn-success');\r\n\r\n // if user can proxy download through LMS, modify the URL\r\n if (lmsBaseUrl) {\r\n url = url.replace(/.*\\/download\\//, lmsBaseUrl + '/plugins/SqueezeESP32/firmware/');\r\n }\r\n\r\n $('#fwurl').val(url);\r\n}\r\n\r\n\r\nfunction rssiToIcon(rssi) {\r\n if (rssi >= -55) {\r\n return `signal-wifi-fill`;\r\n } else if (rssi >= -60) {\r\n return `signal-wifi-3-fill`;\r\n } else if (rssi >= -65) {\r\n return `signal-wifi-2-fill`;\r\n } else if (rssi >= -70) {\r\n return `signal-wifi-1-fill`;\r\n } else { \r\n return `signal-wifi-line`;\r\n }\r\n}\r\n\r\nfunction refreshAP() {\r\n $.getJSON('/scan.json', async function() {\r\n await sleep(2000);\r\n $.getJSON('/ap.json', function(data) {\r\n if (data.length > 0) {\r\n // sort by signal strength\r\n data.sort(function(a, b) {\r\n const x = a.rssi;\r\n const y = b.rssi;\r\n // eslint-disable-next-line no-nested-ternary\r\n return x < y ? 1 : x > y ? -1 : 0;\r\n });\r\n apList = data;\r\n refreshAPHTML2(apList);\r\n\r\n }\r\n });\r\n }); \r\n}\r\nfunction formatAP(ssid, rssi, auth){\r\n return `<tr data-toggle=\"modal\" data-target=\"#WifiConnectDialog\"><td></td><td>${ssid}</td><td>\r\n \r\n \t<svg style=\"fill:white; width:1.5rem; height: 1.5rem;\">\r\n\t\t\t\t<use xlink:href=\"#${rssiToIcon(rssi)}\"></use>\r\n\t\t\t</svg>\r\n </td><td>\r\n \r\n <svg style=\"fill:white; width:1.5rem; height: 1.5rem;\">\r\n <use xlink:href=\"#lock${(auth == 0 ? '-unlock':'')}-fill\"></use>\r\n</svg>\r\n\r\n </td></tr>`;\r\n}\r\nfunction refreshAPHTML2(data) {\r\n let h = '';\r\n $('#wifiTable tr td:first-of-type').text('');\r\n $('#wifiTable tr').removeClass('table-success table-warning');\r\n if(data){\r\n data.forEach(function(e) {\r\n h+=formatAP(e.ssid, e.rssi, e.auth);\r\n });\r\n $('#wifiTable').html(h);\r\n }\r\n if($('.manual_add').length == 0){\r\n $('#wifiTable').append(formatAP('Manual add', 0,0));\r\n $('#wifiTable tr:last').addClass('table-light text-dark').addClass('manual_add');\r\n }\r\n if(ConnectedToSSID.ssid && ( ConnectedToSSID.urc === connectReturnCode.UPDATE_CONNECTION_OK || ConnectedToSSID.urc === connectReturnCode.UPDATE_FAILED_ATTEMPT_AND_RESTORE )){\r\n const wifiSelector=`#wifiTable td:contains(\"${ConnectedToSSID.ssid}\")`;\r\n if($(wifiSelector).filter(function() {return $(this).text() === ConnectedToSSID.ssid; }).length==0){\r\n $('#wifiTable').prepend(`${formatAP(ConnectedToSSID.ssid, ConnectedToSSID.rssi ?? 0, 0)}`);\r\n }\r\n $(wifiSelector).filter(function() {return $(this).text() === ConnectedToSSID.ssid; }).siblings().first().html('&check;').parent().addClass((ConnectedToSSID.urc === connectReturnCode.UPDATE_CONNECTION_OK?'table-success':'table-warning'));\r\n $('span#foot-wifi').html(`SSID: <strong>${ConnectedToSSID.ssid}</strong>, IP: <strong>${ConnectedToSSID.ip}</strong>`); \r\n $('#wifiStsIcon').attr('xlink:href',rssiToIcon(ConnectedToSSID.rssi));\r\n }\r\n else {\r\n $('span#foot-wifi').html('');\r\n }\r\n \r\n}\r\nfunction showTask(task) {\r\n console.debug(\r\n this.toLocaleString() +\r\n '\\t' +\r\n task.nme +\r\n '\\t' +\r\n task.cpu +\r\n '\\t' +\r\n taskStates[task.st] +\r\n '\\t' +\r\n task.minstk +\r\n '\\t' +\r\n task.bprio +\r\n '\\t' +\r\n task.cprio +\r\n '\\t' +\r\n task.num\r\n );\r\n $('tbody#tasks').append(\r\n '<tr class=\"table-primary\"><th scope=\"row\">' +\r\n task.num +\r\n '</th><td>' +\r\n task.nme +\r\n '</td><td>' +\r\n task.cpu +\r\n '</td><td>' +\r\n taskStates[task.st] +\r\n '</td><td>' +\r\n task.minstk +\r\n '</td><td>' +\r\n task.bprio +\r\n '</td><td>' +\r\n task.cprio +\r\n '</td></tr>'\r\n );\r\n}\r\nfunction btExists(name){\r\n return getBTSinkOpt(name).length>0;\r\n}\r\nfunction getBTSinkOpt(name){\r\n return $(`${btSinkNamesOptSel} option:contains('${name}')`);\r\n}\r\nfunction getMessages() {\r\n $.getJSON('/messages.json', async function(data) {\r\n for (const msg of data) {\r\n const msgAge = msg.current_time - msg.sent_time;\r\n var msgTime = new Date();\r\n msgTime.setTime(msgTime.getTime() - msgAge);\r\n switch (msg.class) {\r\n case 'MESSAGING_CLASS_OTA':\r\n var otaData = JSON.parse(msg.message);\r\n handle_flash_state({\r\n ota_pct: (otaData.ota_pct ?? -1),\r\n ota_dsc: (otaData.ota_dsc ??''),\r\n event: flash_events.PROCESS_OTA\r\n });\r\n break;\r\n case 'MESSAGING_CLASS_STATS':\r\n // for task states, check structure : task_state_t\r\n var statsData = JSON.parse(msg.message);\r\n console.debug(\r\n msgTime.toLocalShort() +\r\n ' - Number of running tasks: ' +\r\n statsData.ntasks\r\n );\r\n console.debug(\r\n msgTime.toLocalShort() +\r\n '\\tname' +\r\n '\\tcpu' +\r\n '\\tstate' +\r\n '\\tminstk' +\r\n '\\tbprio' +\r\n '\\tcprio' +\r\n '\\tnum'\r\n );\r\n if (statsData.tasks) {\r\n if ($('#tasks_sect').css('visibility') === 'collapse') {\r\n $('#tasks_sect').css('visibility', 'visible');\r\n }\r\n $('tbody#tasks').html('');\r\n statsData.tasks\r\n .sort(function(a, b) {\r\n return b.cpu - a.cpu;\r\n })\r\n .forEach(showTask, msgTime);\r\n } else if ($('#tasks_sect').css('visibility') === 'visible') {\r\n $('tbody#tasks').empty();\r\n $('#tasks_sect').css('visibility', 'collapse');\r\n }\r\n break;\r\n case 'MESSAGING_CLASS_SYSTEM':\r\n showMessage(msg, msgTime);\r\n break;\r\n case 'MESSAGING_CLASS_CFGCMD':\r\n var msgparts = msg.message.split(/([^\\n]*)\\n(.*)/gs);\r\n showCmdMessage(msgparts[1], msg.type, msgparts[2], true);\r\n break;\r\n case 'MESSAGING_CLASS_BT':\r\n if($(\"#cfg-audio-bt_source-sink_name\").is('input')){\r\n var attr=$(\"#cfg-audio-bt_source-sink_name\")[0].attributes;\r\n var attrs='';\r\n for (var j = 0; j < attr.length; j++) {\r\n if(attr.item(j).name!=\"type\"){\r\n attrs+=`${attr.item(j).name } = \"${attr.item(j).value}\" `;\r\n }\r\n }\r\n var curOpt=$(\"#cfg-audio-bt_source-sink_name\")[0].value;\r\n $(\"#cfg-audio-bt_source-sink_name\").replaceWith(`<select id=\"cfg-audio-bt_source-sink_name\" ${attrs}><option value=\"${curOpt}\" data-description=\"${curOpt}\">${curOpt}</option></select> `);\r\n }\r\n JSON.parse(msg.message).forEach(function(btEntry) {\r\n //<input type=\"text\" class=\"form-control bg-success\" placeholder=\"name\" hasvalue=\"true\" longopts=\"sink_name\" shortopts=\"n\" checkbox=\"false\" cmdname=\"cfg-audio-bt_source\" id=\"cfg-audio-bt_source-sink_name\" name=\"cfg-audio-bt_source-sink_name\">\r\n //<select hasvalue=\"true\" longopts=\"jack_behavior\" shortopts=\"j\" checkbox=\"false\" cmdname=\"cfg-audio-general\" id=\"cfg-audio-general-jack_behavior\" name=\"cfg-audio-general-jack_behavior\" class=\"form-control \"><option>--</option><option>Headphones</option><option>Subwoofer</option></select> \r\n if(!btExists(btEntry.name)){\r\n $(\"#cfg-audio-bt_source-sink_name\").append(`<option>${btEntry.name}</option>`);\r\n showMessage({ type:msg.type, message:`BT Audio device found: ${btEntry.name} RSSI: ${btEntry.rssi} `}, msgTime);\r\n }\r\n getBTSinkOpt(btEntry.name).attr('data-description', `${btEntry.name} (${btEntry.rssi}dB)`)\r\n .attr('rssi',btEntry.rssi)\r\n .attr('value',btEntry.name)\r\n .text(`${btEntry.name} [${btEntry.rssi}dB]`).trigger('change');\r\n \r\n });\r\n $(btSinkNamesOptSel).append($(`${btSinkNamesOptSel} option`).remove().sort(function(a, b) { \r\n console.log(`${parseInt($(a).attr('rssi'))} < ${parseInt( $(b).attr('rssi'))} ? `);\r\n return parseInt($(a).attr('rssi')) < parseInt( $(b).attr('rssi')) ? 1 : -1; \r\n }));\r\n break;\r\n default:\r\n break;\r\n }\r\n }\r\n }).fail(function(xhr, ajaxOptions, thrownError){\r\n if(xhr.status==404){\r\n $('.orec').hide(); // system commands won't be available either\r\n } \r\n else {\r\n handleExceptionResponse(xhr, ajaxOptions, thrownError);\r\n }\r\n \r\n }\r\n );\r\n\r\n /*\r\n Minstk is minimum stack space left\r\nBprio is base priority\r\ncprio is current priority\r\nnme is name\r\nst is task state. I provided a \"typedef\" that you can use to convert to text\r\ncpu is cpu percent used\r\n*/\r\n}\r\nfunction handleRecoveryMode(data) {\r\n const locRecovery= data.recovery ??0;\r\n if (LastRecoveryState !== locRecovery) {\r\n LastRecoveryState = locRecovery;\r\n $('input#show-nvs')[0].checked = LastRecoveryState === 1;\r\n }\r\n if ($('input#show-nvs')[0].checked) {\r\n $('*[href*=\"-nvs\"]').show();\r\n\r\n } else {\r\n $('*[href*=\"-nvs\"]').hide();\r\n }\r\n if (locRecovery === 1) {\r\n recovery = true;\r\n $('.recovery_element').show();\r\n $('.ota_element').hide();\r\n $('#boot-button').html('Reboot');\r\n $('#boot-form').attr('action', '/reboot_ota.json');\r\n } else {\r\n recovery = false;\r\n $('.recovery_element').hide();\r\n $('.ota_element').show();\r\n $('#boot-button').html('Recovery');\r\n $('#boot-form').attr('action', '/recovery.json');\r\n }\r\n}\r\n\r\nfunction hasConnectionChanged(data){\r\n// gw: \"192.168.10.1\"\r\n// ip: \"192.168.10.225\"\r\n// netmask: \"255.255.255.0\"\r\n// ssid: \"MyTestSSID\"\r\n\r\n return (data.urc !== ConnectedToSSID.urc || \r\n data.ssid !== ConnectedToSSID.ssid || \r\n data.gw !== ConnectedToSSID.gw ||\r\n data.netmask !== ConnectedToSSID.netmask ||\r\n data.ip !== ConnectedToSSID.ip || data.rssi !== ConnectedToSSID.rssi )\r\n}\r\nfunction handleWifiDialog(data){\r\n if($('#WifiConnectDialog').is(':visible')){\r\n if(ConnectedToSSID.ip) {\r\n $('#ipAddress').text(ConnectedToSSID.ip);\r\n }\r\n if(ConnectedToSSID.ssid) {\r\n $('#connectedToSSID' ).text(ConnectedToSSID.ssid);\r\n } \r\n if(ConnectedToSSID.gw) {\r\n $('#gateway' ).text(ConnectedToSSID.gw);\r\n } \r\n if(ConnectedToSSID.netmask) {\r\n $('#netmask' ).text(ConnectedToSSID.netmask);\r\n } \r\n if(ConnectingToSSID.Action===undefined || (ConnectingToSSID.Action && ConnectingToSSID.Action == ConnectingToActions.STS)) {\r\n $(\"*[class*='connecting']\").hide();\r\n $('.connecting-status').show();\r\n }\r\n if(SystemConfig.ap_ssid){\r\n $('#apName').text(SystemConfig.ap_ssid.value);\r\n }\r\n if(SystemConfig.ap_pwd){\r\n $('#apPass').text(SystemConfig.ap_pwd.value);\r\n } \r\n if(!data)\r\n {\r\n return;\r\n }\r\n else {\r\n switch (data.urc) {\r\n case connectReturnCode.UPDATE_CONNECTION_OK:\r\n if(data.ssid && data.ssid===ConnectingToSSID.ssid){\r\n $(\"*[class*='connecting']\").hide();\r\n $('.connecting-success').show(); \r\n ConnectingToSSID.Action = ConnectingToActions.STS;\r\n }\r\n break;\r\n case connectReturnCode.UPDATE_FAILED_ATTEMPT:\r\n // \r\n if(ConnectingToSSID.Action !=ConnectingToActions.STS && ConnectingToSSID.ssid == data.ssid ){\r\n $(\"*[class*='connecting']\").hide();\r\n $('.connecting-fail').show();\r\n }\r\n break;\r\n case connectReturnCode.UPDATE_LOST_CONNECTION:\r\n \r\n break; \r\n case connectReturnCode.UPDATE_FAILED_ATTEMPT_AND_RESTORE:\r\n if(ConnectingToSSID.Action !=ConnectingToActions.STS && ConnectingToSSID.ssid != data.ssid ){\r\n $(\"*[class*='connecting']\").hide();\r\n $('.connecting-fail').show();\r\n }\r\n break;\r\n case connectReturnCode.UPDATE_USER_DISCONNECT:\r\n // that's a manual disconnect\r\n // if ($('#wifi-status').is(':visible')) {\r\n // $('#wifi-status').slideUp('fast', function() {});\r\n // $('span#foot-wifi').html('');\r\n \r\n // } \r\n break;\r\n default:\r\n break;\r\n }\r\n }\r\n\r\n }\r\n}\r\nfunction handleWifiStatus(data) {\r\n if(hasConnectionChanged(data)){\r\n ConnectedToSSID=data;\r\n refreshAPHTML2();\r\n }\r\n handleWifiDialog(data);\r\n}\r\n\r\nfunction batteryToIcon(voltage) {\r\n /* Assuming Li-ion 18650s as a power source, 3.9V per cell, or above is treated\r\n\t\t\t\tas full charge (>75% of capacity). 3.4V is empty. The gauge is loosely\r\n\t\t\t\tfollowing the graph here:\r\n\t\t\t\t\thttps://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages\r\n\t\t\t\tusing the 0.2C discharge profile for the rest of the values.\r\n\t\t\t*/\r\n if (voltage > 0) {\r\n if (inRange(voltage, 5.8, 6.8) || inRange(voltage, 8.8, 10.2)) {\r\n return `battery-low-line`;\r\n } else if (inRange(voltage, 6.8, 7.4) || inRange(voltage, 10.2, 11.1)) {\r\n return `battery-low-line`;\r\n } else if (\r\n inRange(voltage, 7.4, 7.5) ||\r\n inRange(voltage, 11.1, 11.25)\r\n ) {\r\n return `battery-low-line`;\r\n } else if (\r\n inRange(voltage, 7.5, 7.8) ||\r\n inRange(voltage, 11.25, 11.7)\r\n ) {\r\n return `battery-fill`;\r\n } else {\r\n return `battery-line`;\r\n }\r\n }\r\n}\r\nfunction checkStatus() {\r\n RepeatCheckStatusInterval();\r\n if (blockAjax) {\r\n return;\r\n }\r\n blockAjax = true;\r\n getMessages();\r\n $.getJSON('/status.json', function(data) {\r\n handleRecoveryMode(data);\r\n handleWifiStatus(data);\r\n handlebtstate(data);\r\n handle_flash_state({\r\n ota_pct: (data.ota_pct ?? -1),\r\n ota_dsc: (data.ota_dsc ??''),\r\n event: flash_events.PROCESS_OTA_STATUS\r\n });\r\n if (data.project_name && data.project_name !== '') {\r\n project_name = data.project_name;\r\n }\r\n if(data.platform_name && data.platform_name!==''){\r\n platform_name = data.platform_name;\r\n }\r\n if (data.version && data.version !== '') {\r\n versionName=data.version;\r\n $(\"#navtitle\").html(`${project_name}${recovery?'<br>[recovery]':''}`);\r\n $('span#foot-fw').html(`fw: <strong>${versionName}</strong>, mode: <strong>${recovery?\"Recovery\":project_name}</strong>`);\r\n } else {\r\n $('span#flash-status').html('');\r\n }\r\n if (data.Voltage) {\r\n $('#battery').attr('xlink:href', `#${batteryToIcon(data.Voltage)}`);\r\n $('#battery').show();\r\n } else {\r\n $('#battery').hide();\r\n }\r\n if((data.message??'')!='' && prevmessage != data.message){\r\n // supporting older recovery firmwares - messages will come from the status.json structure\r\n prevmessage = data.message;\r\n showLocalMessage(data.message, 'MESSAGING_INFO')\r\n }\r\n if(data.is_i2c_locked){\r\n $('flds-cfg-hw-preset').hide();\r\n }\r\n else {\r\n $('flds-cfg-hw-preset').show();\r\n loadPresets();\r\n }\r\n $(\"button[onclick*='handleReboot']\").removeClass('rebooting');\r\n\r\n if (typeof lmsBaseUrl == \"undefined\" || data.lms_ip != prevLMSIP && data.lms_ip && data.lms_port) {\r\n const baseUrl = 'http://' + data.lms_ip + ':' + data.lms_port;\r\n prevLMSIP=data.lms_ip;\r\n $.ajax({\r\n url: baseUrl + '/plugins/SqueezeESP32/firmware/-check.bin', \r\n type: 'HEAD',\r\n dataType: 'text',\r\n cache: false,\r\n error: function() {\r\n // define the value, so we don't check it any more.\r\n lmsBaseUrl = '';\r\n },\r\n success: function() {\r\n lmsBaseUrl = baseUrl;\r\n }\r\n });\r\n }\r\n \r\n $('#o_jack').attr('display', Number(data.Jack) ? 'inline' : 'none');\r\n blockAjax = false;\r\n }).fail(function(xhr, ajaxOptions, thrownError) {\r\n handleExceptionResponse(xhr, ajaxOptions, thrownError);\r\n blockAjax = false;\r\n });\r\n}\r\n// eslint-disable-next-line no-unused-vars\r\nwindow.runCommand = function(button, reboot) {\r\n let cmdstring = button.attributes.cmdname.value;\r\n showCmdMessage(\r\n button.attributes.cmdname.value,\r\n 'MESSAGING_INFO',\r\n 'Executing.',\r\n false\r\n );\r\n const fields = document.getElementById('flds-' + cmdstring);\r\n cmdstring += ' ';\r\n if (fields) {\r\n const allfields = fields.querySelectorAll('select,input');\r\n for (var i = 0; i < allfields.length; i++) {\r\n const attr = allfields[i].attributes;\r\n let qts = '';\r\n let opt = '';\r\n let isSelect = $(allfields[i]).is('select');\r\n const hasValue=attr.hasvalue.value === 'true';\r\n const validVal=(isSelect && allfields[i].value !== '--' ) || ( !isSelect && allfields[i].value !== '' );\r\n\r\n if ( !hasValue|| hasValue && validVal) {\r\n if (attr.longopts.value !== 'undefined') {\r\n opt += '--' + attr.longopts.value;\r\n } else if (attr.shortopts.value !== 'undefined') {\r\n opt = '-' + attr.shortopts.value;\r\n }\r\n\r\n if (attr.hasvalue.value === 'true') {\r\n if (allfields[i].value !== '') {\r\n qts = /\\s/.test(allfields[i].value) ? '\"' : '';\r\n cmdstring += opt + ' ' + qts + allfields[i].value + qts + ' ';\r\n }\r\n } else {\r\n // this is a checkbox\r\n if (allfields[i].checked) {\r\n cmdstring += opt + ' ';\r\n }\r\n }\r\n }\r\n }\r\n }\r\n console.log(cmdstring);\r\n\r\n const data = {\r\n timestamp: Date.now(),\r\n };\r\n data.command = cmdstring;\r\n\r\n $.ajax({\r\n url: '/commands.json',\r\n dataType: 'text',\r\n method: 'POST',\r\n cache: false,\r\n contentType: 'application/json; charset=utf-8',\r\n data: JSON.stringify(data),\r\n error: function(xhr, _ajaxOptions, thrownError){\r\n var cmd=JSON.parse(this.data ).command;\r\n if(xhr.status==404){\r\n showCmdMessage(\r\n cmd.substr(0,cmd.indexOf(' ')),\r\n 'MESSAGING_ERROR',\r\n `${recovery?'Limited recovery mode active. Unsupported action ':'Unexpected error while processing command'}`,\r\n true\r\n );\r\n }\r\n else {\r\n handleExceptionResponse(xhr, _ajaxOptions, thrownError);\r\n showCmdMessage(\r\n cmd.substr(0,cmd.indexOf(' ')-1),\r\n 'MESSAGING_ERROR',\r\n `Unexpected error ${(thrownError !== '')?thrownError:'with return status = '+xhr.status}`,\r\n true\r\n ); \r\n }\r\n },\r\n success: function(response) {\r\n // var returnedResponse = JSON.parse(response.responseText);\r\n $('.orec').show();\r\n console.log(response.responseText);\r\n if (\r\n response.responseText &&\r\n JSON.parse(response.responseText).Result === 'Success' &&\r\n reboot\r\n ) {\r\n delayReboot(2500, button.attributes.cmdname.value);\r\n }\r\n },\r\n });\r\n}\r\nfunction getLongOps(data, name, longopts){\r\n return data.values[name]!==undefined?data.values[name][longopts]:\"\";\r\n}\r\nfunction getCommands() {\r\n $.getJSON('/commands.json', function(data) {\r\n console.log(data);\r\n $('.orec').show();\r\n data.commands.forEach(function(command) {\r\n if ($('#flds-' + command.name).length === 0) {\r\n const cmdParts = command.name.split('-');\r\n const isConfig = cmdParts[0] === 'cfg';\r\n const targetDiv = '#tab-' + cmdParts[0] + '-' + cmdParts[1];\r\n let innerhtml = '';\r\n\r\n // innerhtml+='<tr class=\"table-light\"><td>'+(isConfig?'<h1>':'');\r\n innerhtml +=\r\n '<div class=\"card text-white mb-3\"><div class=\"card-header\">' +\r\n command.help.encodeHTML().replace(/\\n/g, '<br />') +\r\n '</div><div class=\"card-body\">';\r\n innerhtml += '<fieldset id=\"flds-' + command.name + '\">';\r\n if (command.argtable) {\r\n command.argtable.forEach(function(arg) {\r\n let placeholder = arg.datatype || '';\r\n const ctrlname = command.name + '-' + arg.longopts;\r\n const curvalue = getLongOps(data,command.name,arg.longopts);\r\n\r\n let attributes = 'hasvalue=' + arg.hasvalue + ' ';\r\n\r\n // attributes +='datatype=\"'+arg.datatype+'\" ';\r\n attributes += 'longopts=\"' + arg.longopts + '\" ';\r\n attributes += 'shortopts=\"' + arg.shortopts + '\" ';\r\n attributes += 'checkbox=' + arg.checkbox + ' ';\r\n attributes += 'cmdname=\"' + command.name + '\" ';\r\n attributes +=\r\n 'id=\"' +\r\n ctrlname +\r\n '\" name=\"' +\r\n ctrlname +\r\n '\" hasvalue=\"' +\r\n arg.hasvalue +\r\n '\" ';\r\n let extraclass = arg.mincount > 0 ? 'bg-success' : '';\r\n if (arg.glossary === 'hidden') {\r\n attributes += ' style=\"visibility: hidden;\"';\r\n }\r\n if (arg.checkbox) {\r\n innerhtml +=\r\n '<div class=\"form-check\"><label class=\"form-check-label\">';\r\n innerhtml +=\r\n '<input type=\"checkbox\" ' +\r\n attributes +\r\n ' class=\"form-check-input ' +\r\n extraclass +\r\n '\" value=\"\" >' +\r\n arg.glossary.encodeHTML() +\r\n '<small class=\"form-text text-muted\">Previous value: ' +\r\n (curvalue ? 'Checked' : 'Unchecked') +\r\n '</small></label>';\r\n } else {\r\n innerhtml +=\r\n '<div class=\"form-group\" ><label for=\"' +\r\n ctrlname +\r\n '\">' +\r\n arg.glossary.encodeHTML() +\r\n '</label>';\r\n if (placeholder.includes('|')) {\r\n extraclass = placeholder.startsWith('+') ? ' multiple ' : '';\r\n placeholder = placeholder\r\n .replace('<', '')\r\n .replace('=', '')\r\n .replace('>', '');\r\n innerhtml += `<select ${attributes} class=\"form-control ${extraclass}\" >`;\r\n placeholder = '--|' + placeholder;\r\n placeholder.split('|').forEach(function(choice) {\r\n innerhtml += '<option >' + choice + '</option>';\r\n });\r\n innerhtml += '</select>';\r\n } else {\r\n innerhtml +=\r\n '<input type=\"text\" class=\"form-control ' +\r\n extraclass +\r\n '\" placeholder=\"' +\r\n placeholder +\r\n '\" ' +\r\n attributes +\r\n '>';\r\n }\r\n innerhtml +=\r\n '<small class=\"form-text text-muted\">Previous value: ' +\r\n (curvalue || '') +\r\n '</small>';\r\n }\r\n innerhtml += '</div>';\r\n });\r\n }\r\n innerhtml += '<div style=\"margin-top: 16px;\">';\r\n innerhtml +=\r\n '<div class=\"toast show\" role=\"alert\" aria-live=\"assertive\" aria-atomic=\"true\" style=\"display: none;\" id=\"toast_' +\r\n command.name +\r\n '\">';\r\n innerhtml +=\r\n '<div class=\"toast-header\"><strong class=\"mr-auto\">Result</strong><button type=\"button\" class=\"ml-2 mb-1 close\" data-dismiss=\"toast\" aria-label=\"Close\" onclick=\"$(this).parent().parent().hide()\">';\r\n innerhtml +=\r\n '<span aria-hidden=\"true\">×</span></button></div><div class=\"toast-body\" id=\"msg_' +\r\n command.name +\r\n '\"></div></div>';\r\n if (isConfig) {\r\n innerhtml +=\r\n '<button type=\"submit\" class=\"btn btn-info\" id=\"btn-save-' +\r\n command.name +\r\n '\" cmdname=\"' +\r\n command.name +\r\n '\" onclick=\"runCommand(this,false)\">Save</button>';\r\n innerhtml +=\r\n '<button type=\"submit\" class=\"btn btn-warning\" id=\"btn-commit-' +\r\n command.name +\r\n '\" cmdname=\"' +\r\n command.name +\r\n '\" onclick=\"runCommand(this,true)\">Apply</button>';\r\n } else {\r\n innerhtml +=\r\n '<button type=\"submit\" class=\"btn btn-success\" id=\"btn-run-' +\r\n command.name +\r\n '\" cmdname=\"' +\r\n command.name +\r\n '\" onclick=\"runCommand(this,false)\">Execute</button>';\r\n }\r\n innerhtml += '</div></fieldset></div></div>';\r\n if (isConfig) {\r\n $(targetDiv).append(innerhtml);\r\n } else {\r\n $('#commands-list').append(innerhtml);\r\n }\r\n }\r\n });\r\n\r\n data.commands.forEach(function(command) {\r\n $('[cmdname=' + command.name + ']:input').val('');\r\n $('[cmdname=' + command.name + ']:checkbox').prop('checked', false);\r\n if (command.argtable) {\r\n command.argtable.forEach(function(arg) {\r\n const ctrlselector = '#' + command.name + '-' + arg.longopts;\r\n const ctrlValue = getLongOps(data,command.name,arg.longopts);\r\n if (arg.checkbox) {\r\n $(ctrlselector)[0].checked = ctrlValue;\r\n } else {\r\n if (ctrlValue !== undefined) {\r\n $(ctrlselector)\r\n .val(ctrlValue)\r\n .trigger('change');\r\n }\r\n if (\r\n $(ctrlselector)[0].value.length === 0 &&\r\n (arg.datatype || '').includes('|')\r\n ) {\r\n $(ctrlselector)[0].value = '--';\r\n }\r\n }\r\n });\r\n }\r\n });\r\n }).fail(function(xhr, ajaxOptions, thrownError) {\r\n if(xhr.status==404){\r\n $('.orec').hide();\r\n } \r\n else {\r\n handleExceptionResponse(xhr, ajaxOptions, thrownError);\r\n }\r\n \r\n $('#commands-list').empty();\r\n blockAjax = false;\r\n });\r\n}\r\n\r\nfunction getConfig() {\r\n $.getJSON('/config.json', function(entries) {\r\n $('#nvsTable tr').remove();\r\n const data = (entries.config? entries.config : entries);\r\n SystemConfig = data;\r\n Object.keys(data)\r\n .sort()\r\n .forEach(function(key) {\r\n let val = data[key].value;\r\n if (key === 'autoexec') {\r\n if (data.autoexec.value === '0') {\r\n $('#disable-squeezelite')[0].checked = true;\r\n } else {\r\n $('#disable-squeezelite')[0].checked = false;\r\n }\r\n } else if (key === 'autoexec1') {\r\n const re = /-o\\s?([\"][^\"]*[\"]|[^-]+)/g;\r\n const m = re.exec(val);\r\n if (m[1].toUpperCase().startsWith('I2S')) {\r\n handleTemplateTypeRadio('i2s');\r\n } else if (m[1].toUpperCase().startsWith('SPDIF')) {\r\n handleTemplateTypeRadio('spdif');\r\n } else if (m[1].toUpperCase().startsWith('\"BT')) {\r\n handleTemplateTypeRadio('bt');\r\n }\r\n } else if (key === 'host_name') {\r\n val = val.replaceAll('\"', '');\r\n $('input#dhcp-name1').val(val);\r\n $('input#dhcp-name2').val(val);\r\n $('#player').val(val);\r\n document.title = val;\r\n hostName = val;\r\n } else if (key === 'rel_api') {\r\n releaseURL = val;\r\n }\r\n $('tbody#nvsTable').append(\r\n '<tr>' +\r\n '<td>' +\r\n key +\r\n '</td>' +\r\n \"<td class='value'>\" +\r\n \"<input type='text' class='form-control nvs' id='\" +\r\n key +\r\n \"' nvs_type=\" +\r\n data[key].type +\r\n ' >' +\r\n '</td>' +\r\n '</tr>'\r\n );\r\n $('input#' + key).val(data[key].value);\r\n });\r\n $('tbody#nvsTable').append(\r\n \"<tr><td><input type='text' class='form-control' id='nvs-new-key' placeholder='new key'></td><td><input type='text' class='form-control' id='nvs-new-value' placeholder='new value' nvs_type=33 ></td></tr>\"\r\n );\r\n if (entries.gpio) {\r\n $('#pins').show();\r\n $('tbody#gpiotable tr').remove();\r\n entries.gpio.forEach(function(gpioEntry) {\r\n $('tbody#gpiotable').append(\r\n '<tr class=' +\r\n (gpioEntry.fixed ? 'table-secondary' : 'table-primary') +\r\n '><th scope=\"row\">' +\r\n gpioEntry.group +\r\n '</th><td>' +\r\n gpioEntry.name +\r\n '</td><td>' +\r\n gpioEntry.gpio +\r\n '</td><td>' +\r\n (gpioEntry.fixed ? 'Fixed' : 'Configuration') +\r\n '</td></tr>'\r\n );\r\n });\r\n }\r\n else {\r\n $('#pins').hide();\r\n }\r\n }).fail(function(xhr, ajaxOptions, thrownError) {\r\n handleExceptionResponse(xhr, ajaxOptions, thrownError);\r\n blockAjax = false;\r\n });\r\n}\r\nfunction showLocalMessage(message, severity) {\r\n const msg = {\r\n message: message,\r\n type: severity,\r\n };\r\n showMessage(msg, new Date());\r\n}\r\n\r\nfunction showMessage(msg, msgTime) {\r\n let color = 'table-success';\r\n\r\n if (msg.type === 'MESSAGING_WARNING') {\r\n color = 'table-warning';\r\n if (messageseverity === 'MESSAGING_INFO') {\r\n messageseverity = 'MESSAGING_WARNING';\r\n }\r\n } else if (msg.type === 'MESSAGING_ERROR') {\r\n if (\r\n messageseverity === 'MESSAGING_INFO' ||\r\n messageseverity === 'MESSAGING_WARNING'\r\n ) {\r\n messageseverity = 'MESSAGING_ERROR';\r\n }\r\n color = 'table-danger';\r\n }\r\n if (++messagecount > 0) {\r\n $('#msgcnt').removeClass('badge-success');\r\n $('#msgcnt').removeClass('badge-warning');\r\n $('#msgcnt').removeClass('badge-danger');\r\n $('#msgcnt').addClass(pillcolors[messageseverity]);\r\n $('#msgcnt').text(messagecount);\r\n }\r\n\r\n $('#syslogTable').append(\r\n \"<tr class='\" +\r\n color +\r\n \"'>\" +\r\n '<td>' +\r\n msgTime.toLocalShort() +\r\n '</td>' +\r\n '<td>' +\r\n msg.message.encodeHTML() +\r\n '</td>' +\r\n '</tr>'\r\n );\r\n}\r\n\r\nfunction inRange(x, min, max) {\r\n return (x - min) * (x - max) <= 0;\r\n}\r\n\r\nfunction sleep(ms) {\r\n return new Promise(resolve => setTimeout(resolve, ms));\r\n}\r\n\r\n",[],{"ruleId":null,"fatal":true,"severity":2,"message":"9"},"Parsing error: require() of ES Module C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\node_modules\\eslint\\node_modules\\eslint-scope\\lib\\definition.js from C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\node_modules\\babel-eslint\\lib\\require-from-eslint.js not supported.\nInstead change the require of definition.js in C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\node_modules\\babel-eslint\\lib\\require-from-eslint.js to a dynamic import() which is available in all CommonJS modules."]