579 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			579 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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 = true
 | 
						|
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
 | 
						|
global.observeTraffic = false
 | 
						|
 | 
						|
// 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}/index.html`,
 | 
						|
        devTools: DEVELOPMENT,
 | 
						|
        multiUserBrowser: true
 | 
						|
    }
 | 
						|
})
 | 
						|
 | 
						|
function createWindow() {
 | 
						|
 | 
						|
    if (global.observeTraffic) {
 | 
						|
        const {session} = require('electron')
 | 
						|
        session.defaultSession.webRequest.onCompleted((details) => {
 | 
						|
            console.log("onCompleted", details.url)
 | 
						|
        }) 
 | 
						|
    }
 | 
						|
    let { screen } = require('electron')
 | 
						|
    
 | 
						|
    let bounds = store.get('storedBounds')
 | 
						|
        ? store.get('storedBounds')
 | 
						|
        : screen.getPrimaryDisplay().bounds
 | 
						|
 | 
						|
    //    let displays = screen.getAllDisplays()
 | 
						|
    //    let externalDisplay = null
 | 
						|
 | 
						|
    //    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)
 | 
						|
    }
 | 
						|
    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) {
 | 
						|
        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()
 | 
						|
    })
 | 
						|
 | 
						|
    // 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)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function openBrowserView(url, x, y) {
 | 
						|
    const useMinBrowser = false // Change this to switch between Min and a custom browser
 | 
						|
 | 
						|
    const minURL = 'file:///Users/uo/devel/min/index.html'
 | 
						|
    const browserURL = `file://${__dirname}/browser.html`
 | 
						|
    let width = 640
 | 
						|
    let height = 1200
 | 
						|
    let [winWidth, winHeight] = win.getSize()
 | 
						|
    if (x + width > winWidth) {
 | 
						|
        x = winWidth - width
 | 
						|
    }
 | 
						|
    if (y + height > winHeight) {
 | 
						|
        y = winHeight - height
 | 
						|
    }
 | 
						|
    console.log('open browser view')
 | 
						|
    let browser = new BrowserWindow({
 | 
						|
        x,
 | 
						|
        y,
 | 
						|
        width,
 | 
						|
        height,
 | 
						|
        minWidth: 320,
 | 
						|
        minHeight: 350,
 | 
						|
        titleBarStyle: useMinBrowser ? 'hidden-inset' : 'hidden',
 | 
						|
        frame: process.platform !== 'win32'
 | 
						|
    })
 | 
						|
    let browserContents = browser.webContents
 | 
						|
    browser.setAlwaysOnTop(true)
 | 
						|
    if (useMinBrowser) {
 | 
						|
        browserContents.on('did-finish-load', event => {
 | 
						|
            console.log('did-finish-load', browserContents.getURL())
 | 
						|
 | 
						|
            browserContents.executeJavaScript(
 | 
						|
                'Object.values(window.webviews.elementMap).map(obj => obj.src)',
 | 
						|
                result => {
 | 
						|
                    console.log(
 | 
						|
                        'window.webviews',
 | 
						|
                        result,
 | 
						|
                        url,
 | 
						|
                        result.indexOf(url)
 | 
						|
                    )
 | 
						|
                    if (result.indexOf(url) == -1) {
 | 
						|
                        console.log('Adding tab')
 | 
						|
                        browserContents.send('addTab', { url })
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            )
 | 
						|
        })
 | 
						|
        browser.loadURL(minURL)
 | 
						|
    } else {
 | 
						|
        console.log('Loading', browserURL)
 | 
						|
        browser.loadURL(browserURL)
 | 
						|
        let view = new BrowserView({
 | 
						|
            webPreferences: {
 | 
						|
                nodeIntegration: false,
 | 
						|
                devTools: true
 | 
						|
            }
 | 
						|
        })
 | 
						|
        //browserContents.openDevTools({mode: 'right'})
 | 
						|
 | 
						|
        browser.setBrowserView(view)
 | 
						|
        view.setBounds({ x: 0, y: 24, width: width, height: height - 24 })
 | 
						|
        view.setAutoResize({ width: true, height: true })
 | 
						|
 | 
						|
        let viewContents = view.webContents
 | 
						|
        let progress = 0
 | 
						|
 | 
						|
        viewContents.on('page-title-set', event => {
 | 
						|
            console.log('page-title-set', event)
 | 
						|
        })
 | 
						|
        viewContents.on('page-favicon-updated', (event, favicons) => {
 | 
						|
            //console.log("page-favicon-updated", event, favicons)
 | 
						|
            trySend(browserContents, 'favicons', favicons)
 | 
						|
        })
 | 
						|
 | 
						|
        viewContents.on('did-start-loading', event => {
 | 
						|
            onProgress(0)
 | 
						|
            trySend(browserContents, 'progress', 0)
 | 
						|
            trySend(browserContents, 'did-start-loading')
 | 
						|
            //let senderURL = event.sender.getURL() || url
 | 
						|
            //console.log('did-start-loading', senderURL)
 | 
						|
        })
 | 
						|
        viewContents.on(
 | 
						|
            'did-get-response-details',
 | 
						|
            (
 | 
						|
                event,
 | 
						|
                status,
 | 
						|
                newURL,
 | 
						|
                originalURL,
 | 
						|
                httpResponseCode,
 | 
						|
                requestMethod,
 | 
						|
                referrer,
 | 
						|
                headers,
 | 
						|
                resourceType
 | 
						|
            ) => {
 | 
						|
                trySend(browserContents, 'did-get-response-details', {
 | 
						|
                    status,
 | 
						|
                    newURL,
 | 
						|
                    originalURL,
 | 
						|
                    httpResponseCode,
 | 
						|
                    requestMethod,
 | 
						|
                    referrer,
 | 
						|
                    headers,
 | 
						|
                    resourceType
 | 
						|
                })
 | 
						|
                progress += 0.01
 | 
						|
                onProgress(progress)
 | 
						|
                trySend(browserContents, 'progress', progress)
 | 
						|
                //console.log('did-get-response-details', newURL)
 | 
						|
            }
 | 
						|
        )
 | 
						|
        viewContents.on(
 | 
						|
            'did-get-redirect-request',
 | 
						|
            (
 | 
						|
                event,
 | 
						|
                oldURL,
 | 
						|
                newURL,
 | 
						|
                isMainFrame,
 | 
						|
                httpResponseCode,
 | 
						|
                requestMethod,
 | 
						|
                referrer,
 | 
						|
                headers
 | 
						|
            ) => {
 | 
						|
                trySend(browserContents, 'did-get-redirect-request', {
 | 
						|
                    oldURL,
 | 
						|
                    newURL,
 | 
						|
                    isMainFrame,
 | 
						|
                    httpResponseCode,
 | 
						|
                    requestMethod,
 | 
						|
                    referrer,
 | 
						|
                    headers
 | 
						|
                })
 | 
						|
                //console.log('did-get-redirect-request', newURL)
 | 
						|
            }
 | 
						|
        )
 | 
						|
        viewContents.on('did-stop-loading', event => {
 | 
						|
            //console.log('did-stop-loading', event.sender.getURL())
 | 
						|
            trySend(browserContents, 'did-stop-loading')
 | 
						|
        })
 | 
						|
        viewContents.on('did-finish-load', event => {
 | 
						|
            //console.log('did-finish-load', event.sender.getURL())
 | 
						|
            progress = 1
 | 
						|
            onProgress(progress)
 | 
						|
            trySend(browserContents, 'progress', progress)
 | 
						|
        })
 | 
						|
        viewContents.on('dom-ready', event => {
 | 
						|
            if (progress < 0.5) {
 | 
						|
                progress = 0.5
 | 
						|
                onProgress(progress)
 | 
						|
                trySend(browserContents, 'progress', progress)
 | 
						|
            }
 | 
						|
            viewContents.executeJavaScript('document.title', result => {
 | 
						|
                trySend(browserContents, 'title', result)
 | 
						|
            })
 | 
						|
 | 
						|
            //console.log('dom-ready', event.sender.getURL())
 | 
						|
        })
 | 
						|
 | 
						|
        viewContents.on('new-window', function (event, url) {
 | 
						|
            event.preventDefault()
 | 
						|
            console.log('new-window')
 | 
						|
            openBrowserView(url, x, y)
 | 
						|
        })
 | 
						|
 | 
						|
        viewContents.loadURL(url)
 | 
						|
    }
 | 
						|
    browsers.set(url, browser)
 | 
						|
    browser.on('closed', e => {
 | 
						|
        for (let [url, browser] of browsers.entries()) {
 | 
						|
            if (browser == e.sender) {
 | 
						|
                browsers.delete(url)
 | 
						|
                console.log('removed browser view', url)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    })
 | 
						|
}
 | 
						|
 | 
						|
// UO: Experimental call. Opens a Min Browser window or a limited window with a browser view
 | 
						|
ipcMain.on('loadBrowserView', (e, opts = {}) => {
 | 
						|
    let { url, x, y } = opts
 | 
						|
    openBrowserView(url, x, y)
 | 
						|
})
 | 
						|
 | 
						|
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)
 | 
						|
    try {
 | 
						|
        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)
 | 
						|
    } catch (err) {
 | 
						|
        let args = { directory, errorMessage: err.message}
 | 
						|
        e.sender.send('directoryListingError', args)
 | 
						|
    }
 | 
						|
})
 | 
						|
 | 
						|
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
 | 
						|
}
 |