diff options
Diffstat (limited to 'static/js')
| -rw-r--r-- | static/js/base.js | 200 | ||||
| -rw-r--r-- | static/js/controller.js | 93 | ||||
| -rw-r--r-- | static/js/lmmi.js | 39 | ||||
| -rw-r--r-- | static/js/main.js | 118 | ||||
| -rw-r--r-- | static/js/mdio.js | 36 | ||||
| -rw-r--r-- | static/js/mle.js | 35 | ||||
| -rw-r--r-- | static/js/modal.js | 120 | ||||
| -rw-r--r-- | static/js/ss.js | 302 | ||||
| -rw-r--r-- | static/js/ws.js | 147 |
9 files changed, 1090 insertions, 0 deletions
diff --git a/static/js/base.js b/static/js/base.js new file mode 100644 index 0000000..2920c25 --- /dev/null +++ b/static/js/base.js @@ -0,0 +1,200 @@ +/* + * + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: base.js + * + */ + + "use strict"; + +const STATUS = { + OK: 10, + FORM: 20, + DUPLICATE: 30, + REDIRECT: 100, + UNBOUND_FORM: 0, + ERROR_CSRF: -1, + ERROR_PARAMS: -2, + ERROR_SERVER: -3, + ERROR_COOKIES: -4, + ERROR_TAMPER: -5, +}; + +const PORT_PHY0 = 0; +const PORT_PHY1 = 1; +const PORT_PHY2 = 2; +const PORT_PC = 10; + +const MSG_FORMAT_BASIC = 0; +const MSG_FORMAT_CONTROLLER = 1; +const MSG_FORMAT_MLE = 2; + +const MODE_IDLE = 0; +const MODE_LIVE = 1; +const MODE_FILE = 2; + +const MSG_TYPE_NULL = 0 +const MSG_TYPE_WRITE = 1; +const MSG_TYPE_READ = 2; +const MSG_TYPE_REPLY_SUCCESS = 3; +const MSG_TYPE_REPLY_ERROR = 4; +const MSG_TYPE_NOTIY = 5; +const MSG_TYPE_MLE = 0X10; +const MSG_MAP = { MSG_TYPE_NULL : "NULL", MSG_TYPE_WRITE : "Write", MSG_TYPE_READ : "Read", + MSG_TYPE_REPLY_SUCCESS : "Success", MSG_TYPE_REPLY_ERROR : "Error", MSG_TYPE_NOTIY : "Notify", + MSG_TYPE_MLE : "MLE"}; + +/** + * Convert the server's response from JSON (string) to JS object form and + * gracefully handle an exception + */ +function getResponse(resp) { + try { + resp = JSON.parse(resp); + } catch (err) { + resp = {}; + resp.d = ""; + resp.r = MC_CONSTANTS.SERVER; + } + + return resp; +} + +function getString(obj) { + var result = ""; + for (var key in obj) { + if (result) { + result += "&"; + } + result += key + "=" + encodeURIComponent(obj[key]); + } + return result; +} + +function getPrefix() { + return window.location.pathname.split('/')[1]; // eg., /todos/<function> +} + +/** + * Alert the user depending on error returned by the server. Todo: consider + * using jqm popups instead of alert + */ +function processError(resp) { + $.loading('none'); + if (resp.r === MC_CONSTANTS.COOKIES || resp.r === MC_CONSTANTS.CSRF) { + alert("Error saving your submission. Cookies may be blocked. Please modify your browser's settings and try again including refreshing this page"); + } else if (resp.r === MC_CONSTANTS.TAMPER) { + alert("Our web server is in an inconsistent state with your browser. Please refresh your page and try again."); + } else { + alert("Server error. Sorry about the trouble. It's been logged, and our team will look into it"); + } +} + + +function $(selector, el) { + if (!el) { + el = document; + } + return el.querySelector(selector); +} + +$.loading = function(display) { + $('#mc-loading').style.display = display; +}; + + +$.get = function(url, data, callback) { + var xhr = new XMLHttpRequest(); + + xhr.addEventListener("progress", updateProgress); + xhr.addEventListener("load", transferComplete); + xhr.addEventListener("error", transferFailed); + xhr.addEventListener("abort", transferCanceled); + + if (data) { + url += "?" + getString(data); + } + + xhr.open('GET', url, true); + + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + + xhr.send(null); + + function updateProgress(event) { + // was:event.loaded / event.total + console.log('updateProgress'); + } + + function transferComplete(event) { + callback(event.target.response); + } + + function transferFailed(event) { + console.log('transferFailed'); + event.preventDefault(); + event.stopImmediatePropagation(); + } + + function transferCanceled(event) { + console.log('transferCanceled'); + } +}; + +$.post = function(url, data, callback) { + var xhr = new XMLHttpRequest(); + xhr.open('POST', url, true); + + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + + if (!(data instanceof FormData)) { + xhr.setRequestHeader("Content-type", + "application/x-www-form-urlencoded"); + data = getString(data); + } + + xhr.onreadystatechange = function(event) { + if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { + callback(event.target.response); + } + }; + + xhr.send(data); +}; + +function $$(selector, el) { + if (!el) { + el = document; + } + return el.querySelectorAll(selector); + // Note: the returned object is a NodeList. + // If you'd like to convert it to a Array for convenience, use this instead: + // return Array.prototype.slice.call(el.querySelectorAll(selector)); +} + +// MDN: Assigning to an undeclared variable throws a ReferenceError +function isStrictMode() { + try { + mc_doesNotExist = true; + } + catch(E) { + console.log(E.message); + return true; + } + return false; +} + + diff --git a/static/js/controller.js b/static/js/controller.js new file mode 100644 index 0000000..a12362b --- /dev/null +++ b/static/js/controller.js @@ -0,0 +1,93 @@ +/* + * + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: controller.js + * + * + */ + +function ajaxData() { + var data = {}; + return data; +} + + +function log_msg(msg) { + let msg_str = ''; + + msg_str += msg.type + ': ' + msg.address + ' ' + msg.data + '<br>'; + + return msg_str; +} + +function postControllerMsg(event) { + $.loading('block'); + event.preventDefault(); + console.log("postControllerMsg"); + + let fd = new FormData($('#mc-cont_message')); + fd.append('dummy', 'foo') + + $.post("/controller", fd, function(resp) { + $.loading('none'); + resp = JSON.parse(resp); + if (resp.r == STATUS.OK) { + let log = document.getElementById('mc-log'); + let height = 2 * resp.d.length; + for (let i=0; i < resp.d.length; i++) { + msg = resp.d[i]; + if (msg.port == PORT_PC) { + log.innerHTML += '<div class="mc-log-row style=height:' + height.toString() + 'rem"><div class="mc-log-col-time">' + msg.time + '</div>' + + '<div class="mc-log-col-pc">' + log_msg(msg) + '</div>' + + '<div class="mc-log-col-phy0"></div>' + + '<div class="mc-log-col-phy1"></div></div>'; + } + else if (msg.port == PORT_PHY0) { + log.innerHTML += '<div class="mc-log-row"><div class="mc-log-col-time">' + msg.time + '</div>' + + '<div class="mc-log-col-pc"></div>' + + '<div class="mc-log-col-phy0">' + log_msg(msg) + + '</div><div class="mc-log-col-phy1"></div></div>'; + } + } + } + + console.log("done"); + }); + +} + +/* +The DOMContentLoaded event fires as soon as the HTML document has been fully parsed, +which typically occurs long before images, stylesheets, and other external resources +have loaded. +*/ +window.addEventListener("DOMContentLoaded", function(e) { + var MsgForm = $('#mc-cont_message') + try { + // $('button[value=submit]').addEventListener('click', postControllerMsg); + MsgForm.addEventListener('submit', postControllerMsg); + } catch (event) { + } +}); + +/* +The load event is essential for operations that require the entire web page to be fully loaded, +including all dependent resources like images and stylesheets. +This event ensures that every element is available for script manipulation. +*/ +window.addEventListener("load", function(e) { + console.log("controller loaded"); +});
\ No newline at end of file diff --git a/static/js/lmmi.js b/static/js/lmmi.js new file mode 100644 index 0000000..5cad4e5 --- /dev/null +++ b/static/js/lmmi.js @@ -0,0 +1,39 @@ +/* + * + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: lmmi.js + * + * + */ + + "use strict"; + +function ajaxData() { + var data = {}; + return data; +} + +/* +The load event is essential for operations that require the entire web page to be fully loaded, +including all dependent resources like images and stylesheets. +This event ensures that every element is available for script manipulation. +*/ +window.addEventListener("load", function(e) { + + ss = spreadSheet('.mc-ss-main-panel'); + $('#mc-ss-reload-all').addEventListener('click', ss.getNewTable); + console.log("lmmi loaded"); +});
\ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js new file mode 100644 index 0000000..3caa3a7 --- /dev/null +++ b/static/js/main.js @@ -0,0 +1,118 @@ +/* + * + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: main.js + * + */ + + +"use strict"; + +let selected_mode = MODE_LIVE; + +window.addEventListener("load", function(e) { + // re-initialize controls, as needed + /* + $('#mc-mode-live').checked=true; + $('#mc-filename').value = null; // clear any file that was previously selected + + // set up event listeners + $('#mc-mode-live').addEventListener('change',wl_changeMode); + $('#mc-mode-file').addEventListener('change',wl_changeMode); + $('#mc-filename').addEventListener('change',wl_filename); + $('#mc-start').addEventListener('click', wl_start); + $('#mc-stop').addEventListener('click', wl_stop); + $('#mc-help').addEventListener('click', wl_help); + $('#mc-clear').addEventListener('click', wl_clear); + */ + console.log("strict mode test: ",isStrictMode()); +}); + + +function wl_changeMode(event) { + event.preventDefault(); + console.log('change mode: ',event.target.id); + if (event.target.id == "mc-mode-file") { + selected_mode = MODE_FILE; + } + else { + selected_mode = MODE_LIVE; + } +} + +function wl_filename(event) { + event.preventDefault(); + $.loading('block'); + + let data = { 'filename': event.target.files[0].name }; + + $.post("/filename", data, function(resp) { + console.log("filename"); + }); + + $.loading('none'); +} + +function wl_start(event) { + event.preventDefault(); + let data = { 'mode': selected_mode } + $.loading('block'); + + $.post("/start", data, function(resp) { + $('#mc-mode').innerText = "Running"; + }); + // await new Promise(r => setTimeout(r, 1000)); + $.loading('none'); +} + +function wl_stop(event) { + event.preventDefault(); + let data = { 'mode': MODE_IDLE } + $('#mc-mode').innerText = "Idle"; + $.loading('block'); + $.post("/stop", data, function(resp) { + $('#mc-mode').innerText = "Idle"; + }); + $.loading('none'); +} + +function wl_help(event) { + event.preventDefault(); + m = modal('large'); + /* jshint multistr: true */ + m.display('<h2>GEODSS WebLogger Help:</h2> \ + <img width=800px src="/static/fiber_tap.svg">'); +} + +function wl_clear(event) { + event.preventDefault(); + let log = $('#mc-log'); + while (log.firstChild) { + log.removeChild(log.firstChild); + } +} + +function update_fw_version(fw_version) { + $('#mc-fw-version').innerText = fw_version.substring(2,4); +} + +function update_fw_increment(fw_increment) { + $('#mc-fw-increment').innerText = fw_increment.substring(2,4); +} + +function update_temperature(temperature) { + $('#mc-temperature').innerText = parseInt(temperature,16) + " \xB0F"; +} diff --git a/static/js/mdio.js b/static/js/mdio.js new file mode 100644 index 0000000..9185450 --- /dev/null +++ b/static/js/mdio.js @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: mdio.js + * + */ + + "use strict"; + +function ajaxData() { + var data = {}; + return data; +} + +/* +The load event is essential for operations that require the entire web page to be fully loaded, +including all dependent resources like images and stylesheets. +This event ensures that every element is available for script manipulation. +*/ +window.addEventListener("load", function(e) { + var ss = spreadSheet('.mc-ss-main-panel'); + $('#mc-ss-reload-all').addEventListener('click', ss.getNewTable); + console.log("mdio loaded"); +});
\ No newline at end of file diff --git a/static/js/mle.js b/static/js/mle.js new file mode 100644 index 0000000..0a28665 --- /dev/null +++ b/static/js/mle.js @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: mle.js + * + */ + + "use strict"; + +function ajaxData() { + var data = {}; + return data; +} + +/* +The load event is essential for operations that require the entire web page to be fully loaded, +including all dependent resources like images and stylesheets. +This event ensures that every element is available for script manipulation. +*/ +window.addEventListener("load", function(e) { + + console.log("mle loaded"); +});
\ No newline at end of file diff --git a/static/js/modal.js b/static/js/modal.js new file mode 100644 index 0000000..af4763b --- /dev/null +++ b/static/js/modal.js @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: modal.js + * + */ + +"use strict"; + +/* + * Modal TODO: stop using ids + * Only add and remove classes programmatically + * + */ + +function Modal(size) { + var modal = this; + this.modalContainer = $('#mc-modal'); + this.modalBody = $('#mc-modal-body', this.modalContainer); + this.modalBackdrop = $('#mc-modal-backdrop', this.modalContainer); + this.modalClose = $('#mc-modal-close', this.modalContainer); + + this.modalBody.classList=""; + + if (size !== undefined) { + this.modalBody.classList.add('mc-' + size); + } + + this.modalClose.addEventListener('click', function(event) { + event.preventDefault(); + event.stopPropagation(); + modal.modalContainer.style.display = 'none'; + if ($('#mc-modal-content')) { + el = $('#mc-modal-content'); + el.remove(); // IE supports removeNode + } + }); + this.modalBackdrop.addEventListener('click', function(event) { + event.preventDefault(); + event.stopPropagation(); + }); +} + +Modal.prototype = { + constructor : Modal, + display : modal_display, + hide : modal_hide, + clean : modal_clean, +}; + +function modal(size) { + return new Modal(size); +} + +function modal_display(content) { + var img, body, ratio, oldWidth, oldHeight; + this.clean(); + + if (content instanceof HTMLImageElement) { + img = content; + body = $('#mc-modal-body'); + + ratio = img.width / img.height; + + /* todo use the bigger img dimension */ + if (window.innerWidth - img.width < 20) { + img.width = window.innerWidth - 80; + img.height = img.width / ratio; + } + + if (window.innerHeight - img.height < 20) { + img.height = window.innerHeight - 80; + } + + body.style.top = String((window.innerHeight - img.height - 64) / 2) + 'px'; + body.style.bottom = String((window.innerHeight - img.height - 64) / 2) + 'px'; + body.style.left = String((window.innerWidth - img.width - 64) / 2) + 'px'; + body.style.right = String((window.innerWidth - img.width - 64) / 2) + 'px'; + + el = document.createElement("div"); + el.id = "mc-modal-content"; + el.innerHTML = '<img src="' + img.src + '">'; + this.modalBody.appendChild(el); + + } else { + el = document.createElement("div"); + el.id = "mc-modal-content"; + el.innerHTML = content; + this.modalBody.appendChild(el); + var dateInputs = $$('.mc-date', this.modalBody); + dateInputs.forEach(function(val) { + val.addEventListener('click', setDate); + }); + } + + this.modalContainer.style.display = 'block'; +} + +function modal_hide() { + this.modalContainer.style.display = 'none'; +} + +function modal_clean() { + var el; + if ((el = $('#mc-modal-content', this.modalContainer))) { + el.remove(); + } +}
\ No newline at end of file diff --git a/static/js/ss.js b/static/js/ss.js new file mode 100644 index 0000000..6f0310f --- /dev/null +++ b/static/js/ss.js @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: ss.js + * + */ + +"use strict"; + +/* constructor spreadsheet */ +function SpreadSheet(selector) { + var ss = this; + this.activeCell = null; + this.panel = $(selector); + this.panel.addEventListener('click', function(event){ + + if (event.target.classList.contains('mc-edit')) { + ss.editCell.call(ss, event); + } + else if (event.target.classList.contains('mc-icon') || event.target.nodeName==="IMG") { + ss.actions.call(ss, event); + } + else if (event.target.classList.contains('mc-sortable')) { + data = ajaxData(); + data.sort=event.target.innerHTML.toLowerCase(); + ss.getNewTable(event,data); + } + }); + this.getNewTable = function(event, data) { + $.loading('block'); + if (data == undefined) { + data = ajaxData(); + } + $.get('/' + getPrefix(), data, function(resp) { + resp = JSON.parse(resp); + let values = resp.values; + console.log(resp.values); + cols=$$(".mc-values"); + for (let i = 0; i < 16; i++) { + cols[i].textContent = "0x" + values[i].toString(16); + } + $.loading('none'); + }); + } +} + +SpreadSheet.prototype = { + constructor: SpreadSheet, + actions: spreadSheet_actions, + editCell: spreadSheet_editCell, + newRow: spreadSheet_newRow +}; + +function spreadSheet(selector) { + return new SpreadSheet(selector); +} + +function spreadSheet_actions(event) { + event.preventDefault(); // this fixed my href bug on 3/2/18! + $.loading('block'); + var ss = this; + var link, icon, td, tr, id, action, txt, el, data, m; + + if (event.target.nodeName==="IMG") { + icon = event.target; + link = icon.parentNode; + } + else { + link=event.target; + } + + action=link.dataset.action; + + td=link.parentNode.parentNode; // link is wrapped in a div and then a td cell + tr = td.parentElement; + id = tr.dataset.id; + + txt = td.innerHTML; + + // modal actions + if (action === 'edit' || action === 'cut' || action === 'notify' || action === 'modify') { + + $.get('/' + getPrefix() + '/' + action + '/' + id, null, + function(resp) { + var resp = getResponse(resp); + $.loading('none'); + + if (action === 'edit') { + m = modal('large'); + } + else { + m = modal(); + } + + if (resp.r === MC_CONSTANTS.REDIRECT) { + window.location.assign(resp.d); + } else if (resp.r === MC_CONSTANTS.NONE) { + m.display(resp.d) + $('button[value="submit"]', m.modalContainer) + .addEventListener('click', submitForm); + } else { + processError(resp); + } + }) + } + + // replace object actions + else if (action === "recalc") { + $.get('/' + getPrefix() + '/' + action + '/' + id, null, function(resp) { + var resp = getResponse(resp); + $.loading('none'); + tr.innerHTML = resp.d; + if (resp.n) { + m = modal('large'); + m.display(resp.n); + } + }); + } + + function submitForm(e) { + console.log('submit Form'); + $.loading('block'); + var form = $('form', m.modalContainer); + // var nodes = form.childNodes; + var data = new FormData(form); + // nodes.forEach(addToFormData, data); + + $.post('/' + getPrefix() + '/' + action + '/' + id, data, function(resp) { + resp = getResponse(resp); + if (resp.r === MC_CONSTANTS.NONE) { + m.hide(); + ss.getNewTable(e); + } else if (resp.r === MC_CONSTANTS.FORM) { + form.innerHTML=resp.d; + $('button[value="submit"]',m.modalContainer).addEventListener('click', submitForm ); + } else { + processError(resp); + } + $.loading('none'); + }); + } +} + +/* set up a handler for a new SpreadSheet item / row */ +function spreadSheet_newRow(selector) { + var ss = this; + var m = modal('large'); + try{ + $(selector).addEventListener('click',function(event) { + $.get('/' + getPrefix() + '/new/', null, function(resp) { + + resp = getResponse(resp); + if (resp.r === MC_CONSTANTS.NONE) { + m.display(resp.d); + $('button[value="submit"]', m.modalContainer).addEventListener('click', submitForm); + } else { + processError(resp); + } + }); + }); + } + catch(event) { + console.log('spreadSheet_newRow problem') + } + + function submitForm(e) { + $.loading('block'); + var form = $('form', m.modalContainer); + var nodes = form.childNodes; + var data = new FormData(); + nodes.forEach(addToFormData, data); + + $.post('/' + getPrefix() + '/new/', data, function(resp) { + resp = getResponse(resp); + if (resp.r === MC_CONSTANTS.NONE) { + m.hide(); + ss.getNewTable(e); + } else if (resp.r === MC_CONSTANTS.FORM) { + form.innerHTML=resp.d; + $('button[value="submit"]',m.modalContainer).addEventListener('click', submitForm ); + } else { + processError(resp); + } + $.loading('none'); + }); + } +} + + +function spreadSheet_editCell(event) { + console.log('editCell'); + var td=event.target; + var tr = td.parentElement; + var id = tr.dataset.id; + var type=td.dataset.type; + var field=td.dataset.field; + var txt = td.innerHTML; + var el, data; + + if (type==="text") { + td.innerHTML=""; + el = document.createElement("input"); + el.addEventListener('keypress', keyHandler); + $('body').addEventListener('click',clickHandler); + // append the input + el.className="mc-ss-input" + el.value=txt; + td.appendChild(el); + el.focus(); + } + + /* TODO: do I need to be worried that the handler won't be set in time? */ + else if (type==="date") { + var cal = calendar('.mc-cal'); + cal.container.addEventListener("evtCalClose", calCloseHandler); + cal.open(td); + } + + /* calendar has been closed */ + function calCloseHandler(event) { + console.log('calCloseHandler'); + cal.container.removeEventListener('evtCalClose',calCloseHandler); + + if (event.detail.cancel) { + return; + } + + $.loading('block'); + data = { + id: id, + field: "date", + value: cal.getSelectedDate().toISOString(), + }; + + $.post('/' + getPrefix() + '/modify', data, function(resp) { + resp = getResponse(resp); + if(resp.r == MC_CONSTANTS.NONE) { + td.innerHTML=resp.d; + } + else { + processError(resp); + } + $.loading('none'); + }); + } + + function keyHandler(event) { + console.log(keyHandler); + var el = event.target; + var key = event.keyCode; + var txt = el.value; + + if (key==27) { + event.preventDefault(); + el.remove(); + td.innerHTML=txt; + } + else if (key==13) { + event.preventDefault(); + var data = { + id: id, + field: field, + value: txt, + }; + + el.remove(); + td.innerHTML=txt; + + $.post('/' + getPrefix() + '/modify', data, function(response) { + console.log("upload text suceeded"); + }); + } + } + + function clickHandler(event) { + console.log('clickHandler'); + if(event.target.nodeName==="TD") { + return; // don't cancel out of a cell we just clicked + } + $('body').removeEventListener('click',clickHandler); + var children = td.childNodes; + children.forEach(function(val) { + val.remove(); + }) + td.innerHTML=txt; + } +} + + +
\ No newline at end of file diff --git a/static/js/ws.js b/static/js/ws.js new file mode 100644 index 0000000..bc814ff --- /dev/null +++ b/static/js/ws.js @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: ws.js + * + */ + +"use strict"; + +let socket = new WebSocket("ws://localhost:8010/logger"); +console.log("here we go"); +let state = "Wait"; +let log = document.getElementById('mc-log'); + +function log_msg(msg) { + let msg_str = ''; + + if (msg.num_cmds > 1) { + console.log('multi commands'); + } + + for (let i = 0; i < msg.num_cmds; i++) { + if (msg.cmds[i][0].includes('RDREG') || msg.cmds[i][0].includes('GETTEMP')) { + msg_str += msg.cmds[i][0] + ' ' + msg.cmds[i][1] + '<br>'; + } + else if (msg.cmds[i][0] == "" && (msg.cmds[i][1] == "PHY1_STATUS" || msg.cmds[i][1] == "PHY2_STATUS")) { + let val = parseInt(msg.cmds[i][2], 16) & 0x8; + if (val) { + msg_str += msg.cmds[i][1] + ': LOS = 1' + '<br>'; + } + else { + msg_str += msg.cmds[i][1] + ': LOS = 0' + '<br>'; + } + } + else { + msg_str += msg.cmds[i][0] + ' ' + msg.cmds[i][1] + ' ' + msg.cmds[i][2] + '<br>'; + } + } + + return msg_str; +} + +// setInterval(function(){console.log(state)},1000); + +socket.onopen = function(e) { + console.log("[open] Connection established"); + console.log("Sending to server"); + socket.send("open"); // can send data as a string, Blob, or ArrayBuffer. +}; + +function update_mode(mode) { + if (mode == MODE_IDLE) + $('#mc-mode').innerText = "Idle"; + else if (mode == MODE_LIVE || mode == MODE_FILE) + $('#mc-mode').innerText = "Running"; + else + console.log("unsupported mode") +} + +socket.onmessage = function(event) { + let num_msgs = 0; + + let msg = JSON.parse(event.data); + if (msg.format == MSG_FORMAT_BASIC) { + if (typeof msg['mode'] !== "undefined") { + console.log("mode: ", msg['mode']); + update_mode(msg['mode']); + } + else if (typeof msg['fw_version'] !== "undefined") { + console.log("fw_version: ", msg['fw_version']); + update_fw_version(msg['fw_version']); + } + else if (typeof msg['fw_increment'] !== "undefined") { + console.log("fw_increment: ", msg['fw_increment']); + update_fw_increment(msg['fw_increment']); + } + else if (typeof msg['temperature'] !== "undefined") { + console.log("temperature: ", msg['temperature']); + update_temperature(msg['temperature']); + } + else + console.log("unsupported control"); + + return; + } + + let height = 2 * msg.num_cmds; + if (height == 0) { + height = 2; + } + + num_msgs += 1; + console.log("From server:", num_msgs, ': ', msg); + + if (msg.port == PORT_PC) { + log.innerHTML += '<div class="mc-log-row style=height:' + height.toString() + 'rem"><div class="mc-log-col-time">' + msg.time + '</div>' + + '<div class="mc-log-col-pc">' + log_msg(msg) + '</div>' + + '<div class="mc-log-col-phy0"></div>' + + '<div class="mc-log-col-phy1"></div></div>'; + } + else if (msg.port == PORT_PHY0) { + log.innerHTML += '<div class="mc-log-row"><div class="mc-log-col-time">' + msg.time + '</div>' + + '<div class="mc-log-col-pc"></div>' + + '<div class="mc-log-col-phy0">' + log_msg(msg) + + '</div><div class="mc-log-col-phy1"></div></div>'; + } + else if (msg.port == PORT_PHY1) { + log.innerHTML += '<div class="mc-log-row"><div class="mc-log-col-time">' + msg.time + '</div>' + + '<div class="mc-log-col-pc"></div>' + + '<div class="mc-log-col-phy0"></div>' + + '<div class="mc-log-col-phy1">' + log_msg(msg) + '</div></div>'; + } + + // socket.send("request"); + state = "Request" +}; + +socket.onclose = function(event) { + state = "Closed" + if (event.wasClean) { + console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); + } else { + // e.g. server process killed or network down + // event.code is usually 1006 in this case + console.log('[close] Connection died'); + } +}; + +socket.onerror = function(error) { + state = "Error" + console.log(`[error]`); +}; + +// When you've finished using the WebSocket connection, +// call the WebSocket method close(): |



