Commit 2b7d7b82 authored by Andrew Cohen's avatar Andrew Cohen

auto updater in main and experiment/image window, server tab

parent 96c8ca5d
......@@ -29,7 +29,7 @@
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
},
"args": [
"--image"],
],
"program": "${workspaceRoot}/elever/main.js"
},
{
......
!macro customInstall
createShortCut "$SMPROGRAMS\leverjsImageWindow.lnk" "$INSTDIR\leverjs.exe" "--image" "$INSTDIR\icon.ico" 0
createShortCut "$SMPROGRAMS\leverjsExperimentWindow.lnk" "$INSTDIR\leverjs.exe" "" "$INSTDIR\experimentWindow.ico" 0
!macroend
\ No newline at end of file
const electron = require('electron')
const electron = require('electron');
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow
......@@ -13,7 +15,6 @@ let mainWindow;
function createWindow ()
{
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600})
......@@ -33,9 +34,6 @@ function createWindow ()
slashes: true
}));
// Open the DevTools.
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
......@@ -89,6 +87,7 @@ ipcMain.on('locateFolder', (event,strDialogTitle) => {
}
});
});
function launchDataWindow(strFileName)
{
// get output folder path
......@@ -117,7 +116,6 @@ function launchDataWindow(strFileName)
var leverFile;
ipcMain.on('openFile', (event) => {
const {dialog} = require('electron');
const fs = require('fs');
const path = require('path');
......@@ -175,28 +173,25 @@ ipcMain.on('getDir', (event) => {
});
})
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
// app.on('ready', createWindow)
ipcMain.on('doUpdate', (event) => {
const update=require('./update.js');
update.doUpdate(mainWindow);
});
app.on('ready', () => {
function isDeployed()
{
return process.mainModule.filename.indexOf('app.asar') !== -1;
}
createWindow();
// this is called in window.onload by both experiment viewer and image window viewer
// when deployed...
ipcMain.on('ready', (event) => {
if (isDeployed()) {
const update=require('./update.js');
update.checkForUpdates(mainWindow);
}
});
// Register a shortcut listener.
/*const ret = electron.globalShortcut.register('CommandOrControl+o', () => {
console.log('CommandOrControl+o is pressed');
mainWindow.webContents.send('global-shortcut', "open");
})
if (!ret) {
console.log('registration failed')
}
*/
// Check whether a shortcut is registered.
// console.log(globalShortcut.isRegistered('CommandOrControl+X'))
})
// Quit when all windows are closed.
app.on('window-all-closed', function () {
......@@ -215,6 +210,9 @@ app.on('activate', function () {
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', () => {
createWindow();
})
\ No newline at end of file
// from https://gist.github.com/iffy/0ff845e8e3f59dbe7eaf2bf24443f104
const log = require('electron-log');
const electron = require('electron');
const {autoUpdater} = require("electron-updater");
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow
const app = electron.app
const url = require('url');
var win;
const path=require('path');
function sendStatusToWindow(text) {
log.info(text);
win.webContents.send('autoUpdater', text);
}
autoUpdater.on('checking-for-update', () => {
sendStatusToWindow('checking for updates');
})
autoUpdater.on('update-available', (ev, info) => {
sendStatusToWindow('update-available');
})
autoUpdater.on('update-not-available', (ev, info) => {
sendStatusToWindow('no update available.');
})
autoUpdater.on('error', (ev, err) => {
log.error(err);
sendStatusToWindow('update check failed');
})
autoUpdater.on('download-progress', (ev, progressObj) => {
sendStatusToWindow('progress');
})
autoUpdater.on('update-downloaded', (ev, info) => {
sendStatusToWindow('downloaded');
});
autoUpdater.on('update-downloaded', (ev, info) => {
autoUpdater.quitAndInstall();
});
function checkForUpdates(mainWindow)
{
// store a reference browser window for ui stuff
win=mainWindow;
autoUpdater.autoDownload=false;
autoUpdater.checkForUpdates();
}
function doUpdate()
{
autoUpdater.downloadUpdate();
}
module.exports.checkForUpdates=checkForUpdates;
module.exports.doUpdate=doUpdate;
\ No newline at end of file
......@@ -27,6 +27,13 @@
</head>
<body>
<div id="updateBar" style="display:none">
<div id="updateStatus"> no updates available </div>
<button id="updateButton" class="buttonTB" onClick="updateButtonClick()"> close </button>
<button id="downloadNow" class="buttonTB" onClick="updateButtonClick(true)" style="display:none">
download now </button>
</div>
<div id="sidebar">
<div id="sbResizeBorder"> </div>
<div id="sbTabHeader">
......@@ -98,7 +105,7 @@
<link href="https://fonts.googleapis.com/css?family=Roboto|Prompt" rel="stylesheet">
<link rel="stylesheet" href="./css/font-awesome-4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="./css/expSidebar.css">
<link rel="stylesheet" type="text/css" href="./css/updateBar.css">
<script src="ExperimentWindowBuilder.js" type="text/javascript"> </script>
<script src="lineage.js" type="text/javascript"> </script>
......
......@@ -20,15 +20,27 @@
//
/*****************************************************************************/
const IS_NODE = (typeof module !== 'undefined');
var gServerURL;
var gDB;
var g_db_path;
var ROOT;
var gbReadOnlyDB;
const IS_NODE = typeof module !== 'undefined' && module.exports;
// our ipc to the main thread in electron
// note ipc handlers sit at global scope - instantiating in a function can make them
// mulitplee instanced
var ipcRenderer;
if (IS_NODE) {
const updateBar=require('./updateBar.js');
ipcRenderer= require('electron').ipcRenderer;
ipcRenderer.on('autoUpdater', (event, text) => {
const updateBar=require('./updateBar.js');
updateBar.processUpdateEvent(event,text);
});
}
function processSelected(bAddAll)
{
......@@ -564,7 +576,9 @@ window.onload = function ()
var rootDiv=document.getElementById('divRoot');
var bRedraw=true;
if (undefined!==IS_NODE && IS_NODE) {
if (IS_NODE) {
// let the main thread know we're ready for any ui work she needs done
ipcRenderer.send('ready');
sbTab = document.getElementById('sbtabImport');
sbTab.style.display = 'inline-block';
setRootDir();
......
......@@ -26,15 +26,25 @@
<title>LEVER.js</title>
</head>
<body style="background-color:gray;">
<div id="logoDiv">
<div id="imageHeader"><a href="./ExperimentWindow.html"><img id="logo" src="uiImages/icon.ico" width="64" style="z-index: 6;"><br>leverjs.net</a></div>
</div>
<link rel="stylesheet" type="text/css" href="./css/image.css">
<link rel="stylesheet" type="text/css" href="./css/sidebar.css">
<link rel="stylesheet" type="text/css" href="./css/context.css">
<link rel="stylesheet" href="./css/font-awesome-4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="./css/webToolbar.css">
<link rel="stylesheet" type="text/css" href="./css/updateBar.css">
<div id="updateBar" style="display:none">
<div id="updateStatus"> no updates available </div>
<button id="updateButton" class="buttonTB" onClick="updateButtonClick()"> close </button>
<button id="downloadNow" class="buttonTB" onClick="updateButtonClick(true)" style="display:none">
download now </button>
</div>
<div id="logoDiv">
<div id="imageHeader"><a href="./ExperimentWindow.html"><img id="logo" src="uiImages/icon.ico" width="64" style="z-index: 6;"><br>leverjs.net</a></div>
</div>
<!-- WEB TOOLBAR -->
<div id="webToolbar" type="toolbarButton" style="z-index:2;">
......@@ -79,6 +89,7 @@
<div class="sbTab" id="sbtabEdit" onclick="sbtabClick('Edit')">Edit</div>
<div class="sbTab" id="sbtabAlgorithms" onclick="sbtabClick('Algorithms')">Algorithms</div>
<div class="sbTab" id="sbtabAuthorization" onclick="sbtabClick('Authorization')">Authorization </div>
<div class="sbTab" id="sbtabServer" onclick="sbtabClick('Server')">Server </div>
</div>
<div id="sbPanelContainer">
<div id='sbLineage'>
......@@ -146,6 +157,20 @@
</div>
<div id="sbAuthorization" style='display:none'>
</div>
<div id="sbServer" style='display:none'>
<div> <br> <br> Allows visualization to be controlled from external scripting. </div>
<div> see https://leverjs.net/examples/code/uiServer for details </div>
<label for="uiServerPort">port</label>
<input id='uiServerPort' type='number' value=4444></input>
<button class='sbButton' id="btnStartUIserver" tabindex="1" onclick="startUIserver()">
start UI server</button>
<div> <br><br> start a lever server in the current folder </div>
<div> point browser (chrome/firefox/opera) at http://localhost:port </div>
<label for="leverServerPort">port</label>
<input id='leverServerPort' type='number' value=3000></input>
<button class='sbButton' id="btnStartLeverServer" tabindex="1" onclick="startLeverServer(true)">
start lever server</button>
</div>
</div>
</div>
......
......@@ -33,41 +33,37 @@ var gnFrames=1;
// Flag to tell if the animation is running
var gbPlayMovie=0;
// Used to find the average FPS
var gFPSArray = new Array();
// Scales the hulls when zoomed
var gScale = 1;
// Top left corner of the image clipping
var ImgClippingX= 0, ImgClippingY = 0;
// The height and width of the clipping
var ImgClippingWidth;
var ImgClippingHeight;
var gImgWidth;
var gImgHeight;
var miniMapmousedrag = false;
var gRedBoxWidth;
var gRedBoxHeight;
// database is read only
var gbReadOnlyDB=false;
// Scaling image factor
var gScalingFacX;
var gScalingFacY;
const IS_NODE = typeof module !== 'undefined' && module.exports;
// Local ID flag that tells whether the user is looking at global IDs or local IDs
var gLocalIDFlag;
// our ipc to the main thread in electron
// note ipc handlers sit at global scope - instantiating in a function can make them
// mulitplee instanced
var ipcRenderer;
// Bounding box for main clone
var CloneX;
var CloneY;
var CloneSizeX;
var CloneSizeY;
if (IS_NODE) {
const updateBar=require('./updateBar.js');
ipcRenderer= require('electron').ipcRenderer;
// note -- filename needs be the last argument in the list...
ipcRenderer.on('fileName', (event, fullFileName) => {
if (gbServer && gbCaptureMovieAndExit)
console.error('ERROR: cannot set both reseg and capture movie');
open_DB_File(fullFileName);
});
ipcRenderer.on('autoUpdater', (event, text) => {
const updateBar=require('./updateBar.js');
updateBar.processUpdateEvent(event,text);
});
}
// database is read only
var gbReadOnlyDB=false;
// for vscode debugging render process, starts in vs code folder...
// cwd to the argv0 -- path to our leverjs/leverjs...
......@@ -84,7 +80,6 @@ function setRootDir()
}
} // setRootDir
const IS_NODE = typeof module !== 'undefined' && module.exports;
// called to update web toolbar, only from browser mode
function setToolUI(CONSTANTS)
......@@ -113,13 +108,6 @@ function setToolUI(CONSTANTS)
}
} // setToolUI
function setMovieUI()
{
document.getElementById('sidebar').style.display='none';
// document.getElementById('removeAllExtFamily').style.display='none';
// document.getElementById('addAllExtFamily').style.display='none';
} // setMovieUI
var gbServer=false;
var gbCaptureMovieAndExit=false;
......@@ -138,45 +126,6 @@ function setUI()
var gServerURL;
var g_db_path;
function processStartupArgs()
{
// open_DB_File(g_db_path);
// fun with electon -- show a file open/save dialog...
const {dialog}=require('electron').remote;
var ffn;
var fullFileName;
const {ipcRenderer} = require('electron')
// note -- showing dialogs is done from the main thread. electron gets wierd with dialogs in
// the browser thread for now. so, we use ipc to control. see main.js
ipcRenderer.send('openFile', () => {
console.log("Event sent.");
});
ipcRenderer.on('setArgs', (event, strArgs) => {
switch (strArgs) {
case '-captureMovieAndExit':
console.log('processStartupArgs: gbCaptureMovieAndExit set to true! ');
gbCaptureMovieAndExit=true;
gbReadOnlyDB=true;
setMovieUI();
break;
default:
console.error('UNKNOWN COMMAND LINE OPTION');
}
});
// note -- filename needs be the last argument in the list...
ipcRenderer.on('fileName', (event, fullFileName) => {
if (gbServer && gbCaptureMovieAndExit)
console.error('ERROR: cannot set both reseg and capture movie');
open_DB_File(fullFileName);
});
} // processStartupArgs
// Runs when the window is first open
window.onload = function () {
......@@ -193,23 +142,23 @@ window.onload = function () {
}
g_db_path=g_db_path.substring(1);
if (IS_NODE) {
//document.getElementById('resegBar').style.display='block';
document.getElementById('sbtabAuthorization').style.display='none';
setRootDir();
if (null===g_db_path || g_db_path.length<2)
processStartupArgs();
if (null===g_db_path || g_db_path.length<2) {
// we weren't opened by experiment window (no incoming path)
// let the main thread know we're ready for any ui work she needs done
ipcRenderer.send('ready');
// get main thread to pop file dialog for user to pick .lever file
ipcRenderer.send('openFile');
}
else
open_DB_File(g_db_path);
// const uiServer=require('./uiServer.js');
// uiServer.init(this);
}
else {
// in the browser!
// gbReadOnlyDB=true;
document.getElementById('sbtabServer').style.display='none';
var constantsReq = new XMLHttpRequest();
var pathArray = window.location.href.split( '/' );
gServerURL='';
for (var i=0;i<pathArray.length-1;i++)
......
#updateBar{
position:fixed;
top:5px;
left:20%;
width: 40%;
background-color:rgba(79, 171, 232, 1);
color: white;
z-index:100;
height: 2em;
}
#updateStatus{
float: left;
margin-left: 2em;
margin-top: 5px;
height: 60%;
}
#downloadNow, #updateButton {
float:right;
margin-right: 1em;
margin-top: 3px;
width: auto;
height: 80%;
}
#downloadNow{
margin-right: 5em;
}
......@@ -199,6 +199,10 @@ function sbtabClick(strID)
sbPanelShow('sbAuthorization');
updateAuthorization();
break;
case 'Server':
sbPanelShow('sbServer');
// updateServer();
break;
case 'Process':
sbPanelShow('sbProcess');
updateProcess();
......@@ -363,3 +367,72 @@ function updateEditProcess()
}
} // updateProcess
function startUIserver()
{
if (!IS_NODE)
return;
const uiServer=require('./uiServer.js');
var port=document.getElementById('uiServerPort').value;
port=+port;
if (port<1 || isNaN(port))
return;
uiServer.init(port,window);
// update ui
document.getElementById('btnStartUIserver').innerText='server running';
document.getElementById('btnStartUIserver').disabled=true;
}
var leverServer=null;
function startLeverServer()
{
if (!IS_NODE)
return;
if (null!==leverServer) {
// server is running - kill it and reset
leverServer.kill();
leverServer=null;
document.getElementById('btnStartLeverServer').innerText='start lever server';
return;
}
const path=require('path');
const spawn = require('child_process').spawn;
var port=document.getElementById('leverServerPort').value;
port=+port;
if (port<1 || isNaN(port))
return;
var serverPath=path.dirname(g_db_path);
var serverCmd;
var isWin = /^win/.test(process.platform);
if (isWin)
serverCmd='node.exe'
else
serverCmd='node';
var serverArgs=['server.js','--port='+port,serverPath];
console.log('launching leverServer : '+serverCmd+' '+serverArgs);
leverServer=spawn(serverCmd,serverArgs);
// update ui
document.getElementById('btnStartLeverServer').innerText='kill running lever server';
} // startLeverServer
// helper for updateBar.js
function updateButtonClick(bUpdate)
{
const updateBar=require('./updateBar.js');
updateBar.updateButtonHandler(bUpdate);
}
\ No newline at end of file
......@@ -2,12 +2,15 @@ const express = require('express');
const app = express();
const bodyParser = require('body-parser');
var jsonParser = bodyParser.json({limit:'100mb'});
var PORT=4444;
var gWindow;
function init(window)
function init(port,window)
{
gWindow=window;
// this starts the server
app.listen(port, function () {
console.log('uiServer: listening on port: ' + port);
});
}
app.post('/view', jsonParser,function (req, res) {
......@@ -90,10 +93,6 @@ app.get('/screenCap',function(req,res){
});
// this starts the server
app.listen(PORT, function () {
console.log('uiServer: listening on port: ' + PORT);
});
module.exports.init=init;
\ No newline at end of file
// updateBar.js
const log = require('electron-log');
bUpdateInProgress=false;
function updateButtonHandler(bUpdate)
{
if (bUpdate){
log.info('user requests do update!');
eventUpdate.sender.send('doUpdate');
var updateButton=document.getElementById('updateButton');
updateButton.style.display='none';
var downloadButton=document.getElementById('downloadNow');
downloadButton.style.display='none';
bUpdateInProgress=true;
return;
}
if (bUpdateInProgress)
return;
var updateBar=document.getElementById('updateBar');
updateBar.style.display="none";
if (hideUpdateTimer>0) {
hideUpdateTimer=-1;
clearTimeout(hideUpdateTimer);
}
}
var hideUpdateTimer=-1;
var eventUpdate;
function handleUpdateAvailable(event)
{
eventUpdate=event;
var updateBar=document.getElementById('updateBar');
updateBar.style.display="inline";
var status=document.getElementById('updateStatus');
status.innerHTML='update available!';
var updateButton=document.getElementById('updateButton');
updateButton.innerHTML='download later';
var downloadButton=document.getElementById('downloadNow');
downloadButton.style.display='inline';
hideUpdateTimer=setTimeout(updateButtonHandler, 30000);
}
var hideUpdateTimer=-1;
var processIndicator='';
function processUpdateEvent(event,text)
{
log.info(text);
if (hideUpdateTimer>0) {
clearTimeout(hideUpdateTimer);
hideUpdateTimer=-1;
}
if (text==='update-available'){
handleUpdateAvailable(event);
return;
}
var updateButton=document.getElementById('updateButton');
updateButton.innerHTML='close';
var updateBar=document.getElementById('updateBar');
updateBar.style.display="inline";
if (text==='progress') {
if (processIndicator.length>10)
processIndicator='.';
else
processIndicator+='.';
text+='downloading'+processIndicator;
}
var status=document.getElementById('updateStatus');
status.innerHTML=text;
hideUpdateTimer=setTimeout(updateButtonHandler, 5000);
}
module.exports.processUpdateEvent=processUpdateEvent;
module.exports.updateButtonHandler=updateButtonHandler;
\ No newline at end of file
shut off ui elements
% shut off ui elements
UISERVER='http://localhost:4444/'
ui=webread([UISERVER 'ui']);
......@@ -25,6 +25,8 @@ end
im=webread([UISERVER 'screenCap']);
imagesc(im)
axis image
axis off
view=webread([UISERVER 'view']);
options = weboptions('MediaType','application/json');
theta=1/pi;
......
......@@ -12,11 +12,13 @@
"author": "andrew r. cohen, mark winter, edgar cardenas",
"license": "UNLICENSED",
"dependencies": {
"electron-log": "^2.2.9",
"express-jwt": "^5.3.0",
"express.js": "^1.0.0",
"jwks-rsa": "^1.2.0",
"minimist": "^1.2.0",
"sqlite3": "^3.1.13"
"sqlite3": "^3.1.13",
"electron-updater": "^2.10.1"
},
"devDependencies": {
"electron": "1.7.8",
......@@ -24,19 +26,35 @@
"sqlite3": "^3.1.13"
},
"build": {
"files":{
"filter": ["**/*", "!bin/*","!prebuilt/matlabPollDB","!matlab/*"]
"publish": [