[
  {
    "path": ".gitignore",
    "content": ".idea/\nnode_modules\nnpm-debug.log\ndist\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 Quentin RENARD\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "`astilectron` is an Electron app that provides an API over a TCP socket that allows executing Electron's method as well as capturing Electron's events.\n\n# Warning\n\nThis project is not maintained anymore.\n\n# Architecture\n\n    +-----------------------+    TCP    +-------------+    IPC   +---------------------+\n    + Client App (any Lang) |<--------->+ Astilectron +<-------->+ win1: (HTML/JS/CSS) +\n    +-----------------------+           +-------------+     |    +---------------------++\n                 |                             |            +---->+ win2: (HTML/JS/CSS) +\n                 |         +----------+        |               |  +---------------------++\n                 +---------+ Electron +--------+               +-->+ win3: (HTML/JS/CSS) +\n                           +----------+                            +---------------------+\n                            \n# Language bindings\n\nLanguage bindings play a major role with `astilectron` as they allow communicating with its TCP socket and therefore interacting with its API in any language.\n\n## I want to develop language bindings for a new language\n\nGreat! :)\n\nHere's a few things you need to know:\n\n- it's the responsibility of the language bindings to provision `astilectron` which usually consists of downloading and unzipping `astilectron` as well as `electron`\n- the TCP addr is sent to `astilectron` through a command line argument therefore your command line when calling `astilectron` should look like `<path to electron executable> <path to astilectron directory>/main.js <tcp addr>`\n\n## Language bindings for GO\n\nCheck out [go-astilectron](https://github.com/asticode/go-astilectron) for `astilectron` GO language bindings\n\n# Features and roadmap\n\n- [x] window basic methods (create, show, close, resize, minimize, maximize, ...)\n- [x] window basic events (close, blur, focus, unresponsive, crashed, ...)\n- [x] remote messaging (messages between GO and the JS in the webserver)\n- [x] multi screens/displays\n- [x] menu methods and events (create, insert, append, popup, clicked, ...)\n- [x] dialogs (open or save file, alerts, ...)\n- [ ] accelerators (shortcuts)\n- [ ] file methods (drag & drop, ...)\n- [ ] clipboard methods\n- [ ] power monitor events (suspend, resume, ...)\n- [ ] notifications (macosx)\n- [ ] desktop capturer (audio and video)\n- [ ] session methods\n- [ ] session events\n- [ ] window advanced options (add missing ones)\n- [ ] window advanced methods (add missing ones)\n- [ ] window advanced events (add missing ones)\n- [ ] child windows\n\n# Contribute\n\nFor now only GO has its official bindings with `astilectron`, but the more language has its bindings the better! Therefore if you feel like implementing bindings with `astilectron` in some other language feel free to reach out to me to get some more info and finally to get your repo listed here.\n\nAlso I'm far from being an expert in Node.JS therefore if you see anything that seems wrong in `astilectron` feel free to create an issue or even better contribute through a PR!\n\nYou know you want to! :D\n\n# Cheers to\n\n[thrust](https://github.com/breach/thrust) which is awesome but unfortunately not maintained anymore. It inspired this project.\n"
  },
  {
    "path": "index.js",
    "content": "// @ts-check\n'use strict'\n\nconst electron = require('electron')\nconst {app, BrowserWindow, ipcMain, Menu, MenuItem, Tray, dialog, Notification, globalShortcut} = electron\nconst consts = require('./src/consts.js')\nconst client = require('./src/client.js')\nconst readline = require('readline')\n\nlet rl;\nlet callbacks = {};\nlet counters = {};\nlet elements = {};\nlet windowOptions = {};\nlet menus = {};\nlet quittingApp = false;\n\n// Single instance\nlet lastWindowId = null;\n\n// App is quitting\nconst beforeQuit = () => {\n    quittingApp = true;\n    client.write(consts.targetIds.app,consts.eventNames.appCmdQuit);\n};\n\n// App is ready\nfunction onReady () {\n    // Init\n    const screen = electron.screen\n    Menu.setApplicationMenu(null)\n\n    // Listen to screen events\n    screen.on('display-added', function() {\n        client.write(consts.targetIds.app, consts.eventNames.displayEventAdded, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}})\n    })\n    screen.on('display-metrics-changed', function() {\n        client.write(consts.targetIds.app, consts.eventNames.displayEventMetricsChanged, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}})\n    })\n    screen.on('display-removed', function() {\n        client.write(consts.targetIds.app, consts.eventNames.displayEventRemoved, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}})\n    })\n\n    const powerMonitor = electron.powerMonitor\n    // Listen to power events\n    powerMonitor.on('suspend', function() {\n        client.write(consts.targetIds.app, consts.eventNames.powerEventSuspend)\n    })\n\n    powerMonitor.on('resume', function() {\n        client.write(consts.targetIds.app, consts.eventNames.powerEventResume)\n    })\n\n    powerMonitor.on('on-ac', function() {\n        client.write(consts.targetIds.app, consts.eventNames.powerEventOnAC)\n    })\n\n    powerMonitor.on('on-battery', function () {\n        client.write(consts.targetIds.app, consts.eventNames.powerEventOnBattery)\n    })\n\n    powerMonitor.on('shutdown', function() {\n        client.write(consts.targetIds.app, consts.eventNames.powerEventShutdown)\n    })\n\n    powerMonitor.on('lock-screen', function() {\n        client.write(consts.targetIds.app, consts.eventNames.powerEventLockScreen)\n    })\n\n    powerMonitor.on('unlock-screen', function() {\n        client.write(consts.targetIds.app, consts.eventNames.powerEventUnlockScreen)\n    })\n\n    powerMonitor.on('user-did-become-active', function() {\n        client.write(consts.targetIds.app, consts.eventNames.powerEventUserDidBecomeActive)\n    })\n\n    powerMonitor.on('user-did-resign-active', function() {\n        client.write(consts.targetIds.app, consts.eventNames.powerEventUserDidResignActive)\n    })\n\n    // Listen on main ipcMain\n    ipcMain.on(consts.eventNames.ipcEventMessage, (event, arg) => {\n        let payload = {message: arg.message};\n        if (typeof arg.callbackId !== \"undefined\") payload.callbackId = arg.callbackId;\n        client.write(arg.targetID, consts.eventNames.windowEventMessage, payload)\n    });\n    ipcMain.on(consts.eventNames.ipcEventMessageCallback, (event, arg) => {\n        let payload = {message: arg.message};\n        if (typeof arg.callbackId !== \"undefined\") payload.callbackId = arg.callbackId;\n        client.write(arg.targetID, consts.eventNames.windowEventMessageCallback, payload)\n    });\n\n    // Read from client\n    rl.on('line', function(line){\n        // Parse the JSON\n        let json = JSON.parse(line)\n\n        // Switch on event name\n        let window;\n        switch (json.name) {\n            // App\n            case consts.eventNames.appCmdQuit:\n            app.quit();\n            break;\n\n            // Dock\n            case consts.eventNames.dockCmdBounce:\n            let id = 0;\n            if (typeof app.dock !== \"undefined\") {\n                id = app.dock.bounce(json.bounceType);\n            }\n            client.write(consts.targetIds.dock, consts.eventNames.dockEventBouncing, {id: id});\n            break;\n            case consts.eventNames.dockCmdBounceDownloads:\n            if (typeof app.dock !== \"undefined\") {\n                app.dock.downloadFinished(json.filePath);\n            }\n            client.write(consts.targetIds.dock, consts.eventNames.dockEventDownloadsBouncing);\n            break;\n            case consts.eventNames.dockCmdCancelBounce:\n            if (typeof app.dock !== \"undefined\") {\n                app.dock.cancelBounce(json.id);\n            }\n            client.write(consts.targetIds.dock, consts.eventNames.dockEventBouncingCancelled);\n            break;\n            case consts.eventNames.dockCmdHide:\n            if (typeof app.dock !== \"undefined\") {\n                app.dock.hide();\n            }\n            client.write(consts.targetIds.dock, consts.eventNames.dockEventHidden);\n            break;\n            case consts.eventNames.dockCmdSetBadge:\n            if (typeof app.dock !== \"undefined\") {\n                app.dock.setBadge(json.badge);\n            }\n            client.write(consts.targetIds.dock, consts.eventNames.dockEventBadgeSet);\n            break;\n            case consts.eventNames.dockCmdSetIcon:\n            if (typeof app.dock !== \"undefined\") {\n                app.dock.setIcon(json.image);\n            }\n            client.write(consts.targetIds.dock, consts.eventNames.dockEventIconSet);\n            break;\n            case consts.eventNames.dockCmdShow:\n            if (typeof app.dock !== \"undefined\") {\n                app.dock.show();\n            }\n            client.write(consts.targetIds.dock, consts.eventNames.dockEventShown);\n            break;\n\n            // Menu\n            case consts.eventNames.menuCmdCreate:\n            menuCreate(json.menu)\n            menus[json.menu.rootId] = json.targetID\n            setMenu(json.menu.rootId)\n            client.write(json.targetID, consts.eventNames.menuEventCreated)\n            break;\n            case consts.eventNames.menuCmdDestroy:\n            elements[json.targetID] = null\n            if (menus[json.menu.rootId] === json.targetID) {\n                menus[json.menu.rootId] = null\n                setMenu(json.menu.rootId)\n            }\n            client.write(json.targetID, consts.eventNames.menuEventDestroyed)\n            break;\n\n            // Menu item\n            case consts.eventNames.menuItemCmdSetChecked:\n            elements[json.targetID].checked = json.menuItemOptions.checked\n            client.write(json.targetID, consts.eventNames.menuItemEventCheckedSet)\n            break;\n            case consts.eventNames.menuItemCmdSetEnabled:\n            elements[json.targetID].enabled = json.menuItemOptions.enabled\n            client.write(json.targetID, consts.eventNames.menuItemEventEnabledSet)\n            break;\n            case consts.eventNames.menuItemCmdSetLabel:\n            elements[json.targetID].label = json.menuItemOptions.label\n            client.write(json.targetID, consts.eventNames.menuItemEventLabelSet)\n            break;\n            case consts.eventNames.menuItemCmdSetVisible:\n            elements[json.targetID].visible = json.menuItemOptions.visible\n            client.write(json.targetID, consts.eventNames.menuItemEventVisibleSet)\n            break;\n\n            // Notification\n            case consts.eventNames.notificationCmdCreate:\n            notificationCreate(json);\n            break;\n            case consts.eventNames.notificationCmdShow:\n            if (Notification.isSupported()) {\n                elements[json.targetID].show();\n            }\n            break;\n\n            // Session\n            case consts.eventNames.sessionCmdClearCache:\n            elements[json.targetID].clearCache().then(() => {\n                client.write(json.targetID, consts.eventNames.sessionEventClearedCache)\n            })\n            break;\n            case consts.eventNames.sessionCmdFlushStorage:\n            elements[json.targetID].flushStorageData();\n            client.write(json.targetID, consts.eventNames.sessionEventFlushedStorage)\n            break;\n            case consts.eventNames.sessionCmdLoadExtension:\n            elements[json.targetID].loadExtension(json.path).then(() => {\n                client.write(json.targetID, consts.eventNames.sessionEventLoadedExtension)\n            })\n            break;\n\n            // Sub menu\n            case consts.eventNames.subMenuCmdAppend:\n            elements[json.targetID].append(menuItemCreate(json.menuItem))\n            setMenu(json.menuItem.rootId)\n            client.write(json.targetID, consts.eventNames.subMenuEventAppended)\n            break;\n            case consts.eventNames.subMenuCmdClosePopup:\n            window = null\n            if (typeof json.windowId !== \"undefined\") {\n                window = elements[json.windowId]\n            }\n            elements[json.targetID].closePopup(window)\n            client.write(json.targetID, consts.eventNames.subMenuEventClosedPopup)\n            break;\n            case consts.eventNames.subMenuCmdInsert:\n            elements[json.targetID].insert(json.menuItemPosition, menuItemCreate(json.menuItem))\n            setMenu(json.menuItem.rootId)\n            client.write(json.targetID, consts.eventNames.subMenuEventInserted)\n            break;\n            case consts.eventNames.subMenuCmdPopup:\n            window = null\n            if (typeof json.windowId !== \"undefined\") {\n                window = elements[json.windowId]\n            }\n            json.menuPopupOptions.async = true\n            elements[json.targetID].popup(window, json.menuPopupOptions)\n            client.write(json.targetID, consts.eventNames.subMenuEventPoppedUp)\n            break;\n\n            // Tray\n            case consts.eventNames.trayCmdCreate:\n            trayCreate(json)\n            break;\n            case consts.eventNames.trayCmdDestroy:\n            elements[json.targetID].destroy()\n            elements[json.targetID] = null\n            client.write(json.targetID, consts.eventNames.trayEventDestroyed)\n            break;\n            case consts.eventNames.trayCmdSetImage:\n            elements[json.targetID].setImage(json.image);\n            client.write(json.targetID, consts.eventNames.trayEventImageSet)\n            break;\n            case consts.eventNames.trayCmdPopupContextMenu:\n            trayPopUpContextMenu(json);\n            client.write(json.targetID, consts.eventNames.trayEventPoppedUpContextMenu);    \n            break;\n\n            // Web contents\n            case consts.eventNames.webContentsEventLoginCallback:\n            executeCallback(consts.callbackNames.webContentsLogin, json, [json.username, json.password]);\n            break;\n\n            // Window\n            case consts.eventNames.windowCmdBlur:\n            elements[json.targetID].blur()\n            break;\n            case consts.eventNames.windowCmdCenter:\n            elements[json.targetID].center()\n            break;\n            case consts.eventNames.windowCmdClose:\n            elements[json.targetID].close()\n            break;\n            case consts.eventNames.windowCmdCreate:\n            windowCreate(json)\n            break;\n            case consts.eventNames.windowCmdDestroy:\n            elements[json.targetID].destroy()\n            elements[json.targetID] = null\n            break;\n            case consts.eventNames.windowCmdFocus:\n            elements[json.targetID].focus()\n            break;\n            case consts.eventNames.windowCmdHide:\n            elements[json.targetID].hide()\n            break;\n            case consts.eventNames.windowCmdLog:\n            elements[json.targetID].webContents.send(consts.eventNames.ipcCmdLog, json.message)\n            break;\n            case consts.eventNames.windowCmdMaximize:\n            elements[json.targetID].maximize()\n            break;\n            case consts.eventNames.windowCmdMessage:\n            case consts.eventNames.windowCmdMessageCallback:\n            let m = {message: json.message}\n            if (typeof json.callbackId !== \"undefined\") m.callbackId = json.callbackId\n            const targetElement = elements[json.targetID]\n            if (targetElement) targetElement.webContents.send(json.name === consts.eventNames.windowCmdMessageCallback ? consts.eventNames.ipcCmdMessageCallback : consts.eventNames.ipcCmdMessage, m)\n            break;\n            case consts.eventNames.windowCmdMinimize:\n            elements[json.targetID].minimize()\n            break;\n            case consts.eventNames.windowCmdMove:\n            elements[json.targetID].setPosition(json.windowOptions.x, json.windowOptions.y, true)\n            break;\n            case consts.eventNames.windowCmdMoveTop:\n            elements[json.targetID].moveTop()\n            client.write(json.targetID, consts.eventNames.windowEventMovedTop)\n            break;\n            case consts.eventNames.windowCmdResize:\n            elements[json.targetID].setSize(json.windowOptions.width, json.windowOptions.height, true)\n            break;\n            case consts.eventNames.windowCmdResizeContent:\n            elements[json.targetID].setContentSize(json.windowOptions.width, json.windowOptions.height, true)\n            break;\n            case consts.eventNames.windowCmdSetAlwaysOnTop:\n            elements[json.targetID].setAlwaysOnTop(json.enable ? json.enable : false)\n            client.write(json.targetID, consts.eventNames.windowEventAlwaysOnTopChanged)\n            break;\n            case consts.eventNames.windowCmdSetBounds:\n            elements[json.targetID].setBounds(json.bounds, true);\n            break;\n            case consts.eventNames.windowCmdSetFullScreen:\n            elements[json.targetID].setFullScreen(json.enable ? json.enable : false)\n            break;\n            case consts.eventNames.windowCmdRestore:\n            elements[json.targetID].restore()\n            break;\n            case consts.eventNames.windowCmdShow:\n            elements[json.targetID].show()\n            break;\n            case consts.eventNames.windowCmdSetContentProtection:\n            elements[json.targetID].setContentProtection(json.enable ? json.enable : false)\n            client.write(json.targetID, consts.eventNames.windowEventContentProtectionSet)\n            break;\n            case consts.eventNames.windowCmdWebContentsCloseDevTools:\n            elements[json.targetID].webContents.closeDevTools()\n            break;\n            case consts.eventNames.windowCmdWebContentsOpenDevTools:\n            elements[json.targetID].webContents.openDevTools()\n            break;\n            case consts.eventNames.windowCmdUnmaximize:\n            elements[json.targetID].unmaximize()\n            break;\n            case consts.eventNames.windowCmdUpdateCustomOptions:\n            windowOptions[json.targetID] = json.windowOptions\n            client.write(json.targetID, consts.eventNames.windowEventUpdatedCustomOptions, json.windowOptions)\n            break;\n            case consts.eventNames.windowCmdWebContentsExecuteJavascript:\n            elements[json.targetID].webContents.executeJavaScript(json.code).then(() => client.write(json.targetID, consts.eventNames.windowEventWebContentsExecutedJavaScript));\n            break;\n\n            // Global Shortcut\n            case consts.eventNames.globalShortcutsCmdRegister:\n            const isRegistered = globalShortcut.register(json.globalShortcuts.accelerator, () => {\n                client.write(json.targetID, consts.eventNames.globalShortcutsEventTriggered, {globalShortcuts:{accelerator: json.globalShortcuts.accelerator}});\n            });\n            client.write(json.targetID, consts.eventNames.globalShortcutsEventRegistered, {globalShortcuts:{isRegistered: isRegistered}});\n            break;\n            case consts.eventNames.globalShortcutsCmdIsRegistered:\n            client.write(json.targetID, consts.eventNames.globalShortcutsEventIsRegistered, {globalShortcuts:{isRegistered: globalShortcut.isRegistered(json.globalShortcuts.accelerator)}});\n            break;\n            case consts.eventNames.globalShortcutsCmdUnregister:\n            globalShortcut.unregister(json.globalShortcuts.accelerator);\n            client.write(json.targetID, consts.eventNames.globalShortcutsEventUnregistered);\n            break\n            case consts.eventNames.globalShortcutsCmdUnregisterAll:\n            globalShortcut.unregisterAll();\n            client.write(json.targetID, consts.eventNames.globalShortcutsEventUnregisteredAll);\n            break\n        }\n    });\n\n    // Send electron.ready event\n    client.write(consts.targetIds.app, consts.eventNames.appEventReady, {\n        displays: {\n            all: screen.getAllDisplays(),\n            primary: screen.getPrimaryDisplay()\n        },\n        supported: {\n            notification: Notification.isSupported()\n        }\n    })\n};\n\n// start begins listening to go-astilectron.\nfunction start(address = process.argv[2]) {\n    client.init(address);\n    rl = readline.createInterface({ input: client.socket });\n\n    app.on(\"before-quit\", beforeQuit);\n    if (app.isReady()) {\n        onReady();\n    } else {\n        app.on(\"ready\", onReady);\n    }\n    app.on(\"window-all-closed\", app.quit);\n}\n\n// menuCreate creates a new menu\nfunction menuCreate(menu) {\n    if (typeof menu !== \"undefined\") {\n        elements[menu.id] = new Menu()\n        for(let i = 0; i < menu.items.length; i++) {\n            elements[menu.id].append(menuItemCreate(menu.items[i]))\n        }\n        return elements[menu.id]\n    }\n    return null\n}\n\n// menuItemCreate creates a menu item\nfunction menuItemCreate(menuItem) {\n    const itemId = menuItem.id\n    menuItem.options.click = function(menuItem) {\n        client.write(itemId, consts.eventNames.menuItemEventClicked, {menuItemOptions: menuItemToJSON(menuItem)})\n    }\n    if (typeof menuItem.submenu !== \"undefined\") {\n        menuItem.options.type = 'submenu'\n        menuItem.options.submenu = menuCreate(menuItem.submenu)\n    }\n    elements[itemId] = new MenuItem(menuItem.options)\n    return elements[itemId]\n}\n\n// menuItemToJSON returns the proper fields not to raise an exception\nfunction menuItemToJSON(menuItem) {\n    return {\n        checked: menuItem.checked,\n        enabled: menuItem.enabled,\n        label: menuItem.label,\n        visible: menuItem.visible,\n    }\n}\n\n// setMenu sets a menu\nfunction setMenu(rootId) {\n    let menu = null\n    if (typeof menus[rootId] !== \"undefined\" && typeof elements[menus[rootId]] !== \"undefined\") {\n        menu = elements[menus[rootId]]\n    }\n    if (rootId === consts.targetIds.app) {\n        Menu.setApplicationMenu(menu)\n    } else if (rootId === consts.targetIds.dock && typeof app.dock !== \"undefined\") {\n        app.dock.setMenu(menu)\n    } else if (elements[rootId].constructor === Tray) {\n        elements[rootId].setContextMenu(menu);\n    } else {\n        elements[rootId].setMenu(menu);\n    }\n}\n\n// notificationCreate creates a notification\nfunction notificationCreate(json) {\n    if (Notification.isSupported()) {\n        elements[json.targetID] = new Notification(json.notificationOptions);\n        elements[json.targetID].on('action', (event, index) => { client.write(json.targetID, consts.eventNames.notificationEventActioned, {index: index}) })\n        elements[json.targetID].on('click', () => { client.write(json.targetID, consts.eventNames.notificationEventClicked) })\n        elements[json.targetID].on('close', () => { client.write(json.targetID, consts.eventNames.notificationEventClosed) })\n        elements[json.targetID].on('reply', (event, reply) => { client.write(json.targetID, consts.eventNames.notificationEventReplied, {reply: reply}) })\n        elements[json.targetID].on('show', () => { client.write(json.targetID, consts.eventNames.notificationEventShown) })\n    }\n    client.write(json.targetID, consts.eventNames.notificationEventCreated)\n}\n\n// trayCreate creates a tray\nfunction trayCreate(json) {\n    elements[json.targetID] = new Tray(json.trayOptions.image);\n    if (typeof json.trayOptions.tooltip !== \"undefined\") {\n        elements[json.targetID].setToolTip(json.trayOptions.tooltip);\n    }\n    elements[json.targetID].on('click', (index, event) => { client.write(json.targetID, consts.eventNames.trayEventClicked, {\"bounds\":{x:event.x, y:event.y,width:event.width,height:event.height}})})\n    elements[json.targetID].on('double-click', (index, event) => { client.write(json.targetID, consts.eventNames.trayEventDoubleClicked, {\"bounds\":{x:event.x, y:event.y,width:event.width,height:event.height}})})\n    elements[json.targetID].on('right-click', (index, event) => { client.write(json.targetID, consts.eventNames.trayEventRightClicked, {\"bounds\":{x:event.x, y:event.y,width:event.width,height:event.height}})})\n    client.write(json.targetID, consts.eventNames.trayEventCreated)\n}\n\n// trayPopUpContextMenu pops up the context menu of the tray\nfunction trayPopUpContextMenu(json) {\n    let menu = menuCreate(json.menu);\n    let position = json.menuPopupOptions;\n    if (!menu && !position) elements[json.targetID].popUpContextMenu();\n    else if (menu && !position) elements[json.targetID].popUpContextMenu(menu);\n    else if (!menu && position) elements[json.targetID].popUpContextMenu(position);\n    else elements[json.targetID].popUpContextMenu(menu, position);\n}\n\n// windowCreate creates a new window\nfunction windowCreate(json) {\n    if (!json.windowOptions.webPreferences) {\n        json.windowOptions.webPreferences = {}\n    }\n    json.windowOptions.webPreferences.contextIsolation = false\n    json.windowOptions.webPreferences.nodeIntegration = true\n    elements[json.targetID] = new BrowserWindow(json.windowOptions)\n    windowOptions[json.targetID] = json.windowOptions\n    if (typeof json.windowOptions.proxy !== \"undefined\") {\n        elements[json.targetID].webContents.session.setProxy(json.windowOptions.proxy)\n            .then(() => windowCreateFinish(json))\n    } else {\n        windowCreateFinish(json)\n    }\n}\n\n// windowCreateFinish finishes creating a new window\nfunction windowCreateFinish(json) {\n    elements[json.targetID].setMenu(null)\n    elements[json.targetID].loadURL(json.url, (typeof json.windowOptions.load !== \"undefined\" ? json.windowOptions.load :  {}));\n    elements[json.targetID].on('blur', () => { client.write(json.targetID, consts.eventNames.windowEventBlur) })\n    elements[json.targetID].on('close', (e) => {\n        if (typeof windowOptions[json.targetID] !== \"undefined\" && typeof windowOptions[json.targetID].custom !== \"undefined\") {\n            if (typeof windowOptions[json.targetID].custom.messageBoxOnClose !== \"undefined\") {\n                let buttonId = dialog.showMessageBoxSync(null, windowOptions[json.targetID].custom.messageBoxOnClose)\n                if (typeof windowOptions[json.targetID].custom.messageBoxOnClose.confirmId !== \"undefined\" && windowOptions[json.targetID].custom.messageBoxOnClose.confirmId !== buttonId) {\n                    e.preventDefault()\n                    return\n                }\n            }\n            if (!quittingApp) {\n                if (windowOptions[json.targetID].custom.minimizeOnClose) {\n                    e.preventDefault();\n                    elements[json.targetID].minimize();\n                } else if (windowOptions[json.targetID].custom.hideOnClose) {\n                    e.preventDefault();\n                    elements[json.targetID].hide();\n                }\n            }\n        }\n    })\n    elements[json.targetID].on('closed', () => {\n        client.write(json.targetID, consts.eventNames.windowEventClosed)\n        delete elements[json.targetID]\n    })\n    elements[json.targetID].on('enter-full-screen', () => { client.write(json.targetID, consts.eventNames.windowEventEnterFullScreen, {windowOptions: {fullscreen: true}})} )\n    elements[json.targetID].on('focus', () => { client.write(json.targetID, consts.eventNames.windowEventFocus) })\n    elements[json.targetID].on('hide', () => { client.write(json.targetID, consts.eventNames.windowEventHide) })\n    elements[json.targetID].on('leave-full-screen', () => { client.write(json.targetID, consts.eventNames.windowEventLeaveFullScreen, {windowOptions: {fullscreen: false}})} )\n    elements[json.targetID].on('maximize', () => {\n        let bounds = elements[json.targetID].getBounds();\n        client.write(json.targetID, consts.eventNames.windowEventMaximize, {bounds: bounds});\n    })\n    elements[json.targetID].on('minimize', () => { client.write(json.targetID, consts.eventNames.windowEventMinimize) })\n    elements[json.targetID].on('move', () => {\n        let bounds = elements[json.targetID].getBounds();\n        client.write(json.targetID, consts.eventNames.windowEventMove, {bounds: bounds});\n    })\n    elements[json.targetID].on('moved', () => {\n        let bounds = elements[json.targetID].getBounds();\n        client.write(json.targetID, consts.eventNames.windowEventMoved, {bounds: bounds});\n    })\n    elements[json.targetID].on('ready-to-show', () => { client.write(json.targetID, consts.eventNames.windowEventReadyToShow) })\n    elements[json.targetID].on('resize', () => {\n        let bounds = elements[json.targetID].getBounds();\n        client.write(json.targetID, consts.eventNames.windowEventResize, {bounds: bounds});\n    })\n    elements[json.targetID].on('resize-content', () => {\n        let bounds = elements[json.targetID].getBounds();\n        client.write(json.targetID, consts.eventNames.windowEventResizeContent, {bounds: bounds})\n    })\n    elements[json.targetID].on('restore', () => { client.write(json.targetID, consts.eventNames.windowEventRestore) })\n    elements[json.targetID].on('show', () => { client.write(json.targetID, consts.eventNames.windowEventShow) })\n    elements[json.targetID].on('unmaximize', () => {\n        let bounds = elements[json.targetID].getBounds();\n        client.write(json.targetID, consts.eventNames.windowEventUnmaximize, {bounds: bounds});\n    })\n    elements[json.targetID].on('unresponsive', () => { client.write(json.targetID, consts.eventNames.windowEventUnresponsive) })\n    elements[json.targetID].on('will-move', () => {\n        let bounds = elements[json.targetID].getBounds();\n        client.write(json.targetID, consts.eventNames.windowEventWillMove, {bounds: bounds});\n    })\n    elements[json.targetID].webContents.on('did-finish-load', () => {\n        elements[json.targetID].webContents.executeJavaScript(\n            `const {ipcRenderer} = require('electron')\n            var astilectron = {\n                onMessageOnce: false,\n                onMessage: function(callback) {\n                    if (astilectron.onMessageOnce) {\n                        return\n                    }\n                    ipcRenderer.on('`+ consts.eventNames.ipcCmdMessage +`', function(event, message) {\n                        let v = callback(message.message)\n                        if (typeof message.callbackId !== \"undefined\") {\n                            let e = {callbackId: message.callbackId, targetID: '`+ json.targetID +`'}\n                            if (typeof v !== \"undefined\") e.message = v\n                            ipcRenderer.send('`+ consts.eventNames.ipcEventMessageCallback +`', e)\n                        }\n                    })\n                    astilectron.onMessageOnce = true\n                },\n                callbacks: {},\n                counters: {},\n                registerCallback: function(k, e, c, n) {\n                    e.targetID = '`+ json.targetID +`';\n                    if (typeof c !== \"undefined\") {\n                        if (typeof astilectron.counters[k] === \"undefined\") {\n                            astilectron.counters[k] = 1;\n                        }\n                        e.callbackId = String(astilectron.counters[k]++);\n                        if (typeof astilectron.callbacks[k] === \"undefined\") {\n                            astilectron.callbacks[k] = {};\n                        }\n                        astilectron.callbacks[k][e.callbackId] = c;\n                    }\n                    ipcRenderer.send(n, e);\n                },\n                executeCallback: function(k, message, args) {\n                    if (typeof astilectron.callbacks[k][message.callbackId] !== \"undefined\") {\n                        astilectron.callbacks[k][message.callbackId].apply(null, args);\n                    }\n                },\n                sendMessage: function(message, callback) {\n                    astilectron.registerCallback('` + consts.callbackNames.webContentsMessage + `', {message: message}, callback, '`+ consts.eventNames.ipcEventMessage +`');\n                }\n            };\n            ipcRenderer.on('`+ consts.eventNames.ipcCmdMessageCallback +`', function(event, message) {\n                astilectron.executeCallback('` + consts.callbackNames.webContentsMessage + `', message, [message.message]);\n            });\n            ipcRenderer.on('`+ consts.eventNames.ipcCmdLog+`', function(event, message) {\n                console.log(message)\n            });\n            ` + (typeof json.windowOptions.custom !== \"undefined\" && typeof json.windowOptions.custom.script !== \"undefined\" ? json.windowOptions.custom.script : \"\") + `\n            document.dispatchEvent(new Event('astilectron-ready'))`\n        )\n        sessionCreate(elements[json.targetID].webContents, json.sessionId)\n        let bounds = elements[json.targetID].getBounds();\n        client.write(json.targetID, consts.eventNames.windowEventDidFinishLoad, {bounds: bounds})\n    })\n    elements[json.targetID].webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {\n        client.write(json.targetID, consts.eventNames.windowEventDidGetRedirectRequest, {\n            newUrl: newUrl,\n            oldUrl: oldUrl\n        })\n    })\n    elements[json.targetID].webContents.on('login', (event, request, authInfo, callback) => {\n        event.preventDefault();\n        registerCallback(json, consts.callbackNames.webContentsLogin, {authInfo: authInfo, request: request}, consts.eventNames.webContentsEventLogin, callback);\n    })\n    elements[json.targetID].webContents.on('will-navigate', (event, url) => {\n        client.write(json.targetID, consts.eventNames.windowEventWillNavigate, {\n            url: url\n        })\n    })\n    if (typeof json.windowOptions.appDetails !== \"undefined\" && process.platform === \"win32\"){\n        elements[json.targetID].setThumbarButtons([]);\n        elements[json.targetID].setAppDetails(json.windowOptions.appDetails);\n    }\n\n    lastWindowId = json.targetID\n}\n\nfunction registerCallback(json, k, e, n, c) {\n    if (typeof counters[k] === \"undefined\") {\n        counters[k] = 1;\n    }\n    e.callbackId = String(counters[k]++);\n    if (typeof callbacks[k] === \"undefined\") {\n        callbacks[k] = {};\n    }\n    callbacks[k][e.callbackId] = c;\n    client.write(json.targetID, n, e);\n}\n\nfunction executeCallback(k, json, args) {\n    if (typeof callbacks[k][json.callbackId] !== \"undefined\") {\n        callbacks[k][json.callbackId].apply(null, args);\n    }\n}\n\nfunction sessionCreate(webContents, sessionId) {\n    elements[sessionId] = webContents.session\n    elements[sessionId].on('will-download', () => { client.write(sessionId, consts.eventNames.sessionEventWillDownload) })\n}\n\nfunction getLastWindow() {\n    if (elements[lastWindowId]) return elements[lastWindowId]\n    return null\n}\n\nmodule.exports = {\n  getLastWindow,\n  start,\n  client,\n  consts\n}\n"
  },
  {
    "path": "main.js",
    "content": "\"use strict\";\n\nconst { app } = require(\"electron\");\nconst { start, getLastWindow, client, consts } = require(\"./index\");\n\n// edge case when the program is launched without arguments\nif (process.argv.length == 1) {\n  app.requestSingleInstanceLock();\n  app.quit();\n  return;\n}\n\nif (process.argv[3] === \"true\") {\n  // Lock\n  const singlesInstanceLock = app.requestSingleInstanceLock();\n  if (!singlesInstanceLock) {\n    app.quit();\n    return;\n  }\n\n  // Someone tried to run a second instance, we should focus our window.\n  app.on(\"second-instance\", (event, commandLine, workingDirectory) => {\n    client.write(consts.targetIds.app, consts.eventNames.appEventSecondInstance, {secondInstance: {commandLine: commandLine, workingDirectory: workingDirectory}})\n    const lastWindow = getLastWindow()\n    if (lastWindow) {\n      if (lastWindow.isMinimized()) lastWindow.restore();\n      lastWindow.show();\n    }\n  });\n}\n\n// Command line switches\nlet idx = 4;\nfor (let i = idx; i < process.argv.length; i++) {\n  let s = process.argv[i].replace(/^[\\-]+/g, \"\");\n  let v;\n  if (\n    typeof process.argv[i + 1] !== \"undefined\" &&\n    !process.argv[i + 1].startsWith(\"-\")\n  ) {\n    v = process.argv[i + 1];\n    i++;\n  }\n  app.commandLine.appendSwitch(s, v);\n}\n\nstart();\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"astilectron\",\n  \"version\": \"0.57.0\",\n  \"description\": \"Electron-based cross-language application framework\",\n  \"license\": \"MIT\",\n  \"author\": \"Quentin Renard (https://github.com/asticode)\",\n  \"contributors\": [\n    \"fregie (https://github.com/fregie)\",\n    \"vahid-sohrabloo (https://github.com/vahid-sohrabloo)\",\n    \"cbairy (https://github.com/cbairy)\",\n    \"john dev (https://github.com/john-dev)\",\n    \"AONOSORA (https://github.com/ZEROKISEKI)\",\n    \"Spencer Brower (https://github.com/sbrow\"\n  ],\n  \"bin\": \"main.js\",\n  \"main\": \"index.js\",\n  \"peerDependencies\": {\n    \"electron\": \">=1.8.1\"\n  }\n}\n"
  },
  {
    "path": "src/client.js",
    "content": "\"use strict\";\n\nconst net = require(\"net\");\nconst url = require(\"url\");\n\n// Client can read/write messages from a TCP server\nclass Client {\n  // init initializes the Client\n  init(addr) {\n    let u = url.parse(\"tcp://\" + addr, false, false);\n    this.socket = new net.Socket();\n    this.socket.connect(u.port, u.hostname, function() {});\n    this.socket.on(\"close\", function() {\n      process.exit();\n    });\n    this.socket.on(\"error\", function(err) {\n      // Prevent Unhandled Exception resulting from TCP Error\n      console.error(err);\n    });\n    return this;\n  }\n\n  // write writes an event to the server\n  write(targetID, eventName, payload) {\n    if(this.socket.destroyed) return;\n    let data = { name: eventName, targetID: targetID };\n    if (typeof payload !== \"undefined\") Object.assign(data, payload);\n    this.socket.write(JSON.stringify(data) + \"\\n\");\n  }\n}\n\nmodule.exports = new Client();\n"
  },
  {
    "path": "src/consts.js",
    "content": "'use strict'\n\nmodule.exports = {\n    callbackNames: {\n        webContentsLogin: \"web.contents.login\",\n        webContentsMessage: \"web.contents.message\",\n    },\n    eventNames: {\n        appCmdQuit: \"app.cmd.quit\",\n        appEventReady: \"app.event.ready\",\n        appEventSecondInstance: \"app.event.second.instance\",\n        displayEventAdded: \"display.event.added\",\n        displayEventMetricsChanged: \"display.event.metrics.changed\",\n        displayEventRemoved: \"display.event.removed\",\n        dockCmdBounce: \"dock.cmd.bounce\",\n        dockCmdBounceDownloads: \"dock.cmd.bounce.downloads\",\n        dockCmdCancelBounce: \"dock.cmd.cancel.bounce\",\n        dockCmdHide: \"dock.cmd.hide\",\n        dockCmdSetBadge: \"dock.cmd.set.badge\",\n        dockCmdSetIcon: \"dock.cmd.set.icon\",\n        dockCmdShow: \"dock.cmd.show\",\n        dockEventBadgeSet: \"dock.event.badge.set\",\n        dockEventBouncing: \"dock.event.bouncing\",\n        dockEventBouncingCancelled: \"dock.event.bouncing.cancelled\",\n        dockEventDownloadsBouncing: \"dock.event.download.bouncing\",\n        dockEventHidden: \"dock.event.hidden\",\n        dockEventIconSet: \"dock.event.icon.set\",\n        dockEventShown: \"dock.event.shown\",\n        globalShortcutsCmdRegister: \"global.shortcuts.cmd.register\",\n        globalShortcutsCmdIsRegistered: \"global.shortcuts.cmd.is.registered\",\n        globalShortcutsCmdUnregister: \"global.shortcuts.cmd.unregister\",\n        globalShortcutsCmdUnregisterAll: \"global.shortcuts.cmd.unregister.all\",\n        globalShortcutsEventRegistered: \"global.shortcuts.event.registered\",\n        globalShortcutsEventIsRegistered: \"global.shortcuts.event.is.registered\",\n        globalShortcutsEventUnregistered: \"global.shortcuts.event.unregistered\",\n        globalShortcutsEventUnregisteredAll: \"global.shortcuts.event.unregistered.all\",\n        globalShortcutsEventTriggered: \"global.shortcuts.event.triggered\",\n        ipcCmdLog: \"ipc.cmd.log\",\n        ipcCmdMessage: \"ipc.cmd.message\",\n        ipcCmdMessageCallback: \"ipc.cmd.message.callback\",\n        ipcEventMessage: \"ipc.event.message\",\n        ipcEventMessageCallback: \"ipc.event.message.callback\",\n        menuCmdCreate: \"menu.cmd.create\",\n        menuCmdDestroy: \"menu.cmd.destroy\",\n        menuEventCreated: \"menu.event.created\",\n        menuEventDestroyed: \"menu.event.destroyed\",\n        menuItemCmdSetChecked: \"menu.item.cmd.set.checked\",\n        menuItemCmdSetEnabled: \"menu.item.cmd.set.enabled\",\n        menuItemCmdSetLabel: \"menu.item.cmd.set.label\",\n        menuItemCmdSetVisible: \"menu.item.cmd.set.visible\",\n        menuItemEventCheckedSet: \"menu.item.event.checked.set\",\n        menuItemEventClicked: \"menu.item.event.clicked\",\n        menuItemEventEnabledSet: \"menu.item.event.enabled.set\",\n        menuItemEventLabelSet: \"menu.item.event.label.set\",\n        menuItemEventVisibleSet: \"menu.item.event.visible.set\",\n        notificationCmdCreate: \"notification.cmd.create\",\n        notificationCmdShow: \"notification.cmd.show\",\n        notificationEventActioned: \"notification.event.actioned\",\n        notificationEventClicked: \"notification.event.clicked\",\n        notificationEventClosed: \"notification.event.closed\",\n        notificationEventCreated: \"notification.event.created\",\n        notificationEventReplied: \"notification.event.replied\",\n        notificationEventShown: \"notification.event.shown\",\n        powerEventSuspend: \"power.event.suspend\",\n        powerEventResume: \"power.event.resume\",\n        powerEventOnAC: \"power.event.on.ac\",\n        powerEventOnBattery: \"power.event.on.battery\",\n        powerEventShutdown: \"power.event.shutdown\",\n        powerEventLockScreen: \"power.event.lock.screen\",\n        powerEventUnlockScreen: \"power.event.unlock.screen\",\n        powerEventUserDidBecomeActive: \"power.event.user.did.become.active\",\n        powerEventUserDidResignActive: \"power.event.user.did.resign.active\",\n        sessionCmdClearCache: \"session.cmd.clear.cache\",\n        sessionCmdFlushStorage: \"session.cmd.flush.storage\",\n        sessionCmdLoadExtension: \"session.cmd.load.extension\",\n        sessionEventClearedCache: \"session.event.cleared.cache\",\n        sessionEventWillDownload: \"session.event.will.download\",\n        sessionEventFlushedStorage: \"session.event.flushed.storage\",\n        sessionEventLoadedExtension: \"session.event.loaded.extension\",\n        subMenuCmdAppend: \"sub.menu.cmd.append\",\n        subMenuCmdClosePopup: \"sub.menu.cmd.close.popup\",\n        subMenuCmdInsert: \"sub.menu.cmd.insert\",\n        subMenuCmdPopup: \"sub.menu.cmd.popup\",\n        subMenuEventAppended: \"sub.menu.event.appended\",\n        subMenuEventClosedPopup: \"sub.menu.event.closed.popup\",\n        subMenuEventInserted: \"sub.menu.event.inserted\",\n        subMenuEventPoppedUp: \"sub.menu.event.popped.up\",\n        trayCmdCreate: \"tray.cmd.create\",\n        trayCmdDestroy: \"tray.cmd.destroy\",\n        trayCmdSetImage: \"tray.cmd.set.image\",\n        trayCmdPopupContextMenu: \"tray.cmd.popup.context.menu\",\n        trayEventClicked: \"tray.event.clicked\",\n        trayEventCreated: \"tray.event.created\",\n        trayEventDestroyed: \"tray.event.destroyed\",\n        trayEventDoubleClicked: \"tray.event.double.clicked\",\n        trayEventImageSet: \"tray.event.image.set\",\n        trayEventRightClicked: \"tray.event.right.clicked\",\n        trayEventContextMenuPoppedUp: \"tray.event.context.menu.popped.up\",\n        webContentsEventLogin: \"web.contents.event.login\",\n        webContentsEventLoginCallback: \"web.contents.event.login.callback\",\n        windowCmdBlur: \"window.cmd.blur\",\n        windowCmdCenter: \"window.cmd.center\",\n        windowCmdClose: \"window.cmd.close\",\n        windowCmdCreate: \"window.cmd.create\",\n        windowCmdDestroy: \"window.cmd.destroy\",\n        windowCmdFocus: \"window.cmd.focus\",\n        windowCmdHide: \"window.cmd.hide\",\n        windowCmdLog: \"window.cmd.log\",\n        windowCmdMaximize: \"window.cmd.maximize\",\n        windowCmdMessage: \"window.cmd.message\",\n        windowCmdMessageCallback: \"window.cmd.message.callback\",\n        windowCmdMinimize: \"window.cmd.minimize\",\n        windowCmdMove: \"window.cmd.move\",\n        windowCmdMoveTop: \"window.cmd.move.top\",\n        windowCmdResize: \"window.cmd.resize\",\n        windowCmdResizeContent: \"window.cmd.resize.content\",\n        windowCmdSetAlwaysOnTop: \"window.cmd.set.always.on.top\",\n        windowCmdSetBounds: \"window.cmd.set.bounds\",\n        windowCmdSetFullScreen: \"window.cmd.set.full.screen\",\n        windowCmdRestore: \"window.cmd.restore\",\n        windowCmdSetContentProtection: \"window.cmd.set.content.protection\",\n        windowCmdShow: \"window.cmd.show\",\n        windowCmdUnmaximize: \"window.cmd.unmaximize\",\n        windowCmdUpdateCustomOptions: \"window.cmd.update.custom.options\",\n        windowCmdWebContentsCloseDevTools: \"window.cmd.web.contents.close.dev.tools\",\n        windowCmdWebContentsOpenDevTools: \"window.cmd.web.contents.open.dev.tools\",\n        windowCmdWebContentsExecuteJavascript: \"window.cmd.web.contents.execute.javascript\",\n        windowEventAlwaysOnTopChanged: \"window.event.always.on.top.changed\",\n        windowEventBlur: \"window.event.blur\",\n        windowEventClosed: \"window.event.closed\",\n        windowEventContentProtectionSet: \"window.cmd.web.contents.execute.javascript\",\n        windowEventDidFinishLoad: \"window.event.did.finish.load\",\n        windowEventDidGetRedirectRequest: \"window.event.did.get.redirect.request\",\n        windowEventEnterFullScreen: \"window.event.enter.full.screen\",\n        windowEventWillNavigate: \"window.event.will.navigate\",\n        windowEventFocus: \"window.event.focus\",\n        windowEventHide: \"window.event.hide\",\n        windowEventLeaveFullScreen: \"window.event.leave.full.screen\",\n        windowEventMaximize: \"window.event.maximize\",\n        windowEventMessage: \"window.event.message\",\n        windowEventMessageCallback: \"window.event.message.callback\",\n        windowEventMinimize: \"window.event.minimize\",\n        windowEventMove: \"window.event.move\",\n        windowEventMoved: \"window.event.moved\",\n        windowEventMovedTop: \"window.event.moved.top\",\n        windowEventReadyToShow: \"window.event.ready.to.show\",\n        windowEventResize: \"window.event.resize\",\n        windowEventResizeContent: \"window.event.resize.content\",\n        windowEventRestore: \"window.event.restore\",\n        windowEventShow: \"window.event.show\",\n        windowEventUnmaximize: \"window.event.unmaximize\",\n        windowEventUnresponsive: \"window.event.unresponsive\",\n        windowEventWebContentsExecutedJavaScript: \"window.event.web.contents.executed.javascript\",\n        windowEventWillMove: \"window.event.will.move\",\n        windowEventUpdatedCustomOptions: \"window.event.updated.custom.options\"\n    },\n    targetIds: {\n        app: 'app',\n        dock: 'dock'\n    }\n}\n"
  }
]