diff --git a/index.js b/index.js index 0e81dec..61c895c 100644 --- a/index.js +++ b/index.js @@ -116,14 +116,16 @@ function log(text = "", type = mStat, socket = null, direction = 0) { if (type & mdLog) { //Send to Launcher log view let logView = $('log'); - //Note scroll position (to see if user has scrolled up), append message, then auto-scroll (down) if bottom was previously in view -// console.log(logView.scrollTop); - let scrollTop = logView.scrollTop; - let scroll = (scrollTop+1 >= logView.scrollHeight-logView.clientHeight); + //Note scroll position to maintain current view (user-set or auto-scroll) + let scrollPosition = logView.scrollTop; + let autoScroll = (scrollPosition+1 >= logView.scrollHeight-logView.clientHeight); + //Add log message, delete oldest (if necessary), update display, and reposition view (if necessary) logLines.push(stamp(verboseLogging) + text + '
'); - if (logLines.length > 250) {logLines.shift()} + let logIsMax = logLines.length > 250; + //TODO adjust scrollPosition by calculated line height + if (logIsMax) {logLines.shift(); scrollPosition -= 12;} logView.innerHTML = logLines.join(''); - if (scroll) {logView.scrollTo(0, logView.scrollHeight)} //else {if (scrollTop !== logView.ScrollTop) {logView.scrollTo(0, scrollTop-(logView.scrollTop-scrollTop))}} + if (autoScroll) {logView.scrollTo(0, logView.scrollHeight)} else {if (logIsMax) {logView.scrollTo(0, scrollPosition)}} } //Send to Launcher console window if (type & mdConsole) {console.log(stamp(true) + text)} @@ -174,7 +176,7 @@ var wxEnableDelay = null; // Default logging and preferred port (could be overridden by stored setting) var verboseLogging = defaultVerboseLogging; -var preferredPort = [{name: '', exists: false}]; +var storedPreferredPort = ''; document.addEventListener('DOMContentLoaded', function() { @@ -206,7 +208,7 @@ document.addEventListener('DOMContentLoaded', function() { $('wx-allow').checked = (result.en_wx !== undefined) ? result.en_wx : defaultWX; verboseLogging = (result.en_vlog !== undefined) ? result.en_vlog : defaultVerboseLogging; $('verbose-logging').checked = verboseLogging; - preferredPort.name = (result.pref_port !== undefined) ? result.pref_port : ''; + storedPreferredPort = (result.pref_port !== undefined) ? result.pref_port : ''; // Save subnet mask for future comparison (must be done here because chrome.storage.sync is asynchronous) sm = sm32bit(); } else { @@ -214,6 +216,7 @@ document.addEventListener('DOMContentLoaded', function() { } }) } else { + log('Launcher settings unavailable - using defaults', mDbug); $('bpc-port').value = defaultPort; $('bpc-url').value = defaultURL; $('sm0').value = defaultSM0; @@ -222,7 +225,7 @@ document.addEventListener('DOMContentLoaded', function() { $('sm3').value = defaultSM3; $('wx-allow').checked = defaultWX; $('verbose-logging').checked = defaultVerboseLogging; - preferredPort.name = ''; + storedPreferredPort = ''; // Save subnet mask for future comparison sm = sm32bit(); } @@ -319,16 +322,29 @@ document.addEventListener('DOMContentLoaded', function() { setTimeout(connect, 500); }); -function updatePreferredPort(port) { -// Remember new preferred port (if not null) - if (port && port !== preferredPort.name) { - preferredPort.name = port; - if (chrome.storage) { - chrome.storage.sync.set({'pref_port': preferredPort.name}, function () {if (chrome.runtime.lastError) {storageError()}}); - } - // A "new" port selection was made; set all existing ports to non-new status - clearNewPortStatus(); - } +function updatePreferredPort(port, socket) { +/* Remember new preferred port (if not null). + Returns true if port is new (to this socket); false otherwise. + socket (required) is the sender associated with this request. */ + let isNewPort = false; + if (port) { + // First, store in settings if new + if (port !== storedPreferredPort) { + storedPreferredPort = port; + if (chrome.storage) { + chrome.storage.sync.set({'pref_port': storedPreferredPort}, function () {if (chrome.runtime.lastError) {storageError()}}); + } + } + // Next, set as preferred for this socket, if new (for this socket) + let lister = portLister.find(function(p) {return p.socket === socket}); //Find the portLister object that belongs to this socket + isNewPort = (lister) && (lister.prefPort.name !== port); + if (isNewPort) { + lister.prefPort = {name: port, exists: true}; + // Since a new port selection was made, set all existing ports to non-new status - applies globally to all connected browsers (sockets) + clearNewPortStatus(); + } + } + return isNewPort; } function sm32bit() { @@ -338,7 +354,7 @@ function sm32bit() { function storageError() { // Log Chrome Storage error - log("Settings Error: " + chrome.runtime.lastError, mDbug); + log("Launcher Settings Error: " + chrome.runtime.lastError, mDbug); } function connect() { @@ -401,16 +417,16 @@ function connect_ws(ws_port, url_path) { if (ws_msg.type === "load-prop") { // load the propeller log('Received Propeller Application for ' + ws_msg.action, mDbug, socket, 1); - updatePreferredPort(ws_msg.portPath); + updatePreferredPort(ws_msg.portPath, socket); setTimeout(function() {loadPropeller(socket, ws_msg.portPath, ws_msg.action, ws_msg.payload, ws_msg.debug)}, 10); // success is a JSON that the browser generates and expects back to know if the load was successful or not } else if (ws_msg.type === "serial-terminal") { // open or close the serial port for terminal/debug - updatePreferredPort(ws_msg.portPath); + updatePreferredPort(ws_msg.portPath, socket); serialTerminal(socket, ws_msg.action, ws_msg.portPath, ws_msg.baudrate, ws_msg.msg); // action is "open", "close" or "msg" } else if (ws_msg.type === "pref-port") { // user selected a new preferred port - updatePreferredPort(ws_msg.portPath); log('User selected preferred port: ' + ws_msg.portPath, mDbug, socket, 1); + if (updatePreferredPort(ws_msg.portPath, socket)) {sendPortList(socket)}; } else if (ws_msg.type === "port-list-request") { // send an updated port list (and continue on scheduled interval) log('Site requested port list', mDbug, socket, 1); @@ -448,9 +464,9 @@ function connect_ws(ws_port, url_path) { // Port Lister management functions // The Port Lister items are timers (and sockets) to automatically send wired/wireless port updates to connected browser sockets function addPortLister(socket) { -//Create new port lister (to send port lists to browser on a timed interval). +//Create new port lister (to send port lists to browser on a timed interval) using last saved preferred port as the default. //socket is the browser socket to send updates to. - startPortListerScanner(portLister.push({socket: socket})-1); + startPortListerScanner(portLister.push({socket: socket, prefPort: {name: storedPreferredPort, exists: false}})-1); } function startPortListerScanner(idx) { @@ -577,10 +593,10 @@ function scanWXPorts() { } function sendPortList(socket) { -/* Send current list of communication ports to browser via socket. - (See "Launcher Communication Port Rules," above, for detailed rules and scenarios this function (and ports.js and index.js) implements.) - List is ordered as: blank (rarely) or preferred (if any) followed by sorted... new wired, old wired, new wireless, then old wireless ports. - New means newly-arrived (since last port selection changed); old means existing since before last port selection changed.*/ +/* Send list of current communication ports to browser via socket. + (See "Launcher Communication Port Rules," above, for detailed rules and scenarios that this function (and ports.js and index.js) implements.) + List is ordered as: blank (rarely) or preferred (if any) followed by individually sorted groups of new wired, old wired, new wireless, then old wireless ports. + "New" means newly-arrived (since last port selection changed); "old" means existing since before last port selection changed.*/ let bp = []; // Either empty (common) or blank string (rarely) let pp = []; // Peferred port name (qty 0 or 1) let nwp = []; // New wired port name list (often qty 0 or 1) @@ -589,34 +605,39 @@ function sendPortList(socket) { let owlp = []; // Old Wireless port (=> 0) let qty = 0; // Quantity of ports found - // gather separated port lists (preferred port (if any), new wired/wireless ports (if any), old wired/wireless ports, then sort them) - ports.forEach(function(p) { - if (p.name === preferredPort.name) { - pp.push(p.name) - } else { - if (p.isWired) { - if (p.new) {nwp.push(p.name)} else {owp.push(p.name)} + //Find the portLister object that belongs to this socket + let lister = portLister.find(function(p) {return p.socket === socket}); + if (lister) { + // Found our required lister object + // gather separated port lists (preferred port (if any), new wired/wireless ports (if any), old wired/wireless ports, then sort them) + ports.forEach(function(p) { + if (p.name === lister.prefPort.name) { + pp.push(p.name) } else { - if (p.new) {nwlp.push(p.name)} else {owlp.push(p.name)} + if (p.isWired) { + if (p.new) {nwp.push(p.name)} else {owp.push(p.name)} + } else { + if (p.new) {nwlp.push(p.name)} else {owlp.push(p.name)} + } } - } - }); - nwp.sort(); - owp.sort(); - nwlp.sort(); - owlp.sort(); - qty = pp.length+nwp.length+owp.length+nwlp.length+owlp.length; - - // Remember when the preferredPort exists; otherwise if preferredPort just disappeared, clear all "new" port statuses - we only care about new-arrivals since last preferred port selection - if (pp.length) {preferredPort.exists = true} else {if (preferredPort.exists) {preferredPort.exists = false; clearNewPortStatus();}} - - // report back to editor; blank (rarely), preferred port first (if any), new wired ports (if any), old wired ports, new wireless ports (if any), and finally old wireless ports - if (qty && !pp.length && !nwp.length) {bp.push("")} // Send leading blank port only if > 0 ports found, none match the preferred port, and there are no new wired ports - var msg_to_send = {type:'port-list',ports:bp.concat(pp.concat(nwp.concat(owp.concat(nwlp.concat(owlp)))))}; - - log('Sending port list' + (!verboseLogging ? ' (qty '+qty+')' : ': ' + msg_to_send.ports.toString()), mDbug, socket, -1); - socket.send(JSON.stringify(msg_to_send)); - if (chrome.runtime.lastError) {log(chrome.runtime.lastError, mDbug)} + }); + nwp.sort(); + owp.sort(); + nwlp.sort(); + owlp.sort(); + qty = pp.length+nwp.length+owp.length+nwlp.length+owlp.length; + + // Remember when the preferred port exists; otherwise if preferred port just disappeared, clear all "new" port statuses - we only care about new-arrivals since last preferred port selection + if (pp.length) {lister.prefPort.exists = true} else {if (lister.prefPort.exists) {lister.prefPort.exists = false; clearNewPortStatus();}} + + // report back to editor; blank (rarely), preferred port first (if any), new wired ports (if any), old wired ports, new wireless ports (if any), and finally old wireless ports + if (qty && !pp.length && !nwp.length) {bp.push("")} // Send leading blank port only if > 0 ports found, none match the preferred port, and there are no new wired ports + var msg_to_send = {type:'port-list',ports:bp.concat(pp.concat(nwp.concat(owp.concat(nwlp.concat(owlp)))))}; + + log('Sending port list' + (!verboseLogging ? ' (qty '+qty+')' : ': ' + msg_to_send.ports.toString()), mDbug, lister.socket, -1); + lister.socket.send(JSON.stringify(msg_to_send)); + if (chrome.runtime.lastError) {log(chrome.runtime.lastError, mDbug)} + } } diff --git a/manifest.json b/manifest.json index 22ceec2..662351a 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "BlocklyProp Launcher", "description": "A Chrome application that connects your Propeller-Powered Hardware to the BlocklyProp website.", - "version": "1.0.3", + "version": "1.0.4", "manifest_version": 2, "minimum_chrome_version": "45",