/* globals require, __dirname, process */ /*eslint no-console: ["error", { allow: ["log"] }]*/ const { app, BrowserWindow, BrowserView, ipcMain, dialog, shell } = require('electron') const electronLocalshortcut = require('electron-localshortcut') //const electron = require('electron') const os = require('os') const fs = require('fs') const path = require('path') const { URL } = require('url') const Store = require('./store.js') const { prettyPrint } = require('html') // Use this constant to start the application in kiosk-mode or in development mode const DEVELOPMENT = true // true: Dev-Tools are open // false (KIOSK-Mode): No application switcher, no menu, no taskbar (or dock on a mac), shortcuts are working global.multiUserMode = false global.errorCount = 0 global.stopTestsOnError = false global.jsonData = { value: null } // UO: Experimental feature using Native Windows global.useBrowserView = false global.useMinimalPad = true global.menu = null // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. let win let browsers = new Map() // url: BrowserWindow const store = new Store({ // We'll call our data file 'user-preferences' configName: 'user-preferences', defaults: { url: `file://${__dirname}/test.html`, devTools: DEVELOPMENT, multiUserBrowser: true } }) function createWindow() { console.log("createWindow") const {session} = require('electron') session.defaultSession.on('will-download', (event, item, webContents) => { // event.preventDefault() }) let { screen } = require('electron') let displays = screen.getAllDisplays() let externalDisplay = null let bounds = store.get('storedBounds') ? store.get('storedBounds') : screen.getPrimaryDisplay().bounds // externalDisplay = displays[displays.length-1] // const {width, height} =displays[displays.length-1].workAreaSize // externalDisplay = displays[0] // const {width, height} =displays[0].workAreaSize win = new BrowserWindow({ x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height, fullscreenable: true, fullscreen: !DEVELOPMENT, title: 'IWM Browser', show: false, kiosk: !DEVELOPMENT, acceptFirstMouse: true, webPreferences: { webSecurity: false, allowRunningInsecureContent: true, nodeIntegration: true, webviewTag: true, nativeWindowOpen: true, devTools: true, preload: path.join(__dirname, './preload.js') }, icon: path.join(__dirname, 'assets/icons/png/64x64.png') }) module.exports.win = win let url = store.get('url') if (process.argv.length > 2) { let path = process.argv[2] url = `file://${__dirname}/../${path}` console.log('Using process.argv[2]', url) } let indpath = "index.html" url = url = `file://${__dirname}/${indpath}` console.log('Using', url) win.maximize() // BAD: All other methods don't work (like ensureFileSync, fileExists...) /*try { let settings = require('./settings.json') console.log('Using settings', `file://${__dirname}/${settings.url}`) win.loadURL(`file://${__dirname}/${settings.url}`) } catch (ex) { console.log('FAILED', url) win.loadURL(url) }*/ win.loadURL(url) const { webContents } = win // // if (process.platform === 'win32' && win.isKiosk()) { // webContents.on('did-finish-load', function() { // webContents.executeJavaScript('document.body.style.cursor = "none";') // }) // } // Add the app menu let menu = require('./menu.js') global.menu = menu // Add global shortcuts // Esc quits the app electronLocalshortcut.register('Esc', () => { app.quit() }) electronLocalshortcut.register('CommandOrControl+Shift+F', () => { win.webContents.send('globalSearch') }) // Command (Mac) or Control (Win) + K toggles the Kiosk mode electronLocalshortcut.register('CommandOrControl+K', () => { if (win) { win.setKiosk(!win.isKiosk()) } }) // Show if its ready. win.once('ready-to-show', () => { webContents.send('preparePads') win.show() }) // Clear cache webContents.session.clearCache(() => console.log('Cache cleared')) // Open dev tools when in development mode if (store.get('devTools')) { webContents.openDevTools({ mode: 'right' }) } else { webContents.closeDevTools() } webContents.on('devtools-opened', () => { store.set('devTools', true) }) webContents.on('devtools-closed', () => { store.set('devTools', false) }) webContents.on('did-navigate', (event, url) => { menu.setHistoryStatus() }) /* UO: At this point we have no access to the event or link position*/ webContents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures) => { console.log('new-window', global.multiUserMode) if (global.multiUserMode) { event.preventDefault() webContents.send('newPad', url, options.x, options.y) } }) // WORKAROUND: On windows, if the app was set to fullscreen, the menubar is not hidden if (win.isKiosk()) { win.setMenuBarVisibility(false) } win.on('focus', event => { menu.focus() }) win.on('blur', event => { menu.blur() }) win.on('enter-full-screen', () => { win.setMenuBarVisibility(false) }) win.on('leave-full-screen', () => { win.setMenuBarVisibility(true) }) win.on('enter-html-full-screen', () => { win.setMenuBarVisibility(false) }) win.on('leave-html-full-screen', () => { win.setMenuBarVisibility(true) }) win.on('close', () => { store.set('storedBounds', win.getBounds()) }) // Emitted when the window is closed. win.on('closed', () => { app.quit() }) } // When work makes progress, show the progress bar function onProgress(progress) { // Use values 0 to 1, or -1 to hide the progress bar try { win.setProgressBar(progress || -1) // Progress bar works on all platforms } catch (e) { if (DEVELOPMENT) console.log(e.message) } } function trySend(target, ...args) { try { target.send(...args) } catch (e) { if (DEVELOPMENT) console.log(e.message) } } ipcMain.on('multiUserMode', (e, opts = {}) => { global.multiUserMode = opts }) ipcMain.on('padContainerLoaded', e => { win.webContents.send('padContainerAvailable') }) ipcMain.on('createScreenshot', (e, opts = {}) => { opts = Object.assign( {}, { name: `iwmbrowser-${new Date() .toISOString() .replace(/:/g, '-')}.png`, path: os.tmpdir() }, opts ) win.webContents.capturePage(image => { if (image) { let file = path.join(opts.path, opts.name) fs.writeFile(file, image.toPNG(), err => { if (err) { //throw err } else { console.log(`Screenshot saved: ${file}`) } }) } }) }) ipcMain.on('directoryListing', (e, opts = {}) => { let { directory, files, folders } = opts console.log("directoryListing", opts) let listing = fs.readdirSync(directory) let result = { directory, files: [], folders: [] } for (let name of listing) { if (name.startsWith('.')) continue let fullPath = path.join(directory, name) let stat = fs.lstatSync(fullPath) if (files && stat.isFile()) { if (typeof files == 'string' && !files.endsWith(files)) continue result.files.push(name) } if (folders && stat.isDirectory()) result.folders.push(name) } e.sender.send('directoryListing', result) }) ipcMain.on('createTextfile', (e, opts = {}) => { opts = Object.assign( {}, { name: `iwmbrowser-${new Date() .toISOString() .replace(/:/g, '-')}.txt`, path: os.tmpdir(), text: '' }, opts ) let file = path.join(opts.path, opts.name) fs.writeFile(file, opts.text, err => { if (err) { //throw err } else { console.log(`Textfile saved: ${file}`) } }) }) ipcMain.on('error', e => { console.log('Received error notification') global.errorCount += 1 }) ipcMain.on('openExternal', (e, url=null) => { console.log('Received openExternal', url) if (url) { shell.openExternal(url) } }) ipcMain.on('save', (e, opts = {}) => { let { url, html, saveAs, action } = opts // url must absolute URL let urlObj = new URL(url) let pathname = urlObj.pathname if (saveAs) { pathname = dialog.showSaveDialog(win, { title: 'Save as:', defaultPath: pathname }) if (typeof pathname == 'undefined') return } try { console.log("Saving", pathname, action) html = prettyPrint(html, { indent_size: 4 }); fs.writeFileSync(pathname, html, 'utf-8') if (saveAs) { let normalized = pathname.replace(/\\/g, '/') e.sender.send('savedAs', {url: `file://${normalized}`, action}) } } catch (e) { console.warn('Failed to save the file', pathname) e.sender.send('saveFailed', pathname) } }) // 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) // Quit when all windows are closed. app.on('window-all-closed', () => { // On macOS it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== 'darwin') { app.quit() } }) app.on( 'select-client-certificate', (event, webContents, url, list, callback) => { console.log('select-client-certificate', url, list) event.preventDefault() ipc.once('client-certificate-selected', (event, item) => { console.log('selected:', item) callback(item) }) mainWindow.webContents.send('select-client-certificate', list) } ) app.on( 'certificate-error', (event, webContents, url, error, certificate, callback) => { console.log('certificate-error', url) event.preventDefault() const result = true // TODO: do real validation here callback(result) } ) app.on('activate', () => { // On macOS it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (win === null) { createWindow() } }) module.exports = { store: store }