From 9e347bab77820b135eccf3e42fd5e043ddaa2dea Mon Sep 17 00:00:00 2001 From: Vishwas Shashidhar Date: Tue, 7 May 2019 13:26:57 +0530 Subject: [PATCH] refactor code and remove old JS code --- .eslintignore | 8 - .eslintrc | 31 - NOTICE | 2 +- PULL_REQUEST_TEMPLATE.md | 2 +- js/aboutApp/about-app.html | 52 - js/aboutApp/index.js | 144 -- js/aboutApp/renderer.js | 52 - js/aboutApp/symphony-logo.png | Bin 10257 -> 0 bytes js/activityDetection/index.js | 126 -- js/autoLaunch/index.js | 60 - js/badgeCount.js | 62 - js/basicAuth/basic-auth.html | 97 -- js/basicAuth/index.js | 177 --- js/basicAuth/renderer.js | 83 - js/bringToFront.js | 31 - js/cacheHandler/index.js | 39 - js/config.js | 420 ----- js/crashReporter.js | 85 - js/cryptoLib.js | 132 -- js/desktopCapturer/getSource.js | 161 -- js/desktopCapturer/getSources.js | 146 -- js/desktopCapturer/index.js | 174 --- js/desktopCapturer/renderer.js | 319 ---- js/desktopCapturer/screen-picker.html | 246 --- js/dialogs/showBasicAuth.js | 46 - js/dialogs/showCertError.js | 48 - js/dialogs/showLoadError.js | 67 - js/downloadManager/index.js | 253 --- js/enums/api.js | 36 - js/enums/logLevels.js | 16 - js/enums/titleBarStyles.js | 12 - js/eventEmitter.js | 10 - js/log.js | 168 -- js/main.js | 479 ------ js/mainApiMgr.js | 210 --- js/memoryMonitor.js | 143 -- js/menus/menuTemplate.js | 582 ------- js/moreInfo/index.js | 141 -- js/moreInfo/more-info.html | 45 - js/moreInfo/renderer.js | 66 - js/networkError/contents.js | 126 -- js/networkError/index.js | 47 - js/networkError/style.css | 77 - js/notify/AnimationQueue.js | 61 - js/notify/assets/symphony-logo-black.png | Bin 1768 -> 0 bytes js/notify/assets/symphony-logo-white.png | Bin 1796 -> 0 bytes js/notify/electron-notify-preload.js | 257 --- js/notify/electron-notify.html | 24 - js/notify/electron-notify.js | 856 ---------- js/notify/notifyImpl.js | 246 --- ...configure-notification-position-preload.js | 117 -- .../configure-notification-position.css | 91 -- .../configure-notification-position.html | 60 - .../configure-notification-position.js | 211 --- js/preload/preloadMain.js | 601 ------- js/protocolHandler/index.js | 67 - js/screenSharingIndicator/index.js | 111 -- js/screenSharingIndicator/renderer.js | 38 - .../screen-sharing-indicator.html | 104 -- .../showScreenSharingIndicator.js | 46 - js/screenSnippet/index.js | 215 --- js/snackBar/content.js | 11 - js/snackBar/index.js | 54 - js/snackBar/style.css | 51 - js/spellChecker/index.js | 122 -- js/stats.js | 94 -- js/translation/i18n.js | 52 - js/utils/archiveHandler.js | 62 - js/utils/compareSemVersions.js | 86 -- js/utils/getCmdLineArg.js | 31 - js/utils/getGuid.js | 17 - js/utils/getRegistry.js | 64 - js/utils/isInDisplayBounds.js | 30 - js/utils/misc.js | 16 - js/utils/stringFormat.js | 22 - js/utils/throttle.js | 44 - js/utils/whitelistHandler.js | 166 -- js/windowMgr.js | 1374 ----------------- js/windowsTitleBar/contents.js | 144 -- js/windowsTitleBar/index.js | 286 ---- js/windowsTitleBar/style.css | 106 -- locale/en-US.json | 180 --- locale/en.json | 180 --- locale/fr-FR.json | 180 --- locale/fr.json | 180 --- locale/ja-JP.json | 180 --- locale/ja.json | 180 --- package.json | 3 +- spec/__mocks__/electron.ts | 8 + spec/autoLaunchController.spec.ts | 31 +- src/app/auto-launch-controller.ts | 3 + src/app/window-handler.ts | 10 +- tests/DownloadManager.test.js | 90 -- tests/ProtocolHandler.test.js | 118 -- tests/README.md | 2 - tests/ScreenSnippet.test.js | 134 -- tests/__mocks__/electron.js | 80 - tests/activityDetection.test.js | 84 - tests/config.test.js | 322 ---- tests/fixtures/snippet/ScreenSnippet.jpeg | Bin 2813 -> 0 bytes tests/fixtures/snippet/snippet-base64.js | 5 - tests/log.test.js | 70 - tests/utils/getCmdLineArg.test.js | 28 - tests/utils/getGuid.test.js | 25 - tests/utils/getRegistry.test.js | 70 - tests/utils/isInDisplayBounds.test.js | 164 -- tests/utils/throttle.test.js | 111 -- tests/utils/whitelist.test.js | 88 -- 108 files changed, 28 insertions(+), 13656 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc delete mode 100644 js/aboutApp/about-app.html delete mode 100644 js/aboutApp/index.js delete mode 100644 js/aboutApp/renderer.js delete mode 100644 js/aboutApp/symphony-logo.png delete mode 100644 js/activityDetection/index.js delete mode 100644 js/autoLaunch/index.js delete mode 100644 js/badgeCount.js delete mode 100644 js/basicAuth/basic-auth.html delete mode 100644 js/basicAuth/index.js delete mode 100644 js/basicAuth/renderer.js delete mode 100644 js/bringToFront.js delete mode 100644 js/cacheHandler/index.js delete mode 100644 js/config.js delete mode 100644 js/crashReporter.js delete mode 100644 js/cryptoLib.js delete mode 100644 js/desktopCapturer/getSource.js delete mode 100644 js/desktopCapturer/getSources.js delete mode 100644 js/desktopCapturer/index.js delete mode 100644 js/desktopCapturer/renderer.js delete mode 100644 js/desktopCapturer/screen-picker.html delete mode 100644 js/dialogs/showBasicAuth.js delete mode 100644 js/dialogs/showCertError.js delete mode 100644 js/dialogs/showLoadError.js delete mode 100644 js/downloadManager/index.js delete mode 100644 js/enums/api.js delete mode 100644 js/enums/logLevels.js delete mode 100644 js/enums/titleBarStyles.js delete mode 100644 js/eventEmitter.js delete mode 100644 js/log.js delete mode 100644 js/main.js delete mode 100644 js/mainApiMgr.js delete mode 100644 js/memoryMonitor.js delete mode 100644 js/menus/menuTemplate.js delete mode 100644 js/moreInfo/index.js delete mode 100644 js/moreInfo/more-info.html delete mode 100644 js/moreInfo/renderer.js delete mode 100644 js/networkError/contents.js delete mode 100644 js/networkError/index.js delete mode 100644 js/networkError/style.css delete mode 100644 js/notify/AnimationQueue.js delete mode 100644 js/notify/assets/symphony-logo-black.png delete mode 100644 js/notify/assets/symphony-logo-white.png delete mode 100644 js/notify/electron-notify-preload.js delete mode 100644 js/notify/electron-notify.html delete mode 100644 js/notify/electron-notify.js delete mode 100644 js/notify/notifyImpl.js delete mode 100644 js/notify/settings/configure-notification-position-preload.js delete mode 100644 js/notify/settings/configure-notification-position.css delete mode 100644 js/notify/settings/configure-notification-position.html delete mode 100644 js/notify/settings/configure-notification-position.js delete mode 100644 js/preload/preloadMain.js delete mode 100644 js/protocolHandler/index.js delete mode 100755 js/screenSharingIndicator/index.js delete mode 100644 js/screenSharingIndicator/renderer.js delete mode 100755 js/screenSharingIndicator/screen-sharing-indicator.html delete mode 100644 js/screenSharingIndicator/showScreenSharingIndicator.js delete mode 100644 js/screenSnippet/index.js delete mode 100644 js/snackBar/content.js delete mode 100644 js/snackBar/index.js delete mode 100644 js/snackBar/style.css delete mode 100644 js/spellChecker/index.js delete mode 100644 js/stats.js delete mode 100644 js/translation/i18n.js delete mode 100644 js/utils/archiveHandler.js delete mode 100644 js/utils/compareSemVersions.js delete mode 100644 js/utils/getCmdLineArg.js delete mode 100644 js/utils/getGuid.js delete mode 100644 js/utils/getRegistry.js delete mode 100644 js/utils/isInDisplayBounds.js delete mode 100644 js/utils/misc.js delete mode 100644 js/utils/stringFormat.js delete mode 100644 js/utils/throttle.js delete mode 100644 js/utils/whitelistHandler.js delete mode 100644 js/windowMgr.js delete mode 100644 js/windowsTitleBar/contents.js delete mode 100644 js/windowsTitleBar/index.js delete mode 100644 js/windowsTitleBar/style.css delete mode 100644 locale/en-US.json delete mode 100644 locale/en.json delete mode 100644 locale/fr-FR.json delete mode 100644 locale/fr.json delete mode 100644 locale/ja-JP.json delete mode 100644 locale/ja.json delete mode 100644 tests/DownloadManager.test.js delete mode 100644 tests/ProtocolHandler.test.js delete mode 100644 tests/README.md delete mode 100644 tests/ScreenSnippet.test.js delete mode 100644 tests/__mocks__/electron.js delete mode 100644 tests/activityDetection.test.js delete mode 100644 tests/config.test.js delete mode 100644 tests/fixtures/snippet/ScreenSnippet.jpeg delete mode 100644 tests/fixtures/snippet/snippet-base64.js delete mode 100644 tests/log.test.js delete mode 100644 tests/utils/getCmdLineArg.test.js delete mode 100644 tests/utils/getGuid.test.js delete mode 100644 tests/utils/getRegistry.test.js delete mode 100644 tests/utils/isInDisplayBounds.test.js delete mode 100644 tests/utils/throttle.test.js delete mode 100644 tests/utils/whitelist.test.js diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index b3a47729..00000000 --- a/.eslintignore +++ /dev/null @@ -1,8 +0,0 @@ -build -config -coverage -dist -installer -node_modules -js/preload/_*.js -tests/** diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index b6b9fb22..00000000 --- a/.eslintrc +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": [ - "airbnb-base/rules/best-practices", - "airbnb-base/rules/errors", - "airbnb-base/rules/node", - "airbnb-base/rules/strict", - "airbnb-base/rules/variables" - ], - "rules": { - "comma-dangle": 0, - "vars-on-top": 0, - "one-var": 0, - "indent": [ 2, 4, { - "SwitchCase": 1 - } ], - "space-in-parens": 0, - "func-names" : 0, - "eol-last": 0, - "no-use-before-define": 0, - "strict": 0 - }, - "env": { - "browser": true, - "node": true, - "es6": true - }, - "globals": { - "SYM_API": true, - "ssf": true - } -} diff --git a/NOTICE b/NOTICE index 7042d466..02cf214d 100644 --- a/NOTICE +++ b/NOTICE @@ -1,2 +1,2 @@ SymphonyElectron -Copyright 2016 Symphony Software Foundation +Copyright 2019 Symphony Software Foundation diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index ab7152e8..bf47695b 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -7,7 +7,7 @@ Describe the problem or feature in addition to a link to the Describe the approach you've taken to implement this change / resolve the issue ## Related PRs -List related PRs against other branches / repositories: +List related PRs against other branches/repositories: branch | PR ------ | ------ diff --git a/js/aboutApp/about-app.html b/js/aboutApp/about-app.html deleted file mode 100644 index 1ed09910..00000000 --- a/js/aboutApp/about-app.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - About Symphony - - - -
- - Symphony - - -
- - diff --git a/js/aboutApp/index.js b/js/aboutApp/index.js deleted file mode 100644 index ef4c0023..00000000 --- a/js/aboutApp/index.js +++ /dev/null @@ -1,144 +0,0 @@ -'use strict'; - -const electron = require('electron'); -const BrowserWindow = electron.BrowserWindow; -const path = require('path'); -const fs = require('fs'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const { version, clientVersion, buildNumber } = require('../../package.json'); -const { initCrashReporterMain, initCrashReporterRenderer } = require('../crashReporter.js'); -const i18n = require('../translation/i18n'); -const { isMac } = require('../utils/misc'); - -let aboutWindow; - -let windowConfig = { - width: 350, - height: 260, - show: false, - modal: true, - autoHideMenuBar: true, - titleBarStyle: true, - resizable: false, - fullscreenable: false, - webPreferences: { - preload: path.join(__dirname, 'renderer.js'), - sandbox: true, - nodeIntegration: false, - devTools: false - } -}; - -/** - * method to get the HTML template path - * @returns {string} - */ -function getTemplatePath() { - let templatePath = path.join(__dirname, 'about-app.html'); - try { - fs.statSync(templatePath).isFile(); - } catch (err) { - log.send(logLevels.ERROR, 'about-window: Could not find template ("' + templatePath + '").'); - } - return 'file://' + templatePath; -} - -/** - * Opens the about application window for a specific window - * @param {String} windowName - name of the window upon - * which this window should show - */ -function openAboutWindow(windowName) { - - // This prevents creating multiple instances of the - // about window - if (aboutWindow) { - if (aboutWindow.isMinimized()) { - aboutWindow.restore(); - } - aboutWindow.focus(); - return; - } - let allWindows = BrowserWindow.getAllWindows(); - allWindows = allWindows.find((window) => { return window.winName === windowName }); - - // if we couldn't find any window matching the window name - // it will render as a new window - if (allWindows) { - windowConfig.parent = allWindows; - } - - aboutWindow = new BrowserWindow(windowConfig); - aboutWindow.setVisibleOnAllWorkspaces(true); - aboutWindow.loadURL(getTemplatePath()); - - // sets the AlwaysOnTop property for the about window - // if the main window's AlwaysOnTop is true - let focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow && focusedWindow.isAlwaysOnTop()) { - aboutWindow.setAlwaysOnTop(true); - } - - aboutWindow.once('ready-to-show', () => { - aboutWindow.show(); - }); - - aboutWindow.webContents.on('did-finish-load', () => { - // initialize crash reporter - initCrashReporterMain({ process: 'about app window' }); - initCrashReporterRenderer(aboutWindow, { process: 'render | about app window' }); - aboutWindow.webContents.send('versionInfo', { - version, - clientVersion, - buildNumber, - i18n: i18n.getMessageFor('AboutSymphony') - }); - if (!isMac) { - // prevents from displaying menu items when "alt" key is pressed - aboutWindow.setMenu(null); - } - }); - - aboutWindow.webContents.on('crashed', function (event, killed) { - - log.send(logLevels.INFO, `About Window crashed! Killed? ${killed}`); - - if (killed) { - return; - } - - const options = { - type: 'error', - title: i18n.getMessageFor('Renderer Process Crashed'), - message: i18n.getMessageFor('Oops! Looks like we have had a crash.'), - buttons: ['Close'] - }; - - electron.dialog.showMessageBox(options, function () { - if (aboutWindow && !aboutWindow.isDestroyed()) { - aboutWindow.close(); - } - }); - }); - - aboutWindow.on('close', () => { - destroyWindow(); - }); - - aboutWindow.on('closed', () => { - destroyWindow(); - }); -} - -/** - * Destroys a window - */ -function destroyWindow() { - aboutWindow = null; -} - - -module.exports = { - openAboutWindow: openAboutWindow -}; diff --git a/js/aboutApp/renderer.js b/js/aboutApp/renderer.js deleted file mode 100644 index b7304c17..00000000 --- a/js/aboutApp/renderer.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict'; -const { remote, ipcRenderer, crashReporter } = require('electron'); - -renderDom(); - -/** - * Method that renders application data - */ -function renderDom() { - document.addEventListener('DOMContentLoaded', function () { - const applicationName = remote.app.getName() || 'Symphony'; - let appName = document.getElementById('app-name'); - let copyright = document.getElementById('copyright'); - - appName.innerHTML = applicationName; - copyright.innerHTML = `Copyright © ${new Date().getFullYear()} ${applicationName}` - }); -} - -ipcRenderer.on('versionInfo', (event, versionInfo) => { - const versionText = document.getElementById('version'); - const { version, clientVersion, buildNumber, i18n } = versionInfo; - - document.title = i18n['About Symphony'] || 'About Symphony'; - - if (versionText) { - versionText.innerHTML = version ? `${i18n.Version || 'Version'} ${clientVersion}-${version} (${buildNumber})` : 'N/A'; - } -}); - -ipcRenderer.on('register-crash-reporter', (event, arg) => { - if (arg && typeof arg === 'object') { - crashReporter.start(arg); - } -}); - -// note: this is a workaround until -// https://github.com/electron/electron/issues/8841 -// is fixed on the electron. where 'will-navigate' -// is never fired in sandbox mode -// -// This is required in order to prevent from loading -// dropped content -window.addEventListener('drop', function(e) { - e.preventDefault(); - e.stopPropagation(); -}); - -window.addEventListener('dragover', function(e) { - e.preventDefault(); - e.stopPropagation(); -}); \ No newline at end of file diff --git a/js/aboutApp/symphony-logo.png b/js/aboutApp/symphony-logo.png deleted file mode 100644 index 500c36debfc82f1e18e4fc579a28b0362ddcc9f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10257 zcmY+qWmKFo*DgFr(ZO|q;_mL!f#N#I;BLj;wYUuK?i8oRix+o_OQBHQtvIC|`aJLV ze&^h4B*V^J=K|y-`J8z|FzE+6VU}Z1> z@FfBLkLjD&KBc*wx-tOZOAi19hXDYOucF`s0KkJ205~!M0E9CE03zqyc8KWf0IIW` zt{VV=j`y#?0kU(60RT8%8%-T|9c3k9b0-IOQwt|EOLlJu=T~h2K-63Kwdr8#Zc6Fx zVDIQA>@7z99}VHx_CGNPHRXR)-0j4ub(Ga8rJP(XDf!rW**U4j(I_b?MO`hdgw@~6 z{Ll2)o*4B9cXwxD4h}CbFLo~;b|+VB4lW@fAr4M%4sLF?R}D5dA4hjnZ#G9an*W0Q ze>m?g-OOEWoZW4l94Y_dnwmLzxQkI!|1S@;5#=OCO7Fj8Wjj zu3DkgxyPbfPsxMi_|0;_GUkoW>8bX-(GMjWLFQ=$EYZ!*t1*_h5C|k5k3pqLyG|>% zKLP`NeqrIwVx1xBJF!5X(7FyWq^69;s=R4eL^upDA)I=4nE%qxEwRr-}-SiloFiI7wt6P}a`f)3zbIHva4gXFGktwD!KcFbgRu>3DW_ zwgMVDdaRqP>sTVR-Ze8wEoi4$I@Vx!clU2+r)Z08^mp~506(0FA2C>yOS^s;av3ZK z@4h)ZR0ysD;^(uoN%pI>s{MboIM~hJnBsr3w6fX*{26T<5+RF+$SFn^03|Sq$|}WV zZ!`%|5EVA}LwzE1a&qp14(_<2!d@O9Sw{M+H&r{Vdfr6BYYe?EXHJZFwe^Zp;X>tAN#MnIlA zv89Rj6{H_#Ii4hPcsA$|o1$kmgfXK!iO7DO`qR+ZyfXHsx69oDerT{WZNRMYM>Bb1 zT2yDeI+;1`UK)w*&|ZOJcqOw=tJE%gwZNSs7VvFPGSY1EA$43S!JVf*UY%3BT+g)> zY76n@tRjc>R&lrX*?gtOiW-FkA~7=1pr$A43eO#0UVI zr#yu>#dEE7m^iP?_yIf-oTNDse9li7a_D-p{Dsy1(fYtY2}3D_Vf(hw8V?QsIU)~ufNP4 zNjH=MOtX;wGXp$ZxKQkP2ehF-JwbRftKwpN`1LqdLo^YNTZ-!8(p_I!Raa*OOKLN` z!|jI3w^RfY%pa-aNUyFSuTwNSJ*$L=TOtvetU@~xZoZfmm|C&zwNbT=v7&1FE2kjNUSU3ZoMh6d4yFp zDx)45QkvDoGA#bZ>3voJAI9G|%`B!_rbkc`v2XZS$A#UiCkQtUd}9-B)5XK$pE=wY zPH3Lc_7^<|$uhzB#JaxcP9Mw_3?_B)`{8Ns@j|U`?i!g1TG$cagqDT}@D+Kka#CUz z31+SME~4AMuVzZYZo!9p`{YOZzzZwTw!VoAu-ZTkwo)*bQ|Oq;fwLc%MX(Vz13^XshA0tWF#q*Ake_p+?pp1*O@N%8bkt7|+9-PL}R1xCH$`eKS# z)t$H&uoMH@n~(%KV;3)I7I60`ppsgNo*D)45}zm)Jr=tiuMqX+nW6Et?vDUZHHqDv zmLf1ni5<%!{lBC1ac+N^tvwt`rIWiZBMw9TbBJ&efjBy*9$agRDhOu`gyls}^fCiu z_8O){^lLWBI!WEjpV4!*+h=oEkHB(=2Y9lTC?2riS+ls=Rz2De{^E36xA){|`osrJ zE+a8U+aIh|SR$G-K9rd*_A>ZhJs#Kl)wds2t;WQ}#BOhG{p?4HlZl&btLPR)tmQ_z zYeR5WpdPELvY7i8psuGuqjlq9(aqgFiaDUe*OeCV=R2ZS?(wm4vA4W4>1j(a%vxDY23o2 z6W~*($DB-bIRqtV3CmHhpBaI`BzX5C#(t;G598@CyL%aZ=Phb=xhch5YFXhUv45Ae<=GC`?k&l~=Eq*7zNz_m-sY5y0u66BL zUpD?-eSw`iVXXn(=21owgh|p05XPU`6-hD06n*m_T!%G2p%e6yn@YoCB za|@rSKr&=olboQzCiBE9>5^f2d(G6USpG@@vJZ4J;;$}a)9*)@4H1BMPcmD-I;v%T z-hu(FkI~T38k{kY91Riv6e`$Xc9G8m<&^_{Of59)bBg1H!^&v3q<(DQq#BK zFIVM`rl+92JfCI~`Ev{x6^v5D#9$$R=&VZNYoL34>8DcWWG0uclPCf%QlKm_JQG(x z!eNS@wqK zR8op|vqd3BV%7=a=5`5AHEG$A$w2+w-Lzgfz$+zOOo^IP#k^C8Q(~bcQ(jz$m01L# zw3_^YrN+$q07yXYiS9a!pR%hgC`8N+_xS01Tzexbh%^c_7^$~6rQwv11h=}aXu z`nykRlFRg3sRcT?8Zw#0DX8E+>@n<*j6%*AnLzBCsh2cyG7JqhFu{+=ApA{0o=T_? zH5~@-ETzXnv#qiuy$-HfgpKhSGmC{#DGeVguqjPjAmDf8GL96whM7{0RF2aib~kQt z%6^<^7%YC854~xONDt!l(+*+60goHuoJkT4{82^mpPA)u5>l zn7CLg#i%oni7nlbAz`$fhhe9HfX;RHFmWxZe~^Hb4P#$nFzA4B5P64W>eO=#O#GWd zDT`^vkaKTM#N0uT+M~DUwhB<5Os-t`yv;;3A8Szu0{>XM*(qD8i{$(hPMBz=^IEV zRxJ?k>J^1MqXTPa2NhBCvY1Uos{O)!Sa)AoY^Bb7IeY$sy*A`8Rnz8Ib^Bf+1jT~jmK;|KdTb?#g80hW8!viyUWA!M?_w+=S zJlQgjy#yv)AH+klbz)6 zP(-R^2bYqW%%iQh{KU`1a`QFOCoxG=zWqY0XqttzCB@}NfxmD0vz}T{xy5GLBt+FH zDc%Z#>U6ZX_uA4dT6^A~ zq|Trn!?^3F-!QloS?P4B|K=cv{%z-=NrX;X$Jn{*F-Y6wc72Tz!-PBwH@C42pDqj- z{n17=piT`s(fOVm84ChhwTKXU%<6ub*L^q}e@PW-%}exgF_MpWAEh+tdAirAtf|$$ zRpP2I8=Z=Xvcw)5SQCG4Chkk4!Tl<>^@e!GO3~rwxibvgqBeG8{j-$q9dx8-hcPQV zhn3@;IaRmy4>a)hpkR0Yka_{KhoNO2AsRJ7oI*1_X-F-?GER#IEJY`$YopPiqVg_` zMTTOGY$m>$vnHVoNh$(DLVHV-n9rQ_u|`9*`NxeFt1KaA`1z7%&$Bxz@{pt}18}&# z_=gV(o1O$rdCw)Bd|bD&*& zC>&>hx!Xw@4K22usPO)JzJF}F=#Vgy-P6O->~T{&PYj-CK3!GZ8(Ux5wv_TH+cZ3Y z7LPA7zFlh-)`U{Gs^roO(a18BNWN4YvR=)w<7;V-OF8X~E}WmI!B8?L00_+4rWitzTZB^gX^2C#baWVq*vqu)Dn$HF zBIl=Teuzhhxk|9ufjJQp*`$UDD@$(%RmJo<59NlFvEiM%r$9OpuXAmT<+yH4SyQYd zzYIevp`Ra=d`RiC@eo4yksxHJ#D}(3=<@Bpi}umcF;Jl8Tla}Npg?f>57+Cawc8`v zhOIej-9&6Ua%}B5VL6(&T{67fm_Q#i^#+NT+!%r3Y&5T1tj}7_c;;r4S0sKBd{Rod z^E9&Dq30jxbT5Y-hX1-E;vnXxCCBMw#SFBoL1NFWBXbfx>slZU=4;#!F~EoC#tw_I z9fGUl;TJ|}>)3zCZj-!zXT2Q(HN(iGFIw>XW}R`gRn=X~t=Xwu>Q>o91`O4Z+5=ZP zL-UkBE_38r4||?MIvcdHTV3ktzHO3o6Q9SZdOZ*LDVQ|H7wiNwi@DWrPiL`H(LsE>E zIf*jmSHeaH>?}XWQgT`;l7t{Q$se;c-z;%3j`=2z0xyOVnYjZ5=U0X_=+vQx)&nI| zj>4a!J=FLtPe}u6VtR*eu#WEv z;+eQ`wnLMoR3f)|DT5gsRChJra+qX)G@DVjE>ZH_Fq7)1IyD_Zne;vw9wune%_wtO z=F2FkD1W}4E?BpV;7{ZXLrN$Ir1fJ1{$j9 ziu7sxoZg5w$5d6cd?r2F+;xFyuQF1*G;3m6+gYW%gI)FPk=J-{*qe4{SCb_`fu6$t z%{K)IxLxwcqRn10{#+horMmc2#RqLK!@qNA$Iie**HhMtN0grKB8UoMx_Nr97{gzl zTq>M49=d+B#CCy9>2l|s&37(r6AAh<%3YF+wUa46Zu=t$623a|WFmFU9lm{} zJ5d(~`(2DP{c4p+sy%7vd9Hf`t}6Y1b=6)8m$zid`px&@o-Z?XgssZ}#mE@qV7+e& z?%dZBX}jnVhzG?lsT;3Yj!TP1)$-a^C+0NR$)p5V4kCC)?1RDVNSDdjOi2fFWb;s9 zOaHdB&v!5GkkE-=oB^MHZSUkH{{Z|UKKP>U{`Z4Vt%cG^YSA{)NW#ki^5wEZP9JQ^`Y&6*kk`lL<6b+CV%iU^pqvW++lYDRw%38bLJ>D7Q2v8>f`tszeeM?)z6hB7Cx%Z@4 zy?ch$I@+2>V8 zbXjp-22r6NgklYaRbaqL#hX667giZL_kxb$v1GNhA?jDw{i`8RF2r%Khbgb_66&PU z&w3F7MSU%(^;&g#!AW_KPpeEIb-r?N@t5$)Q&dmDxX>sEM}`AkS#p?=v>T6RHU$qI z_fRC{1Oed!7MdX(H}ZJ{rSXr;4ymYhV(G9kgI()MN^{Eac#Lcy)l0x7uaDP@%h&D6 zh0J~v2b^DtF^zQUS;nwR=d?C-(m_MG&C6-jN{`KCodm&NUim9DAXbcN>>KsZltMCf z3i7xTrE(q=r|sVhgn`-K*V0ah?t~yb-Uf&beVhjrnx2U4Zh^w1Q)pf`USFSt^J^Yd z{KLxv(a=@BQ`G-lJMGL8Ncw9-t|H#+);_K5cxhy9nYOa8=)>8pt9hay?P(TT>4`w6*S#+r zQ0O0$o zyqO$8>H5iKMpj7TByz*)elp7KUX3W&rN7~<5{ZU6b~jcRZzp~wV>}tuXrU^dX7I8T zdu8F9y%zXG`D(B_L+-7peq_%WOIDJDE=jN%OpNk^l{)3G)RF8G#5i+K?`< z!n|^so`C@$T5*1bxS)l{5xZ^W-#+Z6m0l&dn_Yr-T zm`fFK;0MHdjAbVROClL3`pZ>q{R@;k5+i9aV=|qkuFYU?x`3v2<1c}a)4Gh0@2<%5 z&`P8fycC_aZ3FzzCBz}OB)EL%kQucVRs`fh)Tu4zY@xwLTqh5BIKJIxS$Arn68v66 zVVF)MBi7U*Gi$`A7?O9AmgtH?$ELL=#&#V4)Lgi6rBnT z`#O@S3LB!b2y`wYD*?yR>W}i(%qc!hSyxW6b!p?S)+2z#^^spLs$^d>5KjR|j|`A*ZIS@MbB}oTN&$Umb{SDOV3+M`R^UaW?zvBL5Xk0J zZ@)p6kjIqx05M|fQ(|SnVXiDyA@?gFsTdtGn8xC8~ea#8>Y%9xf3S zON$F3MnUIosRRqL$Uj6f#pP}ffR+NwGO9dMIK{p(U~3Zt*@@_`tVOf7%l0&iXxrCx z#%}`H*~)z^X?Vluwf*IW%Y!n1$CPUykzqNQC>b}@dQUo2%Yj`YmhGI;$K^6(m!maM zH9ZuE$r~I=iDLraygLH<>T~RwMMz4+$N9LH#8%~yjJl09#?v#BLS*ElkyNRgQ>a?y z@9&>Gku7F;W_an~b5kIZykGF#ZV^XH$?@_WmYb>v5{Zu~YHrs`T7T)ULo!u*b+c|& zT{jBlAi#$>XWctARVp^TxqjSzg(0ztCO1zBluXwkp}HU)EvT-|^2wIP(#pM9RMaVp zl}thzDT@JP6vcx|0FJ${=>hpXD44W0nbV$0Z*D-nh9H6sMD68&ZkfgDSvrxQRjuC4 zMrnS~c1DcaJY;v6_Io$o=`=m`DM8)}+OBX;cWAPWreQV_p)^qot_Ss@AV+Cl1-!B# z0hgDT4DU2sPf87MhLldWy)0)&cE~LyZ`dHD}lLc@2aSuL(Dv zotYztRCAqH2imr@7nTHOZ~}1SV+nHtjiGkw@0D>crW!bQR*pfXO0dkPh49<$ez=m~ z=i3i;rmHO;#LUi>IenJukO4g&6bx`qB$Fq8N z{*5;#+A%bi^7;0)~>QEmEoWW;i zcUWy2jXW=w#RPHh0Mc(Mva|eBscJNM!e#O)A%;I#Kt3V*DB?JeVjfuaLH^YE5yOev9aF!4<8d-z5400R>n$C_XU2$K0o3Od)lrg-fR>bJy zo#hE#k`=6SusKb`YlO@IKGVm7)HF%~(QcLaTvoN&IoYc5Es@td+SlEQAQ#^HR&6qo zvXG+JI_yH9#P%XMB}fu37pf*Q57yxIlMWy#SBA`K+-r)6K1nY{#l2fa)A2|F@t(*m zW0)I9Vl*TP*6T+gt0#y)VO4&clW}yvh0}-v@mC>OE3=&wk83}Z z?(>k9R$98Sp%c~&d{iy74UmFib&9P?5|`5b5#G=j_Rf&8VrVF;7f3j;!t_^fC=iB>WhL?LP=u##zO6Q0=8_jvQ)i zhj=scanmW(JuK+E0wW(7Egv)0u{`_WvzFt?#U2^|Ei>ViLTl^F>kR}f;|=^Kt4AT8 zv8*4wuQPJrj-WNh^K=qb`YJrng)@2%JGKq6-$iWXdh$j0a8kC6kZZe))u+K2|D zX-0?N$l+Mb2BvvWDLqj{lvxqzTTJ!QWh2GnO&J79M^EwpcrW3SVF&itM2k_mu~*D> z40ky9Qr+ceSeWSnr;I;-fccWI|KcJicdhO(-*)7bR3Noad$dn9cv`ywhL#+0(q@X~vFL3JuUXWHh9z-c%AR)77oJ%96o`TQT)eD5L zu}20zF#ne_3GKx~j`$ha`sqs|lCD-SGGnHQ1__x{*UchU2CJ?slNCeCOqTdFN_n0f zi@kca=5)HNt^`sj_OOrl81|!l-P?TH4A@&%o*`=m70-5HZ=;tmcZl@DiPGTp1jYO~ z*5rE=;jUPnA}Dg8gJv&28wgFKk~Vo;eR>B znq)Ub+zt4AcPvadi#ED&ydOD^5$Zehi0(O^f;Km2cs@3uDVKvue{Ww%I^k4(srW|; z4op~@Rq(}-UrfxJegYL|hN!1v3wTi31&e*1Tx2<>>1$~VB%sCfO~N^;4u|_fB|{IL zmmHXyg7Jg;lpKUQ1i5P1|BzTW+NkCaxu1QqGUb1lbRvKWFePv_0RG0Q3xxeZf3Fw7 zK>wLk6_3U&yoL3|{&e+Avvh&PhYVar^=0CisyG?q2kY;q!tvOFnSVoss%W;4G;mKS zwu6Skc>4wzb1j;f-tJpRCiPF@kLNq114ivRag}^V!!|>B%2ah{uI!4{BE`v=p-z^^ z=i8Bur0Z4@-*Efe?1sX)2R~BW#kFt^l5hn8Rg49!F~o24hykqPis}e_Fr0HPtEO@M zIp;523S&I{n%A&?!ke$CdI9DID_-3F1I5~}Y;9W@n?a@u=sKi|2aP%-m_G; zgS<>VSHFEvbLl}i4fOXPIOD0_`7NA@Wt;6jYc0L+7(G^qad#?R2j<2r*6w#9T-%*W za7jtiVt2WEGg4_7y`BDPaZF^EoxTp0_IA}B;Ms~pEsrl8CwO2Jij}3my)gbP$*l>N zzBO(&bk#r9)9SE>>FWf%Ml7r0Jgbe0e2*Y0?O$DW%8COhWtQJDgW(Tn;C+u zr?iT>1vz*06e!RaN|0%@(wl$R`cs?;vYcU zv%llUf0NM__R8Eg^P<0qdSC?`!I#TA>)O)dOaK0fDf&&Ke#|_tCdc73Tgzw*!J7+t zyjSiRXDQ!z)^eg)Z2Lx`tliC2ZvK>sq9^RIb1&QOezxRy_tV*L3UU8T+23jXN#j&B zGO&?vD_ip3!a{J`{B=cgJ4EOA%L%q6VzO4h94MKP5LC8>VRl%VpPvq9N}kG9@_c28 zrlzKDMejF5RXQ=xj2o>mf|Qk=J{>!_o*q+Q_Ybbjb>J@oJSv@$(ZlVehIPd=w|j-m zh*TmvyqU>(vL6#4OT5n3vcim?Kq#BZmj)831$HqAZWVtkpa+a}_ZXk|m{~W+dJk|r zjUc%>IpeK{ajoyP_|1m~ZYin6Taf9oO)y+5oYK|vRT_tHbaW7A%?VmGAo>o6-lyB^ zS3mL)(U)16HW|KN&bj$7o9C|Bd`jX^Ht^OT?fNi7y$W|@6fug(h6-DJXIJ3jxdiPL zQ*G@hL|Lz^+$LyRDp#2kx7)?i7p7Or+#Hzh629P_$}g9AZ{Hlh!PnXcG%;pV)6ybX zZnVbI!T~oURQIo9G28uY${=J9YTXJEUaHM*hqiUy_Lr?}ll|VCME}7YZ_E7?vsA2R zJS_HDE_-T@rMoM%YxW%j^C=43+flr_(c9jo2a#mi{Zqb|7r1KiGmE^{k&S;3@8qRb K-q%T*g!~`c=r?Wv diff --git a/js/activityDetection/index.js b/js/activityDetection/index.js deleted file mode 100644 index 090779b2..00000000 --- a/js/activityDetection/index.js +++ /dev/null @@ -1,126 +0,0 @@ -'use strict'; -const electron = require('electron'); -const { app } = electron; -const throttle = require('../utils/throttle'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); - -let setIsAutoReloadFnc; -let getIsOnlineFnc; -if (!process.env.ELECTRON_QA) { - /* eslint-disable global-require */ - const { getIsOnline, setIsAutoReload } = require('../windowMgr'); - getIsOnlineFnc = getIsOnline; - setIsAutoReloadFnc = setIsAutoReload; - /* eslint-enable global-require */ -} - -let maxIdleTime; -let activityWindow; -let intervalId; -let throttleActivity; -/** - * Check if the user is idle - */ -function activityDetection() { - - if (app.isReady()) { - - electron.powerMonitor.querySystemIdleTime((time) => { - // sent Idle time in milliseconds - let idleTime = time * 1000 + 1; //ensuring that zero wont be sent - if (idleTime != null && idleTime !== undefined) { - - if (idleTime < maxIdleTime) { - - let systemActivity = { isUserIdle: false, systemIdleTime: idleTime }; - if (systemActivity && !systemActivity.isUserIdle && typeof systemActivity.systemIdleTime === 'number') { - return self.send({ systemIdleTime: systemActivity.systemIdleTime }); - } - } - } - // If idle for more than 4 mins, monitor system idle status every second - if (!intervalId) { - self.monitorUserActivity(); - } - return null; - }) - } -} - -/** - * Start monitoring user activity status. - * Run every 4 mins to check user idle status - */ -function initiateActivityDetection() { - - if (!throttleActivity) { - throttleActivity = throttle(maxIdleTime, activityDetection); - setInterval(throttleActivity, maxIdleTime); - } - self.activityDetection(); -} - -/** - * Monitor system idle status every second - */ -function monitorUserActivity() { - intervalId = setInterval(monitor, 1000); - - function monitor() { - if (app.isReady()) { - electron.powerMonitor.querySystemIdleTime((time) => { - // sent Idle time in milliseconds - let idleTime = time * 1000 + 1; //ensuring that zero wont be sent - if (idleTime != null && idleTime !== undefined) { - if (idleTime < maxIdleTime && typeof getIsOnlineFnc === 'function' && getIsOnlineFnc()) { - // If system is active, send an update to the app bridge and clear the timer - self.activityDetection(); - if (typeof setIsAutoReloadFnc === 'function') { - setIsAutoReloadFnc(false); - } - clearInterval(intervalId); - intervalId = undefined; - } - } - }); - } - - } -} - -/** - * Sends user activity status from main process to activity detection hosted by - * renderer process. Allows main process to use activity detection - * provided by JS. - * @param {object} data - data as object - */ -function send(data) { - if (activityWindow && data) { - log.send(logLevels.INFO, 'activity occurred at time= ' + new Date().toUTCString()); - activityWindow.send('activity', { - systemIdleTime: data.systemIdleTime - }); - } -} - -/** - * Set the activity's window - * @param period - * @param win - */ -function setActivityWindow(period, win) { - maxIdleTime = period; - activityWindow = win; - // Initiate activity detection to monitor user activity status - initiateActivityDetection(); -} - -// Exporting this for unit tests -const self = { - send: send, - setActivityWindow: setActivityWindow, - activityDetection: activityDetection, - monitorUserActivity: monitorUserActivity, -}; -module.exports = self; diff --git a/js/autoLaunch/index.js b/js/autoLaunch/index.js deleted file mode 100644 index 7bc4a56b..00000000 --- a/js/autoLaunch/index.js +++ /dev/null @@ -1,60 +0,0 @@ -const AutoLaunch = require('auto-launch'); - -// Local Dependencies -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const { readConfigFileSync } = require('../config.js'); -const { isMac } = require('../utils/misc.js'); - -const globalConfigData = readConfigFileSync(); -const props = isMac ? { - name: 'Symphony', - mac: { - useLaunchAgent: true, - }, - path: process.execPath, -} : { - name: 'Symphony', - path: getAutoLaunchPath() || process.execPath, -}; - -class AutoLaunchController extends AutoLaunch { - - constructor(opts) { - super(opts); - } - - /** - * Enable auto launch - * @return {Promise} - */ - enableAutoLaunch() { - log.send(logLevels.INFO, `Enabling auto launch!`); - return this.enable(); - } - - /** - * Disable auto launch - * @return {Promise} - */ - disableAutoLaunch() { - log.send(logLevels.INFO, `Disabling auto launch!`); - return this.disable(); - } -} - -/** - * Replace forward slash in the path to backward slash - * @return {any} - */ -function getAutoLaunchPath() { - const autoLaunchPath = globalConfigData && globalConfigData.autoLaunchPath || null; - return autoLaunchPath ? autoLaunchPath.replace(/\//g, '\\') : null; -} - -const autoLaunchInstance = new AutoLaunchController(props); - -module.exports = { - enable: autoLaunchInstance.enableAutoLaunch.bind(autoLaunchInstance), - disable: autoLaunchInstance.disableAutoLaunch.bind(autoLaunchInstance) -}; \ No newline at end of file diff --git a/js/badgeCount.js b/js/badgeCount.js deleted file mode 100644 index bd8d43ed..00000000 --- a/js/badgeCount.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -const electron = require('electron'); -const app = electron.app; -const nativeImage = electron.nativeImage; - -const { isMac } = require('./utils/misc.js'); -const windowMgr = require('./windowMgr.js'); -const maxCount = 1e8; -const log = require('./log.js'); -const logLevels = require('./enums/logLevels.js'); - -/** - * Shows the badge count - * @param count - */ -function show(count) { - if (typeof count !== 'number') { - log.send(logLevels.WARN, 'badgeCount: invalid func arg, must be a number: ' + count); - return; - } - - if (isMac) { - // too big of a number here and setBadgeCount crashes - app.setBadgeCount(Math.min(maxCount, count)); - return; - } - - // handle ms windows... - const mainWindow = windowMgr.getMainWindow(); - - if (mainWindow) { - if (count > 0) { - // get badge img from renderer process, will return - // img dataUrl in setDataUrl func. - mainWindow.send('createBadgeDataUrl', { count: count }); - } else { - // clear badge count icon - mainWindow.setOverlayIcon(null, ''); - } - } -} - -/** - * Sets the data url - * @param dataUrl - * @param count - */ -function setDataUrl(dataUrl, count) { - const mainWindow = windowMgr.getMainWindow(); - if (mainWindow && dataUrl && count) { - let img = nativeImage.createFromDataURL(dataUrl); - // for accessibility screen readers - const desc = 'Symphony has ' + count + ' unread messages'; - mainWindow.setOverlayIcon(img, desc); - } -} - -module.exports = { - show: show, - setDataUrl: setDataUrl -}; diff --git a/js/basicAuth/basic-auth.html b/js/basicAuth/basic-auth.html deleted file mode 100644 index 5291ef37..00000000 --- a/js/basicAuth/basic-auth.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - -
- - - -
- - - - - - - - - - - -
- -
- -
- -
-
- - \ No newline at end of file diff --git a/js/basicAuth/index.js b/js/basicAuth/index.js deleted file mode 100644 index 6c9a4168..00000000 --- a/js/basicAuth/index.js +++ /dev/null @@ -1,177 +0,0 @@ -'use strict'; - -const electron = require('electron'); -const BrowserWindow = electron.BrowserWindow; -const ipc = electron.ipcMain; -const path = require('path'); -const fs = require('fs'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const { isMac } = require('../utils/misc'); -const { initCrashReporterMain, initCrashReporterRenderer } = require('../crashReporter.js'); -const i18n = require('../translation/i18n'); - -let basicAuthWindow; - -const local = {}; - -let windowConfig = { - width: 360, - height: isMac ? 270 : 295, - show: false, - modal: true, - autoHideMenuBar: true, - titleBarStyle: true, - resizable: false, - webPreferences: { - preload: path.join(__dirname, 'renderer.js'), - sandbox: true, - nodeIntegration: false, - devTools: false - } -}; - -/** - * method to get the HTML template path - * @returns {string} - */ -function getTemplatePath() { - let templatePath = path.join(__dirname, 'basic-auth.html'); - try { - fs.statSync(templatePath).isFile(); - } catch (err) { - log.send(logLevels.ERROR, 'basic-auth: Could not find template ("' + templatePath + '").'); - } - return 'file://' + templatePath; -} - -/** - * Opens the basic auth window for authentication - * @param {String} windowName - name of the window upon which this window should show - * @param {String} hostname - name of the website that requires authentication - * @param {boolean} isValidCredentials - false if invalid username or password - * @param {Function} clearSettings - * @param {Function} callback - */ -function openBasicAuthWindow(windowName, hostname, isValidCredentials, clearSettings, callback) { - - // Register callback function - if (typeof callback === 'function') { - local.authCallback = callback; - } - // Register close function - if (typeof clearSettings === 'function') { - local.clearSettings = clearSettings; - } - - // This prevents creating multiple instances of the - // basic auth window - if (basicAuthWindow) { - if (basicAuthWindow.isMinimized()) { - basicAuthWindow.restore(); - } - basicAuthWindow.focus(); - return; - } - let allWindows = BrowserWindow.getAllWindows(); - allWindows = allWindows.find((window) => { return window.winName === windowName }); - - // if we couldn't find any window matching the window name - // it will render as a new window - if (allWindows) { - windowConfig.parent = allWindows; - } - - basicAuthWindow = new BrowserWindow(windowConfig); - basicAuthWindow.setVisibleOnAllWorkspaces(true); - basicAuthWindow.loadURL(getTemplatePath()); - - // sets the AlwaysOnTop property for the basic auth window - // if the main window's AlwaysOnTop is true - let focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow && focusedWindow.isAlwaysOnTop()) { - basicAuthWindow.setAlwaysOnTop(true); - } - - basicAuthWindow.once('ready-to-show', () => { - basicAuthWindow.show(); - }); - - basicAuthWindow.webContents.on('did-finish-load', () => { - const basicAuthContent = i18n.getMessageFor('BasicAuth'); - basicAuthWindow.webContents.send('i18n-basic-auth', basicAuthContent); - // initialize crash reporter - initCrashReporterMain({ process: 'basic auth window' }); - initCrashReporterRenderer(basicAuthWindow, { process: 'render | basic auth window' }); - basicAuthWindow.webContents.send('hostname', hostname); - basicAuthWindow.webContents.send('isValidCredentials', isValidCredentials); - if (!isMac) { - // prevents from displaying menu items when "alt" key is pressed - basicAuthWindow.setMenu(null); - } - }); - - basicAuthWindow.webContents.on('crashed', function (event, killed) { - - log.send(logLevels.INFO, `Basic Auth Window crashed! Killed? ${killed}`); - - if (killed) { - return; - } - - const options = { - type: 'error', - title: i18n.getMessageFor('Renderer Process Crashed'), - message: i18n.getMessageFor('Oops! Looks like we have had a crash.'), - buttons: ['Close'] - }; - - electron.dialog.showMessageBox(options, function () { - closeAuthWindow(true); - }); - }); - - basicAuthWindow.on('close', () => { - destroyWindow(); - }); - - basicAuthWindow.on('closed', () => { - destroyWindow(); - }); -} - -ipc.on('login', (event, args) => { - if (typeof args === 'object' && typeof local.authCallback === 'function') { - local.authCallback(args.username, args.password); - closeAuthWindow(false); - } -}); - -ipc.on('close-basic-auth', () => { - closeAuthWindow(true); -}); - -/** - * Destroys a window - */ -function destroyWindow() { - basicAuthWindow = null; -} - -/** - * Method to close the auth window - * @param {boolean} clearSettings - Whether to clear the auth settings - */ -function closeAuthWindow(clearSettings) { - if (clearSettings && typeof local.clearSettings === 'function') { - local.clearSettings(); - } - - if (basicAuthWindow) { - basicAuthWindow.close(); - } -} - -module.exports = { - openBasicAuthWindow: openBasicAuthWindow -}; diff --git a/js/basicAuth/renderer.js b/js/basicAuth/renderer.js deleted file mode 100644 index 6b4fff0f..00000000 --- a/js/basicAuth/renderer.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict'; -const { ipcRenderer, crashReporter } = require('electron'); - -renderDom(); - -/** - * Method that renders application data - */ -function renderDom() { - document.addEventListener('DOMContentLoaded', function () { - loadContent(); - }); -} - -function loadContent() { - let basicAuth = document.getElementById('basicAuth'); - let cancel = document.getElementById('cancel'); - - if (basicAuth) { - basicAuth.onsubmit = (e) => { - e.preventDefault(); - submitForm(); - }; - } - - if (cancel) { - cancel.addEventListener('click', () => { - ipcRenderer.send('close-basic-auth'); - }); - } -} - -/** - * Method that gets invoked on submitting the form - */ -function submitForm() { - let username = document.getElementById('username').value; - let password = document.getElementById('password').value; - - if (username && password) { - ipcRenderer.send('login', { username, password }); - } -} - -/** - * Updates the hosts name - */ -ipcRenderer.on('hostname', (event, host) => { - let hostname = document.getElementById('hostname'); - - if (hostname){ - hostname.innerHTML = host || 'unknown'; - } -}); - -/** - * Triggered if user credentials are invalid - */ -ipcRenderer.on('isValidCredentials', (event, isValidCredentials) => { - let credentialsError = document.getElementById('credentialsError'); - - if (credentialsError){ - credentialsError.style.display = isValidCredentials ? 'none' : 'block' - } -}); - -ipcRenderer.on('register-crash-reporter', (event, arg) => { - if (arg && typeof arg === 'object') { - crashReporter.start(arg); - } -}); - -ipcRenderer.on('i18n-basic-auth', (event, content) => { - if (content && typeof content === 'object') { - const i18nNodes = document.querySelectorAll('[data-i18n-text]'); - - for (let node of i18nNodes) { - if (node.attributes['data-i18n-text'] && node.attributes['data-i18n-text'].value) { - node.innerText = content[node.attributes['data-i18n-text'].value] || node.attributes['data-i18n-text'].value; - } - } - } -}); \ No newline at end of file diff --git a/js/bringToFront.js b/js/bringToFront.js deleted file mode 100644 index 5912273e..00000000 --- a/js/bringToFront.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const windowMgr = require('./windowMgr.js'); -const { getConfigField } = require('./config.js'); -const log = require('./log.js'); -const logLevels = require('./enums/logLevels.js'); - -/** - * Method that checks if user has enabled the bring to front feature - * if so then activates the main window - * @param {String} windowName - Name of the window to activate - * @param {String} reason - The reason for which the window is to be activated - */ -function bringToFront(windowName, reason) { - - getConfigField('bringToFront') - .then((bringToFrontSetting) => { - if (typeof bringToFrontSetting === 'boolean' && bringToFrontSetting) { - log.send(logLevels.INFO, 'Window has been activated for: ' + reason); - windowMgr.activate(windowName || 'main', false); - } - }) - .catch((error) => { - log.send(logLevels.ERROR, 'Could not read bringToFront field from config error= ' + error); - }); -} - - -module.exports = { - bringToFront: bringToFront -}; \ No newline at end of file diff --git a/js/cacheHandler/index.js b/js/cacheHandler/index.js deleted file mode 100644 index f883b8d1..00000000 --- a/js/cacheHandler/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const fs = require('fs'); -const nodePath = require('path'); -const electron = require('electron'); - -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); - -const cacheCheckFilename = 'CacheCheck'; -const cacheCheckFilePath = nodePath.join(electron.app.getPath('userData'), cacheCheckFilename); - -function handleCacheFailureCheckOnStartup() { - - return new Promise((resolve) => { - - if (fs.existsSync(cacheCheckFilePath)) { - log.send(logLevels.INFO, `Cache check file exists, so not clearing cache!`); - fs.unlinkSync(cacheCheckFilePath); - resolve(); - } else { - log.send(logLevels.INFO, `Cache check file does not exist, we are clearing the cache!`); - electron.session.defaultSession.clearCache(() => { - log.send(logLevels.INFO, `Cleared cache!`); - resolve(); - }); - } - - }); - -} - -function handleCacheFailureCheckOnExit() { - log.send(logLevels.INFO, `Clean exit! Creating cache check file!`); - fs.writeFileSync(cacheCheckFilePath, ""); -} - -module.exports = { - handleCacheFailureCheckOnStartup: handleCacheFailureCheckOnStartup, - handleCacheFailureCheckOnExit: handleCacheFailureCheckOnExit -}; diff --git a/js/config.js b/js/config.js deleted file mode 100644 index d445ce41..00000000 --- a/js/config.js +++ /dev/null @@ -1,420 +0,0 @@ -'use strict'; - -const electron = require('electron'); -const app = electron.app; -const path = require('path'); -const fs = require('fs'); -const omit = require('lodash.omit'); -const pick = require('lodash.pick'); -const difference = require('lodash.difference'); - -const isDevEnv = require('./utils/misc.js').isDevEnv; -const isMac = require('./utils/misc.js').isMac; -const getRegistry = require('./utils/getRegistry.js'); -const log = require('./log.js'); -const logLevels = require('./enums/logLevels.js'); -const { buildNumber } = require('../package.json'); - -const configFileName = 'Symphony.config'; - -// cached config when first reading files. initially undefined and will be -// updated when read from disk. -let userConfig; -let globalConfig; - -let ignoreSettings = [ - 'minimizeOnClose', - 'launchOnStartup', - 'alwaysOnTop', - 'url', - 'memoryRefresh', - 'bringToFront', - 'isCustomTitleBar' -]; - -/** - * Tries to read given field from user config file, if field doesn't exist - * then tries reading from global config. User config is stord in directory: - * app.getPath('userData') and file called Symphony.config. Global config is - * stored in file Symphony.config in directory where executable gets installed. - * - * Config is a flat key/value file. - * e.g. { url: 'https://my.symphony.com', } - * - * @param {String} fieldName Name of field to try fetching - * @return {Promise} Returns promise that will succeed with field - * value if found in either user or global config. Otherwise will fail promise. - */ -function getConfigField(fieldName) { - return getUserConfigField(fieldName) - .then((value) => { - // got value from user config - return value; - }, () => { - // failed to get value from user config, so try global config - return getGlobalConfigField(fieldName); - }); -} - -/** - * Gets a specific user config value for a field - * @param fieldName - * @returns {Promise} - */ -function getUserConfigField(fieldName) { - return readUserConfig().then((config) => { - if (typeof fieldName === 'string' && fieldName in config) { - return config[fieldName]; - } - - throw new Error('field does not exist in user config: ' + fieldName); - }); -} - -/** - * Reads the user config file and returns all the attributes - * @param customConfigPath - * @returns {Promise} - */ -function readUserConfig(customConfigPath) { - - return new Promise((resolve, reject) => { - - if (userConfig) { - resolve(userConfig); - return; - } - - let configPath = customConfigPath; - - if (!configPath) { - configPath = path.join(app.getPath('userData'), configFileName); - } - - log.send(logLevels.INFO, `config path ${configPath}`); - - fs.readFile(configPath, 'utf8', (err, data) => { - - if (err) { - log.send(logLevels.INFO, `cannot open user config file ${configPath}, error is ${err}`); - reject(new Error(`cannot open user config file ${configPath}, error is ${err}`)); - return; - } - - try { - // data is the contents of the text file we just read - userConfig = JSON.parse(data); - resolve(userConfig); - } catch (e) { - log.send(logLevels.INFO, `cannot parse user config data ${data}, error is ${e}`); - reject(new Error(`cannot parse user config data ${data}, error is ${e}`)); - } - - }); - }); -} - -/** - * Gets a specific global config value for a field - * @param fieldName - * @returns {Promise} - */ -function getGlobalConfigField(fieldName) { - return readGlobalConfig().then((config) => { - if (typeof fieldName === 'string' && fieldName in config) { - return config[fieldName]; - } - - throw new Error('field does not exist in global config: ' + fieldName); - }); -} - -/** - * reads global configuration file: config/Symphony.config. this file is - * hold items (such as the start url) that are intended to be used as - * global (or default) values for all users running this app. for production - * this file is located relative to the executable - it is placed there by - * the installer. this makes the file easily modifable by admin (or person who - * installed app). for dev env, the file is read directly from packed asar file. - */ -function readGlobalConfig() { - return new Promise((resolve, reject) => { - if (globalConfig) { - resolve(globalConfig); - return; - } - - let configPath; - let globalConfigFileName = path.join('config', configFileName); - if (isDevEnv) { - // for dev env, get config file from asar - configPath = path.join(app.getAppPath(), globalConfigFileName); - } else { - // for non-dev, config file is placed by installer relative to exe. - // this is so the config can be easily be changed post install. - let execPath = path.dirname(app.getPath('exe')); - // for mac exec is stored in subdir, for linux/windows config - // dir is in the same location. - configPath = path.join(execPath, isMac ? '..' : '', globalConfigFileName); - } - - fs.readFile(configPath, 'utf8', (err, data) => { - if (err) { - reject(new Error('cannot open global config file: ' + configPath + ', error: ' + err)); - } else { - try { - // data is the contents of the text file we just read - globalConfig = JSON.parse(data); - } catch (e) { - reject(new Error('can not parse config file data: ' + data + ', error: ' + err)); - } - getRegistry('PodUrl') - .then((url) => { - globalConfig.url = url; - resolve(globalConfig); - }).catch(() => { - resolve(globalConfig); - }); - } - }); - }); -} - -/** - * Updates user config with given field with new value - * @param {String} fieldName Name of field in config to be added/changed. - * @param {Object} newValue Object to replace given value - * @return {Promise} Promise that resolves/rejects when file write is complete. - */ -function updateConfigField(fieldName, newValue) { - return readUserConfig() - .then((config) => { - return saveUserConfig(fieldName, newValue, config); - }, () => { - // in case config doesn't exist, can't read or is corrupted. - // add configBuildNumber - just in case in future we need to provide - // upgrade capabilities. - return saveUserConfig(fieldName, newValue, { - configBuildNumber: buildNumber || '0', - }); - }); -} - -/** - * Saves an updated value to the user config - * @param fieldName - * @param newValue - * @param oldConfig - * @returns {Promise} - */ -function saveUserConfig(fieldName, newValue, oldConfig) { - return new Promise((resolve, reject) => { - let configPath = path.join(app.getPath('userData'), configFileName); - - if (!oldConfig || !fieldName) { - reject(new Error('can not save config, invalid input')); - return; - } - - // clone and set new value - let newConfig = Object.assign({}, oldConfig); - newConfig[fieldName] = newValue; - - let jsonNewConfig = JSON.stringify(newConfig, null, ' '); - - fs.writeFile(configPath, jsonNewConfig, 'utf8', (err) => { - if (err) { - reject(err); - } else { - userConfig = newConfig; - resolve(newConfig); - } - }); - }); -} - -/** - * Updates the existing user config settings by removing - * 'minimizeOnClose', 'launchOnStartup', 'url' and 'alwaysOnTop' - * @param {Object} oldUserConfig the old user config object - */ -function updateUserConfig(oldUserConfig) { - - return new Promise((resolve, reject) => { - - // create a new object from the old user config - // by ommitting the user related settings from - // the old user config - log.send(logLevels.INFO, `old user config string ${JSON.stringify(oldUserConfig)}`); - let newUserConfig = omit(oldUserConfig, ignoreSettings); - let newUserConfigString = JSON.stringify(newUserConfig, null, 2); - - log.send(logLevels.INFO, `new config string ${newUserConfigString}`); - - // get the user config path - let userConfigFile; - userConfigFile = path.join(app.getPath('userData'), configFileName); - - if (!userConfigFile) { - reject(new Error(`user config file doesn't exist`)); - return; - } - - // write the new user config changes to the user config file - fs.writeFile(userConfigFile, newUserConfigString, 'utf-8', (err) => { - if (err) { - reject(new Error(`Failed to update user config error: ${err}`)); - return; - } - userConfig = newUserConfig; - resolve(); - }); - }); - -} - -/** - * Manipulates user config on first time launch - * @returns {Promise} - */ -function updateUserConfigOnLaunch(resolve, reject) { - // we get the user config path using electron - const userConfigFile = path.join(app.getPath('userData'), configFileName); - - // In case the file exists, we remove it so that all the - // values are fetched from the global config - // https://perzoinc.atlassian.net/browse/ELECTRON-126 - return readUserConfig(userConfigFile).then((data) => { - // Add build number info to the user config data - const updatedData = Object.assign(data || {}, { configBuildNumber: buildNumber || '0' }); - - updateUserConfig(updatedData) - .then(resolve) - .catch(reject); - }).catch((err) => { - return reject(err); - }); - -} - -/** - * Method that tries to grab multiple config field from user config - * if field doesn't exist tries reading from global config - * - * @param {Array} fieldNames - array of config filed names - * @returns {Promise} - object all the config data from user and global config - */ -function getMultipleConfigField(fieldNames) { - return new Promise((resolve, reject) => { - let userConfigData; - - if (!fieldNames && fieldNames.length < 0) { - reject(new Error('cannot read config file, invalid fields')); - return; - } - - // reads user config data - readUserConfig().then((config) => { - userConfigData = pick(config, fieldNames); - let userConfigKeys = userConfigData ? Object.keys(userConfigData) : undefined; - - /** - * Condition to validate data from user config, - * if all the required fields are not present - * this tries to fetch the remaining fields from global config - */ - if (!userConfigKeys || userConfigKeys.length < fieldNames.length) { - - // remainingConfig - config field that are not present in the user config - let remainingConfig = difference(fieldNames, userConfigKeys); - - if (remainingConfig && Object.keys(remainingConfig).length > 0) { - readGlobalConfig().then((globalConfigData) => { - // assigns the remaining fields from global config to the user config - userConfigData = Object.assign(userConfigData, pick(globalConfigData, remainingConfig)); - resolve(userConfigData); - }).catch((err) => { - reject(err); - }); - } - - } else { - resolve(userConfigData); - } - }).catch(() => { - // This reads global config if there was any - // error while reading user config - readGlobalConfig().then((config) => { - userConfigData = pick(config, fieldNames); - resolve(userConfigData); - }).catch((err) => { - reject(err); - }); - }); - }); -} - - -/** - * Clears the cached config - */ -function clearCachedConfigs() { - userConfig = null; - globalConfig = null; -} - -function readConfigFileSync() { - - let configPath; - let globalConfigFileName = path.join('config', configFileName); - if (isDevEnv) { - // for dev env, get config file from asar - configPath = path.join(app.getAppPath(), globalConfigFileName); - } else { - // for non-dev, config file is placed by installer relative to exe. - // this is so the config can be easily be changed post install. - let execPath = path.dirname(app.getPath('exe')); - // for mac exec is stored in subdir, for linux/windows config - // dir is in the same location. - configPath = path.join(execPath, isMac ? '..' : '', globalConfigFileName); - } - - let data = fs.readFileSync(configPath); - - try { - return JSON.parse(data); - } catch (err) { - log.send(logLevels.ERROR, 'config: parsing config failed: ' + err); - } - - return null; - -} - -const readConfigFromFile = (key) => { - let data = readConfigFileSync(); - return data[key]; -} - -module.exports = { - - configFileName, - - getConfigField, - - updateConfigField, - updateUserConfigOnLaunch, - - getMultipleConfigField, - - // items below here are only exported for testing, do NOT use! - saveUserConfig, - clearCachedConfigs, - - readConfigFileSync, - readConfigFromFile, - - // use only if you specifically need to read global config fields - getGlobalConfigField, - getUserConfigField -}; diff --git a/js/crashReporter.js b/js/crashReporter.js deleted file mode 100644 index d1147ff6..00000000 --- a/js/crashReporter.js +++ /dev/null @@ -1,85 +0,0 @@ -const { crashReporter } = require('electron'); -const { getMultipleConfigField } = require('./config.js'); - -const log = require('./log.js'); -const logLevels = require('./enums/logLevels.js'); - -const configFields = ['url', 'crashReporter']; -let crashReporterData; - -/** - * Method that returns all the required field for crash reporter - * - * @param extras {object} - * @return {Promise} - */ -function getCrashReporterConfig(extras) { - return new Promise((resolve, reject) => { - - if (crashReporterData && crashReporterData.companyName) { - crashReporterData.extra = Object.assign(crashReporterData.extra, extras); - resolve(crashReporterData); - return; - } - - getMultipleConfigField(configFields) - .then((data) => { - - if (!data && !data.crashReporter && !data.crashReporter.companyName) { - reject(new Error('Company name cannot be empty')); - return; - } - - crashReporterData = { - companyName: data.crashReporter.companyName, - submitURL: data.crashReporter.submitURL, - uploadToServer: data.crashReporter.uploadToServer, - extra: Object.assign( - {podUrl: data.url}, - extras - ) - }; - resolve(crashReporterData); - }) - .catch((err) => log.send( - logLevels.ERROR, - 'Unable to initialize crash reporter failed to read config file. Error is -> ' + err - )); - }) -} - -function initCrashReporterMain(extras) { - getCrashReporterConfig(extras).then((mainCrashReporterData) => { - try { - crashReporter.start(mainCrashReporterData); - } catch (err) { - log.send(logLevels.ERROR, 'Failed to start crash reporter main process. Error is -> ' + err); - } - }).catch((err) => log.send( - logLevels.ERROR, - 'Unable to initialize crash reporter for main process. Error is -> ' + err - )); -} - - -/** - * Method to initialize crash reporter for renderer process - * - * @param browserWindow {Electron.BrowserWindow} - * @param extras {Object} - */ -function initCrashReporterRenderer(browserWindow, extras) { - if (browserWindow && browserWindow.webContents && !browserWindow.isDestroyed()) { - getCrashReporterConfig(extras).then((rendererCrashReporterData) => { - browserWindow.webContents.send('register-crash-reporter', rendererCrashReporterData); - }).catch((err) => log.send( - logLevels.ERROR, - 'Unable to initialize crash reporter for renderer process. Error is -> ' + err - )); - } -} - -module.exports = { - initCrashReporterMain, - initCrashReporterRenderer, -}; \ No newline at end of file diff --git a/js/cryptoLib.js b/js/cryptoLib.js deleted file mode 100644 index 6cc9f673..00000000 --- a/js/cryptoLib.js +++ /dev/null @@ -1,132 +0,0 @@ -const electron = require('electron'); -const app = electron.app; -const ffi = require('ffi'); -const ref = require('ref'); -const path = require('path'); -const execPath = path.dirname(app.getPath('exe')); - -const log = require('./log.js'); -const logLevels = require('./enums/logLevels.js'); -const { isMac, isDevEnv } = require('../js/utils/misc'); - -const TAG_LENGTH = 16; -const arch = process.arch === 'ia32'; -const winLibraryPath = isDevEnv ? path.join(__dirname, '..', 'library') : path.join(execPath, 'library'); -const macLibraryPath = isDevEnv ? path.join(__dirname, '..', 'library') : path.join(execPath, '..', 'library'); - -const cryptoLibPath = isMac ? - path.join(macLibraryPath, 'cryptoLib.dylib') : - (arch ? path.join(winLibraryPath, 'libsymphonysearch-x86.dll') : path.join(winLibraryPath, 'libsymphonysearch-x64.dll')); - -const library = new ffi.Library((cryptoLibPath), { - - AESEncryptGCM: [ref.types.int32, [ - ref.refType(ref.types.uchar), - ref.types.int32, - ref.refType(ref.types.uchar), - ref.types.int32, - ref.refType(ref.types.uchar), - ref.refType(ref.types.uchar), - ref.types.uint32, - ref.refType(ref.types.uchar), - ref.refType(ref.types.uchar), - ref.types.uint32, - ]], - - AESDecryptGCM: [ref.types.int32, [ - ref.refType(ref.types.uchar), - ref.types.int32, - ref.refType(ref.types.uchar), - ref.types.int32, - ref.refType(ref.types.uchar), - ref.types.uint32, - ref.refType(ref.types.uchar), - ref.refType(ref.types.uchar), - ref.types.uint32, - ref.refType(ref.types.uchar), - ]], - - getVersion: [ref.types.CString, []], -}); - -/** - * Method to decrypt content - * @param Base64IV - * @param Base64AAD - * @param Base64Key - * @param Base64In - * @return {*} - * @constructor - */ -const AESGCMEncrypt = function (Base64IV, Base64AAD, Base64Key, Base64In) { - return EncryptDecrypt('AESGCMEncrypt', Base64IV, Base64AAD, Base64Key, Base64In); -}; - -/** - * Method to decrypt content - * @param Base64IV - * @param Base64AAD - * @param Base64Key - * @param Base64In - * @return {*} - * @constructor - */ -const AESGCMDecrypt = function (Base64IV, Base64AAD, Base64Key, Base64In) { - return EncryptDecrypt('AESGCMDecrypt', Base64IV, Base64AAD, Base64Key, Base64In); -}; - -/** - * Encrypt / Decrypt - * @param name {String} - Method name - * @param Base64IV {String} base64 - * @param Base64AAD {String} base64 - * @param Base64Key {String} base64 - * @param Base64In {String} base64 - * @return {*} - * @constructor - */ -const EncryptDecrypt = function (name, Base64IV, Base64AAD, Base64Key, Base64In) { - let base64In = Base64In; - - if (!base64In) { - base64In = ""; - } - - const IV = Buffer.from(Base64IV, 'base64'); - const AAD = Buffer.from(Base64AAD, 'base64'); - const Key = Buffer.from(Base64Key, 'base64'); - const In = Buffer.from(base64In, 'base64'); - - if (name === 'AESGCMEncrypt') { - const OutPtr = Buffer.alloc(In.length); - const Tag = Buffer.alloc(TAG_LENGTH); - - const resultCode = library.AESEncryptGCM(In, In.length, AAD, AAD.length, Key, IV, IV.length, OutPtr, Tag, TAG_LENGTH); - - if (resultCode < 0) { - log.send(logLevels.ERROR, `AESEncryptGCM, Failed to encrypt with exit code ${resultCode}`); - } - const bufferArray = [OutPtr, Tag]; - return Buffer.concat(bufferArray).toString('base64'); - } - - if (name === 'AESGCMDecrypt') { - const CipherTextLen = In.length - TAG_LENGTH; - const Tag = Buffer.from(In.slice(In.length - 16, In.length)); - const OutPtr = Buffer.alloc(In.length - TAG_LENGTH); - - const resultCode = library.AESDecryptGCM(In, CipherTextLen, AAD, AAD.length, Tag, TAG_LENGTH, Key, IV, IV.length, OutPtr); - - if (resultCode < 0) { - log.send(logLevels.ERROR, `AESDecryptGCM, Failed to decrypt with exit code ${resultCode}`); - } - return OutPtr.toString('base64'); - } - - return null; -}; - -module.exports = { - AESGCMEncrypt: AESGCMEncrypt, - AESGCMDecrypt: AESGCMDecrypt, -}; diff --git a/js/desktopCapturer/getSource.js b/js/desktopCapturer/getSource.js deleted file mode 100644 index 1c43e167..00000000 --- a/js/desktopCapturer/getSource.js +++ /dev/null @@ -1,161 +0,0 @@ -'use strict'; - -// This code provides equivalent of desktopCapturer.getSources that works in -// a sandbox renderer. see: https://electron.atom.io/docs/api/desktop-capturer/ -// -// The code here is not entirely kosher/stable as it is using private ipc -// events. The code was take directly from electron.asar file provided in -// prebuilt node module. Note: the slight difference here is the thumbnail -// returns a base64 encoded image rather than a electron nativeImage. -// -// Until electron provides access to desktopCapturer in a sandboxed -// renderer process, this will have to do. See github issue posted here to -// electron: https://github.com/electron/electron/issues/9312 - -const { ipcRenderer, remote, desktopCapturer } = require('electron'); -const apiEnums = require('../enums/api.js'); -const apiCmds = apiEnums.cmds; -const apiName = apiEnums.apiName; -const { isWindowsOS } = require('../utils/misc'); -const USER_CANCELLED = 'User Cancelled'; - -let nextId = 0; -let includes = [].includes; -let screenShareArgv; -let isScreenShareEnabled = false; -let dialogContent; - -function getNextId() { - return ++nextId; -} - -/** - * Checks if the options and their types are valid - * @param options |options.type| can not be empty and has to include 'window' or 'screen'. - * @returns {boolean} - */ -function isValid(options) { - return ((options !== null ? options.types : undefined) !== null) && Array.isArray(options.types); -} - -/** - * Gets the sources for capturing screens / windows - * @param options - * @param callback - * @returns {*} - */ -function getSource(options, callback) { - let captureScreen, captureWindow, id; - let sourceTypes = []; - if (!isValid(options)) { - callback(new Error('Invalid options')); - return; - } - captureWindow = includes.call(options.types, 'window'); - captureScreen = includes.call(options.types, 'screen'); - - let updatedOptions = options; - if (!updatedOptions.thumbnailSize) { - updatedOptions.thumbnailSize = { - width: 150, - height: 150 - }; - } - - if (isWindowsOS && captureWindow) { - /** - * Sets the captureWindow to false if Desktop composition - * is disabled otherwise true - * - * Setting captureWindow to false returns only screen sources - * @type {boolean} - */ - captureWindow = remote.systemPreferences.isAeroGlassEnabled(); - } - - if (captureWindow) { - sourceTypes.push('window'); - } - if (captureScreen) { - sourceTypes.push('screen'); - } - - // displays a dialog if media permissions are disable - if (!isScreenShareEnabled) { - let focusedWindow = remote.BrowserWindow.getFocusedWindow(); - if (focusedWindow && !focusedWindow.isDestroyed()) { - remote.dialog.showMessageBox(focusedWindow, dialogContent || - { - type: 'error', - title: 'Permission Denied!', - message: 'Your administrator has disabled screen share. Please contact your admin for help' - }); - callback(new Error('Permission Denied')); - return; - } - } - - id = getNextId(); - desktopCapturer.getSources({ types: sourceTypes, thumbnailSize: updatedOptions.thumbnailSize }, (event, sources) => { - - if (screenShareArgv) { - const title = screenShareArgv.substr(screenShareArgv.indexOf('=') + 1); - const filteredSource = sources.filter(source => source.name === title); - - if (Array.isArray(filteredSource) && filteredSource.length > 0) { - return callback(null, filteredSource[0]); - } - - if (typeof filteredSource === 'object' && filteredSource.name) { - return callback(null, filteredSource); - } - - if (sources.length > 0) { - return callback(null, sources[0]); - } - - } - - const updatedSources = sources.map(source => { - return Object.assign({}, source, { - thumbnail: source.thumbnail.toDataURL() - }); - }); - - ipcRenderer.send(apiName, { - cmd: apiCmds.openScreenPickerWindow, - sources: updatedSources, - id: id - }); - - function successCallback(e, source) { - // Cleaning up the event listener to prevent memory leaks - if (!source) { - ipcRenderer.removeListener('start-share' + id, func); - return callback(new Error(USER_CANCELLED)); - } - return callback(null, source); - } - - const func = successCallback.bind(this); - ipcRenderer.once('start-share' + id, func); - return null; - }); -} - -// event that updates screen share argv -ipcRenderer.once('screen-share-argv', (event, arg) => { - if (typeof arg === 'string') { - screenShareArgv = arg; - } -}); - -// event that updates screen share permission -ipcRenderer.on('is-screen-share-enabled', (event, screenShare, content) => { - dialogContent = content; - if (typeof screenShare === 'boolean' && screenShare) { - isScreenShareEnabled = true; - } -}); - -module.exports = getSource; \ No newline at end of file diff --git a/js/desktopCapturer/getSources.js b/js/desktopCapturer/getSources.js deleted file mode 100644 index 9d90c3de..00000000 --- a/js/desktopCapturer/getSources.js +++ /dev/null @@ -1,146 +0,0 @@ -'use strict'; - -// This code provides equivalent of desktopCapturer.getSources that works in -// a sandbox renderer. see: https://electron.atom.io/docs/api/desktop-capturer/ -// -// The code here is not entirely kosher/stable as it is using private ipc -// events. The code was take directly from electron.asar file provided in -// prebuilt node module. Note: the slight difference here is the thumbnail -// returns a base64 encoded image rather than a electron nativeImage. -// -// Until electron provides access to desktopCapturer in a sandboxed -// renderer process, this will have to do. See github issue posted here to -// electron: https://github.com/electron/electron/issues/9312 - -const { remote, desktopCapturer, ipcRenderer } = require('electron'); -const { isWindowsOS } = require('../utils/misc'); - -let includes = [].includes; -let screenShareArgv; -let isScreenShareEnabled = false; -let dialogContent; - -/** - * Checks if the options and their types are valid - * @param options |options.type| can not be empty and has to include 'window' or 'screen'. - * @returns {boolean} - */ -function isValid(options) { - return ((options !== null ? options.types : undefined) !== null) && Array.isArray(options.types); -} - -/** - * Gets the sources for capturing screens / windows - * @param options - * @param callback - * @returns {*} - */ -function getSources(options, callback) { - let captureScreen, captureWindow; - let sourceTypes = []; - if (!isValid(options)) { - callback(new Error('Invalid options')); - return; - } - captureWindow = includes.call(options.types, 'window'); - captureScreen = includes.call(options.types, 'screen'); - - let updatedOptions = options; - if (!updatedOptions.thumbnailSize) { - updatedOptions.thumbnailSize = { - width: 150, - height: 150 - }; - } - - if (isWindowsOS && captureWindow) { - /** - * Sets the captureWindow to false if Desktop composition - * is disabled otherwise true - * - * Setting captureWindow to false returns only screen sources - * @type {boolean} - */ - captureWindow = remote.systemPreferences.isAeroGlassEnabled(); - } - if (captureWindow) { - sourceTypes.push('window'); - } - if (captureScreen) { - sourceTypes.push('screen'); - } - - // displays a dialog if media permissions are disable - if (!isScreenShareEnabled) { - let focusedWindow = remote.BrowserWindow.getFocusedWindow(); - if (focusedWindow && !focusedWindow.isDestroyed()) { - remote.dialog.showMessageBox(focusedWindow, dialogContent || - { - type: 'error', - title: 'Permission Denied!', - message: 'Your administrator has disabled screen share. Please contact your admin for help' - }); - callback(new Error('Permission Denied')); - return; - } - } - - desktopCapturer.getSources({ types: sourceTypes, thumbnailSize: updatedOptions.thumbnailSize }, (event, sources) => { - - if (screenShareArgv) { - const title = screenShareArgv.substr(screenShareArgv.indexOf('=') + 1); - const filteredSource = sources.filter(source => source.name === title); - - if (Array.isArray(filteredSource) && filteredSource.length > 0) { - return callback(null, filteredSource[0]); - } - - if (typeof filteredSource === 'object' && filteredSource.name) { - return callback(null, filteredSource); - } - - if (sources.length > 0) { - return callback(null, sources[0]); - } - - } - - let source; - return callback(null, (function() { - let i, len, results; - results = []; - for (i = 0, len = sources.length; i < len; i++) { - source = sources[i]; - results.push({ - id: source.id, - name: source.name, - thumbnail: source.thumbnail.toDataURL() - }); - } - - return results; - - }())); - }); -} - -// event that updates screen share argv -ipcRenderer.once('screen-share-argv', (event, arg) => { - if (typeof arg === 'string') { - screenShareArgv = arg; - } -}); - -// event that updates screen share permission -ipcRenderer.on('is-screen-share-enabled', (event, screenShare, content) => { - dialogContent = content; - if (typeof screenShare === 'boolean' && screenShare) { - isScreenShareEnabled = true; - } -}); - -/** - * @deprecated instead use getSource - * @type {getSources} - */ -module.exports = getSources; \ No newline at end of file diff --git a/js/desktopCapturer/index.js b/js/desktopCapturer/index.js deleted file mode 100644 index fa4fa516..00000000 --- a/js/desktopCapturer/index.js +++ /dev/null @@ -1,174 +0,0 @@ -'use strict'; - -const electron = require('electron'); -const BrowserWindow = electron.BrowserWindow; -const ipc = electron.ipcMain; -const path = require('path'); -const fs = require('fs'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const { isMac, isWindowsOS } = require('./../utils/misc.js'); -const { initCrashReporterMain, initCrashReporterRenderer } = require('../crashReporter.js'); -const i18n = require('../translation/i18n'); - -let screenPickerWindow; -let preloadWindow; -let eventId; - -let windowConfig = { - width: 580, - height: isMac ? 519 : 523, - show: false, - modal: true, - frame: false, - autoHideMenuBar: true, - resizable: false, - alwaysOnTop: true, - webPreferences: { - preload: path.join(__dirname, 'renderer.js'), - sandbox: true, - nodeIntegration: false, - devTools: false - } -}; - -/** - * method to get the HTML template path - * @returns {string} - */ -function getTemplatePath() { - let templatePath = path.join(__dirname, 'screen-picker.html'); - try { - fs.statSync(templatePath).isFile(); - } catch (err) { - log.send(logLevels.ERROR, 'screen-picker: Could not find template ("' + templatePath + '").'); - } - return 'file://' + templatePath; -} - -/** - * Creates the screen picker window - * @param eventSender {RTCRtpSender} - event sender window object - * @param sources {Array} - list of object which has screens and applications - * @param id {Number} - event emitter id - */ -function openScreenPickerWindow(eventSender, sources, id) { - - // prevent a new window from being opened if there is an - // existing window / there is no event sender - if (!eventSender || screenPickerWindow) { - return; - } - - // Screen picker will always be placed on top of the focused window - const focusedWindow = BrowserWindow.getFocusedWindow(); - - // As screen picker is an independent window this will make sure - // it will open screen picker window center of the focused window - if (focusedWindow) { - const { x, y, width, height } = focusedWindow.getBounds(); - - if (x !== undefined && y !== undefined) { - const windowWidth = Math.round(width * 0.5); - const windowHeight = Math.round(height * 0.5); - - // Calculating the center of the parent window - // to place the configuration window - const centerX = x + width / 2.0; - const centerY = y + height / 2.0; - windowConfig.x = Math.round(centerX - (windowWidth / 2.0)); - windowConfig.y = Math.round(centerY - (windowHeight / 2.0)); - } - - if (isWindowsOS) { - windowConfig.parent = focusedWindow; - } - } - - // Store the window ref to send event - preloadWindow = eventSender; - eventId = id; - - screenPickerWindow = new BrowserWindow(windowConfig); - screenPickerWindow.setVisibleOnAllWorkspaces(true); - screenPickerWindow.loadURL(getTemplatePath()); - - screenPickerWindow.once('ready-to-show', () => { - screenPickerWindow.show(); - }); - - screenPickerWindow.webContents.on('did-finish-load', () => { - const screenPickerContent = i18n.getMessageFor('ScreenPicker'); - screenPickerWindow.webContents.send('i18n-screen-picker', screenPickerContent); - // initialize crash reporter - initCrashReporterMain({ process: 'desktop capture window' }); - initCrashReporterRenderer(screenPickerWindow, { process: 'render | desktop capture window' }); - screenPickerWindow.webContents.send('desktop-capturer-sources', sources, isWindowsOS); - }); - - screenPickerWindow.webContents.on('crashed', function (event, killed) { - - log.send(logLevels.INFO, `Screen Picker Window crashed! Killed? ${killed}`); - - if (killed) { - return; - } - - const options = { - type: 'error', - title: i18n.getMessageFor('Renderer Process Crashed'), - message: i18n.getMessageFor('Oops! Looks like we have had a crash.'), - buttons: ['Close'] - }; - - electron.dialog.showMessageBox(options, function () { - if (screenPickerWindow && !screenPickerWindow.isDestroyed()) { - screenPickerWindow.close(); - } - }); - }); - - screenPickerWindow.on('close', () => { - destroyWindow(); - }); - - screenPickerWindow.on('closed', () => { - destroyWindow(); - }); - -} - -/** - * Destroys a window - */ -function destroyWindow() { - // sending null will clean up the event listener - startScreenShare(null); - screenPickerWindow = null; -} - -/** - * Sends an event to a specific with the selected source - * @param source {Object} - User selected source - */ -function startScreenShare(source) { - if (preloadWindow && !preloadWindow.isDestroyed()) { - preloadWindow.send('start-share' + eventId, source); - } -} - -// Emitted when user has selected a source and press the share button -ipc.on('share-selected-source', (event, source) => { - startScreenShare(source); -}); - -// Emitted when user closes the screen picker window -ipc.on('close-screen-picker', () => { - if (screenPickerWindow && !screenPickerWindow.isDestroyed()) { - screenPickerWindow.close(); - } -}); - -module.exports = { - openScreenPickerWindow -}; diff --git a/js/desktopCapturer/renderer.js b/js/desktopCapturer/renderer.js deleted file mode 100644 index a2e3c789..00000000 --- a/js/desktopCapturer/renderer.js +++ /dev/null @@ -1,319 +0,0 @@ -'use strict'; -const { ipcRenderer, crashReporter } = require('electron'); - -const screenRegExp = new RegExp(/^Screen \d+$/gmi); - -// All the required Keyboard keyCode events -const keyCodeEnum = Object.freeze({ - pageDown: 34, - rightArrow: 39, - pageUp: 33, - leftArrow: 37, - homeKey: 36, - upArrow: 38, - endKey: 35, - arrowDown: 40, - enterKey: 13, - escapeKey: 27 -}); - -let availableSources; -let selectedSource; -let currentIndex = -1; -let localizedContent; - -document.addEventListener('DOMContentLoaded', () => { - renderDom(); -}); - -/** - * Method that renders application data - */ -function renderDom() { - const applicationTab = document.getElementById('application-tab'); - const screenTab = document.getElementById('screen-tab'); - const share = document.getElementById('share'); - const cancel = document.getElementById('cancel'); - const xButton = document.getElementById('x-button'); - - // Event listeners - xButton.addEventListener('click', () => { - closeScreenPickerWindow(); - }, false); - - share.addEventListener('click', () => { - startShare(); - }, false); - - cancel.addEventListener('click', () => { - closeScreenPickerWindow(); - }, false); - - screenTab.addEventListener('click', () => { - updateShareButtonText(localizedContent && localizedContent[ 'Select Screen' ] ? - localizedContent[ 'Select Screen' ] : - 'Select Screen'); - }, false); - - applicationTab.addEventListener('click', () => { - updateShareButtonText(localizedContent && localizedContent[ 'Select Application' ] ? - localizedContent[ 'Select Application' ] : - 'Select Application'); - }, false); - - document.addEventListener('keyup', handleKeyUpPress.bind(this), true); - -} - -/** - * Event emitted by main process with a list of available - * Screens and Applications - */ -ipcRenderer.on('desktop-capturer-sources', (event, sources, isWindowsOS) => { - - if (!Array.isArray(sources) && typeof isWindowsOS !== 'boolean') { - return; - } - availableSources = sources; - - if (isWindowsOS) { - document.body.classList.add('window-border'); - } - - const screenContent = document.getElementById('screen-contents'); - const applicationContent = document.getElementById('application-contents'); - const applicationTab = document.getElementById('applications'); - const screenTab = document.getElementById('screens'); - - let hasScreens = false; - let hasApplications = false; - - for (let source of sources) { - screenRegExp.lastIndex = 0; - if (source.display_id !== "") { - source.fileName = 'fullscreen'; - if (source.name === 'Entire screen') { - const screenName = localizedContent['Entire screen'] || 'Entire screen'; - screenContent.appendChild(createItem(source, screenName)); - } else { - const screenName = localizedContent['Screen {number}'] || 'Screen {number}'; - const screenNumber = source.name.substr(7, source.name.length); - const localisedScreenString = screenName.replace('{number}', screenNumber); - screenContent.appendChild(createItem(source, localisedScreenString)); - } - hasScreens = true; - } else { - source.fileName = null; - applicationContent.appendChild(createItem(source, source.name)); - hasApplications = true; - } - } - - if (!hasScreens && !hasApplications) { - const errorContent = document.getElementById('error-content'); - const mainContent = document.getElementById('main-content'); - - errorContent.style.display = 'block'; - mainContent.style.display = 'none'; - } - - if (hasApplications) { - applicationTab.classList.remove('hidden'); - } - - if (hasScreens) { - screenTab.classList.remove('hidden'); - } -}); - -function startShare() { - if (selectedSource && selectedSource.id) { - ipcRenderer.send('share-selected-source', selectedSource); - closeScreenPickerWindow(); - } -} - -/** - * Creates DOM elements and injects data - * @param source Screen source - * @param name Name of the source - * @returns {HTMLDivElement} - */ -function createItem(source, name) { - const itemContainer = document.createElement("div"); - const sectionBox = document.createElement("div"); - const imageTag = document.createElement("img"); - const titleContainer = document.createElement("div"); - - // Added class names to the dom elements - itemContainer.classList.add('item-container'); - sectionBox.classList.add('screen-section-box'); - imageTag.classList.add('img-wrapper'); - titleContainer.classList.add('screen-source-title'); - - // Inject data to the dom element - imageTag.src = source.thumbnail; - titleContainer.innerText = name; - - sectionBox.appendChild(imageTag); - itemContainer.id = source.id; - itemContainer.appendChild(sectionBox); - itemContainer.appendChild(titleContainer); - - itemContainer.addEventListener('click', updateUI.bind(this, source, itemContainer), false); - - return itemContainer; -} - -/** - * When ever user select a source store it and update the UI - * @param source - * @param itemContainer - */ -function updateUI(source, itemContainer) { - selectedSource = source; - - let shareButton = document.getElementById('share'); - shareButton.className = 'share-button'; - - highlightSelectedSource(); - itemContainer.classList.add('selected'); - shareButton.innerText = localizedContent && localizedContent.Share ? localizedContent.Share : 'Share'; -} - -/** - * Loops through the items and removes - * the selected class property - */ -function highlightSelectedSource() { - let items = document.getElementsByClassName('item-container'); - for (const item of items) { - item.classList.remove('selected'); - } -} - -/** - * Method that updates the share button - * text based on the content type - * @param text - */ -function updateShareButtonText(text) { - let shareButton = document.getElementById('share'); - - if (shareButton && shareButton.classList[0] === 'share-button-disable') { - shareButton.innerText = text; - } -} - -/** - * Method handles used key up event - * @param e - */ -function handleKeyUpPress(e) { - const keyCode = e.keyCode || e.which; - - switch (keyCode) { - case keyCodeEnum.pageDown: - case keyCodeEnum.rightArrow: - updateSelectedSource(1); - break; - case keyCodeEnum.pageUp: - case keyCodeEnum.leftArrow: - updateSelectedSource(-1); - break; - case keyCodeEnum.homeKey: - if (currentIndex !== 0) { - updateSelectedSource(0); - } - break; - case keyCodeEnum.upArrow: - updateSelectedSource(-2); - break; - case keyCodeEnum.endKey: - if (currentIndex !== availableSources.length - 1) { - updateSelectedSource(availableSources.length - 1); - } - break; - case keyCodeEnum.arrowDown: - updateSelectedSource(2); - break; - case keyCodeEnum.enterKey: - startShare(); - break; - case keyCodeEnum.escapeKey: - closeScreenPickerWindow(); - break; - default: - break; - } -} - -/** - * Updates UI based on the key press - * @param index - */ -function updateSelectedSource(index) { - - let selectedElement = document.getElementsByClassName('selected')[0]; - if (selectedElement) { - currentIndex = availableSources.findIndex((source) => { - return source.id === selectedElement.id - }); - } - - // Find the next item to be selected - let nextIndex = (currentIndex + index + availableSources.length) % availableSources.length; - if (availableSources[nextIndex] && availableSources[nextIndex].id) { - let item = document.getElementById(availableSources[nextIndex] ? availableSources[nextIndex].id : ""); - - if (item) { - // Method that stores and update the selected source - updateUI(availableSources[nextIndex], item); - } - } -} - -/** - * Closes the screen picker window - */ -function closeScreenPickerWindow() { - document.removeEventListener('keyUp', handleKeyUpPress.bind(this), true); - ipcRenderer.send('close-screen-picker'); -} - -ipcRenderer.on('register-crash-reporter', (event, arg) => { - if (arg && typeof arg === 'object') { - crashReporter.start(arg); - } -}); - -ipcRenderer.on('i18n-screen-picker', (event, content) => { - localizedContent = content; - if (content && typeof content === 'object') { - const i18nNodes = document.querySelectorAll('[data-i18n-text]'); - - for (let node of i18nNodes) { - if (node.attributes['data-i18n-text'] && node.attributes['data-i18n-text'].value) { - node.innerText = content[node.attributes['data-i18n-text'].value] || node.attributes['data-i18n-text'].value; - } - } - } -}); - - -// note: this is a workaround until -// https://github.com/electron/electron/issues/8841 -// is fixed on the electron. where 'will-navigate' -// is never fired in sandbox mode -// -// This is required in order to prevent from loading -// dropped content -window.addEventListener('drop', function(e) { - e.preventDefault(); - e.stopPropagation(); -}); - -window.addEventListener('dragover', function(e) { - e.preventDefault(); - e.stopPropagation(); -}); \ No newline at end of file diff --git a/js/desktopCapturer/screen-picker.html b/js/desktopCapturer/screen-picker.html deleted file mode 100644 index bbeede3b..00000000 --- a/js/desktopCapturer/screen-picker.html +++ /dev/null @@ -1,246 +0,0 @@ - - - - - - - - -
-
- -
-
- - - - - -
-
-
-
- -
-
- - - - -
-
-
-
-
- -
- - -
-
- - - \ No newline at end of file diff --git a/js/dialogs/showBasicAuth.js b/js/dialogs/showBasicAuth.js deleted file mode 100644 index d91cb105..00000000 --- a/js/dialogs/showBasicAuth.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -const electron = require('electron'); - -const basicAuth = require('../basicAuth'); -let currentAuthURL; -let tries = 0; - -/** - * Having a proxy or hosts that requires authentication will allow user to - * enter their credentials 'username' & 'password' - */ -electron.app.on('login', (event, webContents, request, authInfo, callback) => { - - event.preventDefault(); - - // This check is to determine whether the request is for the same - // host if so then increase the login tries from which we can - // display invalid credentials - if (currentAuthURL !== request.url) { - currentAuthURL = request.url; - } else { - tries++ - } - - // name of the host to display - let hostname = authInfo.host || authInfo.realm; - let browserWin = electron.BrowserWindow.fromWebContents(webContents); - let windowName = browserWin.winName || ''; - - /** - * Method that resets currentAuthURL and tries - * if user closes the auth window - */ - function clearSettings() { - callback(); - currentAuthURL = ''; - tries = 0; - } - - /** - * Opens an electron modal window in which - * user can enter credentials fot the host - */ - basicAuth.openBasicAuthWindow(windowName, hostname, tries === 0, clearSettings, callback); -}); diff --git a/js/dialogs/showCertError.js b/js/dialogs/showCertError.js deleted file mode 100644 index 914cab19..00000000 --- a/js/dialogs/showCertError.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -const electron = require('electron'); - -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const i18n = require('../translation/i18n'); - -let ignoreAllCertErrors = false; - -/** - * If certificate error occurs allow user to deny or allow particular certificate - * error. If user selects 'Ignore All', then all subsequent certificate errors - * will ignored during this session. - * - * Note: the dialog is synchronous so further processing is blocked until - * user provides a response. - */ -electron.app.on('certificate-error', function(event, webContents, url, error, - certificate, callback) { - - if (ignoreAllCertErrors) { - event.preventDefault(); - callback(true); - return; - } - - log.send(logLevels.WARNING, 'Certificate error: ' + error + ' for url: ' + url); - - const browserWin = electron.BrowserWindow.fromWebContents(webContents); - const buttonId = electron.dialog.showMessageBox(browserWin, { - type: 'warning', - buttons: [ 'Allow', 'Deny', 'Ignore All' ], - defaultId: 1, - cancelId: 1, - noLink: true, - title: i18n.getMessageFor('Certificate Error'), - message: i18n.getMessageFor('Certificate Error') + `: ${error}\nURL: ${url}`, - }); - - event.preventDefault(); - - if (buttonId === 2) { - ignoreAllCertErrors = true; - } - - callback(buttonId !== 1); -}); diff --git a/js/dialogs/showLoadError.js b/js/dialogs/showLoadError.js deleted file mode 100644 index 0fede3ad..00000000 --- a/js/dialogs/showLoadError.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict'; - -const electron = require('electron'); - -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const i18n = require('../translation/i18n'); - -/** - * Show dialog pinned to given window when loading error occurs - * @param {BrowserWindow} win Window to host dialog - * @param {String} url Url that failed - * @param {String} errorDesc Description of error - * @param {Number} errorCode Error code - * @param {function} retryCallback Callback when user clicks reload - * @param {Boolean} showDialog Indicates if a dialog need to be show to a user - */ -function showLoadFailure(win, url, errorDesc, errorCode, retryCallback, showDialog) { - let msg; - if (url) { - msg = i18n.getMessageFor('Error loading URL') + `:\n${url}`; - } else { - msg = i18n.getMessageFor('Error loading window'); - } - if (errorDesc) { - msg += '\n\n' + errorDesc; - } - if (errorCode) { - msg += '\n\nError Code: ' + errorCode; - } - - if (showDialog) { - electron.dialog.showMessageBox(win, { - type: 'error', - buttons: [i18n.getMessageFor('Reload'), i18n.getMessageFor('Ignore')], - defaultId: 0, - cancelId: 1, - noLink: true, - title: i18n.getMessageFor('Loading Error'), - message: msg - }, response); - } - - log.send(logLevels.WARNING, 'Load failure msg: ' + errorDesc + - ' errorCode: ' + errorCode + ' for url:' + url); - - // async handle of user input - function response(buttonId) { - // retry if hitting butotn index 0 (i.e., reload) - if (buttonId === 0 && typeof retryCallback === 'function') { - retryCallback(); - } - } -} - -/** - * Show message indicating network connectivity has been lost. - * @param {BrowserWindow} win Window to host dialog - * @param {String} url Url that failed - * @param {function} retryCallback Callback when user clicks reload - */ -function showNetworkConnectivityError(win, url, retryCallback) { - let errorDesc = i18n.getMessageFor('Network connectivity has been lost. Check your internet connection.'); - showLoadFailure(win, url, errorDesc, 0, retryCallback, true); -} - -module.exports = { showLoadFailure, showNetworkConnectivityError }; \ No newline at end of file diff --git a/js/downloadManager/index.js b/js/downloadManager/index.js deleted file mode 100644 index 1f2b32f8..00000000 --- a/js/downloadManager/index.js +++ /dev/null @@ -1,253 +0,0 @@ -'use strict'; - -const { ipcRenderer, remote } = require('electron'); - -const local = { - ipcRenderer: ipcRenderer, - downloadItems: [] -}; - -let showInFolderText = 'Show in Folder'; -let openText = 'Open'; -let downloadedText = 'Downloaded'; -let fileNotFoundTitle = 'File not Found'; -let fileNotFoundMessage = 'The file you are trying to open cannot be found in the specified path.'; - -// listen for file download complete event -local.ipcRenderer.on('downloadCompleted', (event, arg) => { - createDOM(arg); -}); - -// listen for file download progress event -local.ipcRenderer.on('downloadProgress', () => { - initiate(); -}); - -// listen for locale change and update -local.ipcRenderer.on('locale-changed', (event, data) => { - - if (data && typeof data === 'object') { - - if (data.downloadManager) { - openText = data.downloadManager.Open; - downloadedText = data.downloadManager.Downloaded; - - showInFolderText = data.downloadManager['Show in Folder']; - fileNotFoundTitle = data.downloadManager['File not Found']; - fileNotFoundMessage = data.downloadManager['The file you are trying to open cannot be found in the specified path.']; - } - - } - -}); - -/** - * Open file in default app. - * @param id - */ -function openFile(id) { - let fileIndex = local.downloadItems.findIndex((item) => { - return item._id === id; - }); - if (fileIndex !== -1) { - let openResponse = remote.shell.openExternal(`file:///${local.downloadItems[fileIndex].savedPath}`); - let focusedWindow = remote.BrowserWindow.getFocusedWindow(); - if (!openResponse && focusedWindow && !focusedWindow.isDestroyed()) { - remote.dialog.showMessageBox(focusedWindow, {type: 'error', title: fileNotFoundTitle, message: fileNotFoundMessage}); - } - } -} - -/** - * Show downloaded file in explorer or finder. - * @param id - */ -function showInFinder(id) { - let showFileIndex = local.downloadItems.findIndex((item) => { - return item._id === id; - }); - if (showFileIndex !== -1) { - let showResponse = remote.shell.showItemInFolder(local.downloadItems[showFileIndex].savedPath); - let focusedWindow = remote.BrowserWindow.getFocusedWindow(); - if (!showResponse && focusedWindow && !focusedWindow.isDestroyed()) { - remote.dialog.showMessageBox(focusedWindow, {type: 'error', title: fileNotFoundTitle, message: fileNotFoundMessage}); - } - } -} - -/** - * Create the document object model - * @param arg - */ -function createDOM(arg) { - - if (arg && arg._id) { - let fileDisplayName = getFileDisplayName(arg.fileName); - let downloadItemKey = arg._id; - - local.downloadItems.push(arg); - - let ul = document.getElementById('download-main'); - if (ul) { - let li = document.createElement('li'); - li.id = downloadItemKey; - li.classList.add('download-element'); - ul.insertBefore(li, ul.childNodes[0]); - - let itemDiv = document.createElement('div'); - itemDiv.classList.add('download-item'); - itemDiv.id = 'dl-item'; - li.appendChild(itemDiv); - let openMainFile = document.getElementById('dl-item'); - openMainFile.addEventListener('click', () => { - let id = openMainFile.parentNode.id; - openFile(id); - }); - - let fileDetails = document.createElement('div'); - fileDetails.classList.add('file'); - itemDiv.appendChild(fileDetails); - - let downProgress = document.createElement('div'); - downProgress.id = 'download-progress'; - downProgress.classList.add('download-complete'); - downProgress.classList.add('flash'); - setTimeout(() => { - downProgress.classList.remove('flash'); - }, 4000); - fileDetails.appendChild(downProgress); - - let fileIcon = document.createElement('span'); - fileIcon.classList.add('tempo-icon'); - fileIcon.classList.add('tempo-icon--download'); - fileIcon.classList.add('download-complete-color'); - setTimeout(() => { - fileIcon.classList.remove('download-complete-color'); - fileIcon.classList.remove('tempo-icon--download'); - fileIcon.classList.add('tempo-icon--document'); - }, 4000); - downProgress.appendChild(fileIcon); - - let fileNameDiv = document.createElement('div'); - fileNameDiv.classList.add('downloaded-filename'); - itemDiv.appendChild(fileNameDiv); - - let h2FileName = document.createElement('h2'); - h2FileName.classList.add('text-cutoff'); - h2FileName.innerHTML = fileDisplayName; - h2FileName.title = fileDisplayName; - fileNameDiv.appendChild(h2FileName); - - let fileProgressTitle = document.createElement('span'); - fileProgressTitle.id = 'per'; - fileProgressTitle.innerHTML = `${arg.total} ${downloadedText}`; - fileNameDiv.appendChild(fileProgressTitle); - - let caret = document.createElement('div'); - caret.id = 'menu'; - caret.classList.add('caret'); - caret.classList.add('tempo-icon'); - caret.classList.add('tempo-icon--dropdown'); - li.appendChild(caret); - - let actionMenu = document.createElement('div'); - actionMenu.id = 'download-action-menu'; - actionMenu.classList.add('download-action-menu'); - caret.appendChild(actionMenu); - - let caretUL = document.createElement('ul'); - caretUL.id = downloadItemKey; - actionMenu.appendChild(caretUL); - - let caretLiOpen = document.createElement('li'); - caretLiOpen.id = 'download-open'; - caretLiOpen.innerHTML = openText; - caretUL.appendChild(caretLiOpen); - let openFileDocument = document.getElementById('download-open'); - openFileDocument.addEventListener('click', () => { - let id = openFileDocument.parentNode.id; - openFile(id); - }); - - let caretLiShow = document.createElement('li'); - caretLiShow.id = 'download-show-in-folder'; - caretLiShow.innerHTML = showInFolderText; - caretUL.appendChild(caretLiShow); - let showInFinderDocument = document.getElementById('download-show-in-folder'); - showInFinderDocument.addEventListener('click', () => { - let id = showInFinderDocument.parentNode.id; - showInFinder(id); - }); - } - } -} - -/** - * Initiate the download manager - */ -function initiate() { - let mainFooter = document.getElementById('footer'); - let mainDownloadDiv = document.getElementById('download-manager-footer'); - - if (mainDownloadDiv) { - - mainFooter.classList.remove('hidden'); - - let ulFind = document.getElementById('download-main'); - - if (!ulFind) { - let uList = document.createElement('ul'); - uList.id = 'download-main'; - mainDownloadDiv.appendChild(uList); - } - - let closeSpanFind = document.getElementById('close-download-bar'); - - if (!closeSpanFind) { - let closeSpan = document.createElement('span'); - closeSpan.id = 'close-download-bar'; - closeSpan.classList.add('close-download-bar'); - closeSpan.classList.add('tempo-icon'); - closeSpan.classList.add('tempo-icon--close'); - mainDownloadDiv.appendChild(closeSpan); - } - - let closeDownloadManager = document.getElementById('close-download-bar'); - if (closeDownloadManager) { - closeDownloadManager.addEventListener('click', () => { - local.downloadItems = []; - document.getElementById('footer').classList.add('hidden'); - document.getElementById('download-main').innerHTML = ''; - }); - } - } -} - -/** - * Return a file display name for the download item - */ -function getFileDisplayName(fileName) { - let fileList = local.downloadItems; - let fileNameCount = 0; - let fileDisplayName = fileName; - - /* Check if a file with the same name exists - * (akin to the user downloading a file with the same name again) - * in the download bar - */ - for (let i = 0; i < fileList.length; i++) { - if (fileName === fileList[i].fileName) { - fileNameCount++; - } - } - - /* If it exists, add a count to the name like how Chrome does */ - if (fileNameCount) { - let extLastIndex = fileDisplayName.lastIndexOf('.'); - let fileCount = ' (' + fileNameCount + ')'; - - fileDisplayName = fileDisplayName.slice(0, extLastIndex) + fileCount + fileDisplayName.slice(extLastIndex); - } - - return fileDisplayName; -} \ No newline at end of file diff --git a/js/enums/api.js b/js/enums/api.js deleted file mode 100644 index 0376da2f..00000000 --- a/js/enums/api.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -let keyMirror = require('keymirror'); - -/** - * Set of APIs exposed to the remote object - * @type {Object} - */ -const cmds = keyMirror({ - isOnline: null, - registerLogger: null, - setBadgeCount: null, - badgeDataUrl: null, - activate: null, - registerBoundsChange: null, - registerProtocolHandler: null, - registerActivityDetection: null, - showNotificationSettings: null, - sanitize: null, - bringToFront: null, - openScreenPickerWindow: null, - popupMenu: null, - optimizeMemoryConsumption: null, - optimizeMemoryRegister: null, - setIsInMeeting: null, - setLocale: null, - keyPress: null, - openScreenSharingIndicator: null, - cancelNetworkStatusCheck: null, - quitWindow: null, -}); - -module.exports = { - cmds: cmds, - apiName: 'symphony-api' -}; diff --git a/js/enums/logLevels.js b/js/enums/logLevels.js deleted file mode 100644 index 087d538e..00000000 --- a/js/enums/logLevels.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -let keyMirror = require('keymirror'); - -/** - * The different log levels - * @type {Object} - */ -module.exports = keyMirror({ - ERROR: null, - CONFLICT: null, - WARN: null, - ACTION: null, - INFO: null, - DEBUG: null -}); diff --git a/js/enums/titleBarStyles.js b/js/enums/titleBarStyles.js deleted file mode 100644 index a1c49ff3..00000000 --- a/js/enums/titleBarStyles.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -let keyMirror = require('keymirror'); - -/** - * The different title bar styles - * @type {Object} - */ -module.exports = keyMirror({ - CUSTOM: null, - NATIVE: null, -}); diff --git a/js/eventEmitter.js b/js/eventEmitter.js deleted file mode 100644 index 3e34f3d5..00000000 --- a/js/eventEmitter.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -const EventEmitter = require('events').EventEmitter; -const eventEmitter = new EventEmitter(); - -// These method should only be used in main process -module.exports = { - emit: eventEmitter.emit, - on: eventEmitter.on -}; \ No newline at end of file diff --git a/js/log.js b/js/log.js deleted file mode 100644 index e1e1f382..00000000 --- a/js/log.js +++ /dev/null @@ -1,168 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const util = require('util'); - -const { app } = require('electron'); -const path = require('path'); -const getCmdLineArg = require('./utils/getCmdLineArg.js'); -const logLevels = require('./enums/logLevels.js'); - -const MAX_LOG_QUEUE_LENGTH = 100; - -let electronLog; - -class Logger { - - constructor() { - // browser window that has registered a logger - this.logWindow = null; - - // holds log messages received before logger has been registered. - this.logQueue = []; - - // Initializes the local logger - if (!process.env.ELECTRON_QA) { - initializeLocalLogger(); - } - } - - /** - * Send log messages from main process to logger hosted by - * renderer process. Allows main process to use logger - * provided by JS. - * @param {enum} level enum from ./enums/LogLevel.js - * @param {string} details msg to be logged - */ - send(level, details) { - if (!level || !details) { - return; - } - - if (!process.env.ELECTRON_QA) { - logLocally(level, details); - } - - let logMsg = { - level: level, - details: details, - startTime: Date.now() - }; - - if (this.logWindow) { - this.logWindow.send('log', { - msgs: [ logMsg ] - }); - } else { - // store log msgs for later when (if) we get logger registered - this.logQueue.push(logMsg); - // don't store more than 100 msgs. keep most recent log msgs. - if (this.logQueue.length > MAX_LOG_QUEUE_LENGTH) { - this.logQueue.shift(); - } - } - } - - /** - * Sets a window instance for the remote object - * @param win - */ - setLogWindow(win) { - this.logWindow = win; - - if (this.logWindow) { - let logMsg = {}; - - if (Array.isArray(this.logQueue)) { - logMsg.msgs = this.logQueue; - } - - // configure desired log level and send pending log msgs - let logLevel = getCmdLineArg(process.argv, '--logLevel=', false); - if (logLevel) { - let level = logLevel.split('=')[1]; - if (level) { - logMsg.logLevel = level; - } - } - - if (getCmdLineArg(process.argv, '--enableConsoleLogging', false)) { - logMsg.showInConsole = true; - } - - if (Object.keys(logMsg).length) { - this.logWindow.send('log', logMsg); - } - - this.logQueue = []; - } - } -} - -let loggerInstance = new Logger(); - -/** - * Initializes the electron logger for local logging - */ -function initializeLocalLogger() { - - // If the user has specified a custom log path use it. - let customLogPathArg = getCmdLineArg(process.argv, '--logPath=', false); - let customLogsFolder = customLogPathArg && customLogPathArg.substring(customLogPathArg.indexOf('=') + 1); - - if (customLogsFolder && fs.existsSync(customLogsFolder)) { - app.setPath('logs', customLogsFolder); - } - // eslint-disable-next-line global-require - electronLog = require('electron-log'); - const logPath = app.getPath('logs'); - cleanupOldLogs(logPath); - electronLog.transports.file.file = path.join(logPath, `app_${Date.now()}.log`); - electronLog.transports.file.level = 'debug'; - electronLog.transports.file.format = '{y}-{m}-{d} {h}:{i}:{s}:{ms} {z} | {level} | {text}'; - electronLog.transports.file.appName = 'Symphony'; -} - -/** - * Cleans up old log files in the given path - * @param {String} logPath Path of the log files - */ -function cleanupOldLogs(logPath) { - let files = fs.readdirSync(logPath); - const deleteTimeStamp = new Date().getTime() + (10 * 24 * 60 * 60 * 1000); - files.forEach((file) => { - if (file === '.DS_Store' || file === 'app.log') { - return; - } - const filePath = path.join(logPath, file); - const stat = fs.statSync(filePath); - const fileTimestamp = new Date(util.inspect(stat.mtime)).getTime(); - if (fileTimestamp > deleteTimeStamp) { - fs.unlinkSync(filePath); - } - }); -} - -/** - * Logs locally using the electron-logger - * @param level - * @param message - */ -function logLocally(level, message) { - switch (level) { - case logLevels.ERROR: electronLog.error(message); break; - case logLevels.CONFLICT: electronLog.error(message); break; - case logLevels.WARN: electronLog.warn(message); break; - case logLevels.ACTION: electronLog.warn(message); break; - case logLevels.INFO: electronLog.info(message); break; - case logLevels.DEBUG: electronLog.debug(message); break; - default: electronLog.debug(message); - } -} - -// Logger class is only exposed for testing purposes. -module.exports = { - Logger: Logger, - send: loggerInstance.send.bind(loggerInstance), - setLogWindow: loggerInstance.setLogWindow.bind(loggerInstance) -}; diff --git a/js/main.js b/js/main.js deleted file mode 100644 index e2d9d930..00000000 --- a/js/main.js +++ /dev/null @@ -1,479 +0,0 @@ -'use strict'; - -// Third Party Dependencies -const electron = require('electron'); -const app = electron.app; -const crashReporter = electron.crashReporter; -const nodeURL = require('url'); -const shellPath = require('shell-path'); -const urlParser = require('url'); -const nodePath = require('path'); -require('electron-dl')(); - -const { version, clientVersion, buildNumber } = require('../package.json'); -const log = require('./log.js'); -const logLevels = require('./enums/logLevels.js'); - -log.send(logLevels.INFO, `-----------------Starting the app with version ${version}-----------------`); - -// Local Dependencies -require('./stats'); -const { getConfigField, readConfigFileSync, updateUserConfigOnLaunch, getUserConfigField } = require('./config.js'); -const protocolHandler = require('./protocolHandler'); -const { setCheckboxValues } = require('./menus/menuTemplate.js'); -const autoLaunch = require('./autoLaunch'); -const { handleCacheFailureCheckOnStartup, handleCacheFailureCheckOnExit} = require('./cacheHandler'); - -const { isMac, isDevEnv } = require('./utils/misc.js'); -const getCmdLineArg = require('./utils/getCmdLineArg.js'); - -//setting the env path child_process issue https://github.com/electron/electron/issues/7688 -shellPath() - .then((path) => { - process.env.PATH = path - }) - .catch(() => { - process.env.PATH = [ - './node_modules/.bin', - '/usr/local/bin', - process.env.PATH - ].join(':'); - }); - -// used to check if a url was opened when the app was already open -let isAppAlreadyOpen = false; - -require('./mainApiMgr.js'); - -// monitor memory of main process -require('./memoryMonitor.js'); - -// adds 'symphony' as a protocol in the system. -// plist file in macOS and registry entries on windows -app.setAsDefaultProtocolClient('symphony'); - -const windowMgr = require('./windowMgr.js'); -const i18n = require('./translation/i18n'); -let ContextMenuBuilder; - -getConfigField('url') - .then(initializeCrashReporter) - .catch(app.quit); - -function initializeCrashReporter(podUrl) { - - getConfigField('crashReporter') - .then((crashReporterConfig) => { - crashReporter.start({ companyName: crashReporterConfig.companyName, submitURL: crashReporterConfig.submitURL, uploadToServer: crashReporterConfig.uploadToServer, extra: { 'process': 'main', podUrl: podUrl } }); - log.send(logLevels.INFO, 'initialized crash reporter on the main process!'); - }) - .catch((err) => { - log.send(logLevels.ERROR, 'Unable to initialize crash reporter in the main process. Error is -> ' + err); - }); - -} - -const allowMultiInstance = getCmdLineArg(process.argv, '--multiInstance', true) || isDevEnv; - -if (!allowMultiInstance) { - const gotTheLock = app.requestSingleInstanceLock(); - - // quit if another instance is already running, ignore for dev env or if app was started with multiInstance flag - if (!gotTheLock) { - app.quit() - } else { - app.on('second-instance', (event, argv) => { - // Someone tried to run a second instance, we should focus our window. - log.send(logLevels.INFO, `Second instance created with args ${argv}`); - let mainWin = windowMgr.getMainWindow(); - if (mainWin) { - isAppAlreadyOpen = true; - if (mainWin.isMinimized()) { - mainWin.restore(); - } - mainWin.focus(); - } - processProtocolAction(argv); - }); - } -} else { - app.releaseSingleInstanceLock(); -} - -/** - * Sets chrome authentication flags in electron - */ -function setChromeFlags() { - - // Read the config parameters synchronously - let config = readConfigFileSync(); - - // If we cannot find any config, just skip setting any flags - if (config && config.customFlags) { - - if (config.customFlags.authServerWhitelist && config.customFlags.authServerWhitelist !== "") { - app.commandLine.appendSwitch('auth-server-whitelist', config.customFlags.authServerWhitelist); - } - - if (config.customFlags.authNegotiateDelegateWhitelist && config.customFlags.authNegotiateDelegateWhitelist !== "") { - app.commandLine.appendSwitch('auth-negotiate-delegate-whitelist', config.customFlags.authNegotiateDelegateWhitelist); - } - - if (config.customFlags.disableGpu) { - app.commandLine.appendSwitch("disable-gpu", true); - app.commandLine.appendSwitch("disable-gpu-compositing", true); - app.commandLine.appendSwitch("disable-d3d11", true); - } - - } - - app.commandLine.appendSwitch("disable-background-timer-throttling", true); - - setChromeFlagsFromCommandLine(); - -} - -/** - * Parse arguments from command line - * and set as chrome flags if applicable - */ -function setChromeFlagsFromCommandLine() { - - // Special args that need to be excluded as part of the chrome command line switch - let specialArgs = ['--url', '--multiInstance', '--userDataPath=', 'symphony://', '--inspect-brk', '--inspect']; - - const cmdArgs = process.argv; - cmdArgs.forEach((arg) => { - // We need to check if the argument key matches the one - // in the special args array and return if it does match - const argSplit = arg.split('='); - const argKey = argSplit[0]; - const argValue = argSplit[1] && arg.substring(arg.indexOf('=')+1); - if (arg.startsWith('--') && specialArgs.includes(argKey)) { - return; - } - - // All the chrome flags starts with -- - // So, any other arg (like 'electron' or '.') - // need to be skipped - if (arg.startsWith('--')) { - // Since chrome takes values after an equals - // We split the arg and set it either as - // just a key, or as a key-value pair - if (argKey && argValue) { - app.commandLine.appendSwitch(argKey.substr(2), argValue); - } else { - app.commandLine.appendSwitch(argKey); - } - log.send(logLevels.INFO, `Appended chrome command line switch ${argKey} with value ${argValue}`); - } - - }); -} - -// Set the chrome flags -setChromeFlags(); - -/** - * 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', () => { - log.send(logLevels.INFO, `App is ready, proceeding to load the POD`); - handlePowerEvents(); - handleCacheFailureCheckOnStartup() - .then(() => { - initiateApp(); - }) - .catch((err) => { - log.send(logLevels.INFO, `Couldn't clear cache and refresh -> ${err}`); - initiateApp(); - }); - - function initiateApp() { - checkFirstTimeLaunch() - .then(readConfigThenOpenMainWindow) - .catch(readConfigThenOpenMainWindow); - - } -}); - -/** - * Is triggered when all the windows are closed - * In which case we quit the app - */ -app.on('window-all-closed', function () { - log.send(logLevels.INFO, `All windows closed, quitting the app`); - app.quit(); -}); - -app.on('quit', function () { - log.send(logLevels.INFO, `-----------------Quitting the app-----------------`); - handleCacheFailureCheckOnExit(); -}); - -/** - * Is triggered when the app is up & running - */ -app.on('activate', function () { - log.send(logLevels.INFO, `SDA is activated again`); - if (windowMgr.isMainWindow(null)) { - log.send(logLevels.INFO, `Main window instance is null, creating new instance`); - setupThenOpenMainWindow(); - } else { - log.send(logLevels.INFO, `Main window instance is available, showing it`); - windowMgr.showMainWindow(); - } -}); - -if (isMac) { - // Sets application version info that will be displayed in about app panel - app.setAboutPanelOptions({ applicationVersion: `${clientVersion}-${version}`, version: buildNumber }); -} - -/** - * This event is emitted only on macOS - * at this moment, support for windows - * is in pipeline (https://github.com/electron/electron/pull/8052) - */ -app.on('open-url', function (event, url) { - log.send(logLevels.INFO, `Open URL event triggered with url ${JSON.stringify(url)}`); - handleProtocolAction(url); -}); - -app.on('web-contents-created', function (event, webContents) { - onWebContent(webContents); -}); - -function onWebContent(webContents) { - - if (!ContextMenuBuilder) { - // eslint-disable-next-line global-require - ContextMenuBuilder = require('electron-spellchecker').ContextMenuBuilder; - } - - const spellchecker = windowMgr.getSpellchecker(); - spellchecker.initializeSpellChecker(); - spellchecker.updateContextMenuLocale(i18n.getMessageFor('ContextMenu')); - const contextMenuBuilder = new ContextMenuBuilder(spellchecker.spellCheckHandler, webContents, false, spellchecker.processMenu.bind(spellchecker)); - contextMenuBuilder.setAlternateStringFormatter(spellchecker.getStringTable(i18n.getMessageFor('ContextMenu'))); - let currentLocale = i18n.getLanguage(); - - const contextMenuListener = (event, info) => { - log.send(logLevels.INFO, `Context menu event triggered for web contents with info ${JSON.stringify(info)}`); - if (currentLocale !== i18n.getLanguage()) { - contextMenuBuilder.setAlternateStringFormatter(spellchecker.getStringTable(i18n.getMessageFor('ContextMenu'))); - spellchecker.updateContextMenuLocale(i18n.getMessageFor('ContextMenu')); - currentLocale = i18n.getLanguage(); - } - contextMenuBuilder.showPopupMenu(info); - }; - - webContents.on('context-menu', contextMenuListener); - - webContents.once('destroyed', () => { - webContents.removeListener('context-menu', contextMenuListener); - }); -} - -/** - * Reads the config fields that are required for the menu items - * then opens the main window - * - * This is a workaround for the issue where the menu template was returned - * even before the config data was populated - * https://perzoinc.atlassian.net/browse/ELECTRON-154 - */ -function readConfigThenOpenMainWindow() { - setCheckboxValues() - .then(setupThenOpenMainWindow) - .catch(setupThenOpenMainWindow) -} - -/** - * Sets up the app (to handle various things like config changes, protocol handling etc.) - * and opens the main window - */ -function setupThenOpenMainWindow() { - - processProtocolAction(process.argv); - - isAppAlreadyOpen = true; - getUrlAndCreateMainWindow(); - - // Allows a developer to set custom user data path from command line when - // launching the app. Mostly used for running automation tests with - // multiple instances - let customDataArg = getCmdLineArg(process.argv, '--userDataPath=', false); - let customDataFolder = customDataArg && customDataArg.substring(customDataArg.indexOf('=') + 1); - - if (customDataArg && customDataFolder) { - app.setPath('userData', customDataFolder); - } - - // Event that fixes the remote desktop issue in Windows - // by repositioning the browser window - electron.screen.on('display-removed', windowMgr.verifyDisplays); - -} - -function checkFirstTimeLaunch() { - return new Promise((resolve, reject) => { - getUserConfigField('configBuildNumber') - .then((configBuildNumber) => { - const execPath = nodePath.dirname(app.getPath('exe')); - const shouldUpdateUserConfig = execPath.indexOf('AppData\\Local\\Programs') !== -1 || isMac; - - if (configBuildNumber && typeof configBuildNumber === 'string' && configBuildNumber !== buildNumber) { - return setupFirstTimeLaunch(resolve, reject, shouldUpdateUserConfig); - } - log.send(logLevels.INFO, `not a first-time launch as - configBuildNumber: ${configBuildNumber} installerBuildNumber: ${buildNumber} shouldUpdateUserConfig: ${shouldUpdateUserConfig}`); - return resolve(); - }) - .catch((e) => { - log.send(logLevels.ERROR, `Error reading configVersion error: ${e}`); - return setupFirstTimeLaunch(resolve, reject, true); - }); - }); -} - -/** - * Setup and update user config - * on first time launch or if the latest app version - * - * @return {Promise} - */ -function setupFirstTimeLaunch(resolve, reject, shouldUpdateUserConfig) { - log.send(logLevels.INFO, 'setting first time launch config'); - getConfigField('launchOnStartup') - .then(setStartup) - .then(() => { - if (shouldUpdateUserConfig) { - log.send(logLevels.INFO, `Resetting user config data? ${shouldUpdateUserConfig}`); - return updateUserConfigOnLaunch(resolve, reject); - } - return resolve(); - }) - .catch(reject); -} - -/** - * Sets Symphony on startup - * @param lStartup - * @returns {Promise} - */ -function setStartup(lStartup) { - log.send(logLevels.INFO, `launch on startup parameter value is ${lStartup}`); - return new Promise((resolve) => { - let launchOnStartup = (String(lStartup) === 'true'); - log.send(logLevels.INFO, `launchOnStartup value is ${launchOnStartup}`); - if (launchOnStartup) { - log.send(logLevels.INFO, `enabling launch on startup`); - autoLaunch.enable(); - return resolve(); - } - autoLaunch.disable(); - return resolve(); - }); -} - -/** - * Checks for the url argument, processes it - * and creates the main window - */ -function getUrlAndCreateMainWindow() { - // allow passing url argument - let url = getCmdLineArg(process.argv, '--url=', false); - if (url) { - windowMgr.createMainWindow(url.substr(6)); - return; - } - - getConfigField('url') - .then(createWin).catch(function (err) { - log.send(logLevels.ERROR, `unable to create main window -> ${err}`); - app.quit(); - }); -} - -/** - * Creates a window - * @param urlFromConfig - */ -function createWin(urlFromConfig) { - // add https protocol if none found. - let parsedUrl = nodeURL.parse(urlFromConfig); - - if (!parsedUrl.protocol || parsedUrl.protocol !== 'https') { - parsedUrl.protocol = 'https:'; - parsedUrl.slashes = true - } - let url = nodeURL.format(parsedUrl); - - windowMgr.createMainWindow(url); -} - -/** - * processes protocol action for windows clients - * @param argv {Array} an array of command line arguments - */ -function processProtocolAction(argv) { - - // In case of windows, we need to handle protocol handler - // manually because electron doesn't emit - // 'open-url' event on windows - if (!(process.platform === 'win32')) { - return; - } - - let protocolUri = getCmdLineArg(argv, 'symphony://', false); - log.send(logLevels.INFO, `Trying to process a protocol action for uri ${protocolUri}`); - - if (protocolUri) { - const parsedURL = urlParser.parse(protocolUri); - if (!parsedURL.protocol || !parsedURL.slashes) { - return; - } - log.send(logLevels.INFO, `Parsing protocol url successful for ${parsedURL}`); - handleProtocolAction(protocolUri); - } -} - -/** - * Handles a protocol action based on the current state of the app - * @param uri - */ -function handleProtocolAction(uri) { - if (!isAppAlreadyOpen) { - log.send(logLevels.INFO, `App started by protocol url ${uri}. We are caching this to be processed later!`); - // app is opened by the protocol url, cache the protocol url to be used later - protocolHandler.setProtocolUrl(uri); - } else { - // This is needed for mac OS as it brings pop-outs to foreground - // (if it has been previously focused) instead of main window - if (isMac) { - const mainWindow = windowMgr.getMainWindow(); - if (mainWindow && !mainWindow.isDestroyed()) { - windowMgr.activate(mainWindow.winName); - } - } - // app is already open, so, just trigger the protocol action method - log.send(logLevels.INFO, `App opened by protocol url ${uri}`); - protocolHandler.processProtocolAction(uri); - } -} - -const handlePowerEvents = () => { - - const events = [ - 'suspend', 'resume', 'on-ac', 'on-battery', 'shutdown', 'lock-screen', 'unlock-screen' - ]; - - events.forEach((appEvent) => { - electron.powerMonitor.on(appEvent, () => { - log.send(logLevels.INFO, `Power Monitor Event Occurred: ${appEvent}`) - }); - }); -}; diff --git a/js/mainApiMgr.js b/js/mainApiMgr.js deleted file mode 100644 index 07f666fa..00000000 --- a/js/mainApiMgr.js +++ /dev/null @@ -1,210 +0,0 @@ -'use strict'; - -/** - * This module runs in the main process and handles api calls - * from the renderer process. - */ -const electron = require('electron'); -const app = electron.app; - -const windowMgr = require('./windowMgr.js'); -const log = require('./log.js'); -const logLevels = require('./enums/logLevels'); -const activityDetection = require('./activityDetection'); -const badgeCount = require('./badgeCount.js'); -const protocolHandler = require('./protocolHandler'); -const configureNotification = require('./notify/settings/configure-notification-position'); -const { bringToFront } = require('./bringToFront.js'); -const eventEmitter = require('./eventEmitter'); -const { isMac } = require('./utils/misc'); -const { openScreenPickerWindow } = require('./desktopCapturer'); -const { openScreenSharingIndicator } = require('./screenSharingIndicator'); -const { setPreloadMemoryInfo, setIsInMeeting, setPreloadWindow } = require('./memoryMonitor'); - -const apiEnums = require('./enums/api.js'); -const apiCmds = apiEnums.cmds; -const apiName = apiEnums.apiName; - -// can be overridden for testing -let checkValidWindow = true; - -/** - * Ensure events comes from a window that we have created. - * @param {EventEmitter} event node emitter event to be tested - * @return {Boolean} returns true if exists otherwise false - */ -function isValidWindow(event) { - if (!checkValidWindow) { - return true; - } - let result = false; - if (event && event.sender) { - // validate that event sender is from window we created - const browserWin = electron.BrowserWindow.fromWebContents(event.sender); - const winKey = event.sender.browserWindowOptions && - event.sender.browserWindowOptions.winKey; - - result = windowMgr.hasWindow(browserWin, winKey); - } - - if (!result) { - log.send(logLevels.WARN, 'invalid window try to perform action, ignoring action'); - } - - return result; -} - -/** - * Method that is invoked when the application is reloaded/navigated - * window.addEventListener('beforeunload') - * @param windowName - */ -function sanitize(windowName) { - // To make sure the reload event is from the main window - if (windowMgr.getMainWindow() && windowName === windowMgr.getMainWindow().winName) { - // reset the badge count whenever an user refreshes the electron client - badgeCount.show(0); - - // Terminates the screen snippet process on reload - if (!isMac) { - eventEmitter.emit('killScreenSnippet'); - } - // Closes all the child windows - windowMgr.cleanUpChildWindows(); - } -} - -/** - * Handle API related ipc messages from renderers. Only messages from windows - * we have created are allowed. - */ -electron.ipcMain.on(apiName, (event, arg) => { - - if (!isValidWindow(event)) { - return; - } - - if (!arg) { - return; - } - - switch(arg.cmd) { - case apiCmds.isOnline: - if (typeof arg.isOnline === 'boolean') { - windowMgr.setIsOnline(arg.isOnline); - } - break; - case apiCmds.setBadgeCount: - if (typeof arg.count === 'number') { - badgeCount.show(arg.count); - } - break; - case apiCmds.registerProtocolHandler: - protocolHandler.setProtocolWindow(event.sender); - protocolHandler.checkProtocolAction(); - break; - case apiCmds.badgeDataUrl: - if (typeof arg.dataUrl === 'string' && typeof arg.count === 'number') { - badgeCount.setDataUrl(arg.dataUrl, arg.count); - } - break; - case apiCmds.activate: - if (typeof arg.windowName === 'string') { - windowMgr.activate(arg.windowName); - } - break; - case apiCmds.registerBoundsChange: - windowMgr.setBoundsChangeWindow(event.sender); - break; - case apiCmds.registerLogger: - // renderer window that has a registered logger from JS. - log.setLogWindow(event.sender); - break; - case apiCmds.registerActivityDetection: - if (typeof arg.period === 'number') { - // renderer window that has a registered activity detection from JS. - activityDetection.setActivityWindow(arg.period, event.sender); - } - break; - case apiCmds.showNotificationSettings: - if (typeof arg.windowName === 'string') { - configureNotification.openConfigurationWindow(arg.windowName); - } - break; - case apiCmds.sanitize: - if (typeof arg.windowName === 'string') { - sanitize(arg.windowName); - } - break; - case apiCmds.bringToFront: - // validates the user bring to front config and activates the wrapper - if (typeof arg.reason === 'string' && arg.reason === 'notification') { - bringToFront(arg.windowName, arg.reason); - } - break; - case apiCmds.openScreenPickerWindow: - if (Array.isArray(arg.sources) && typeof arg.id === 'number') { - openScreenPickerWindow(event.sender, arg.sources, arg.id); - } - break; - case apiCmds.popupMenu: { - let browserWin = electron.BrowserWindow.fromWebContents(event.sender); - if (browserWin && !browserWin.isDestroyed()) { - windowMgr.getMenu().popup(browserWin, {x: 20, y: 15, async: true}); - } - break; - } - case apiCmds.optimizeMemoryConsumption: - if (typeof arg.activeRequests === 'number') { - log.send(logLevels.INFO, 'Received active network request from renderer processes'); - setPreloadMemoryInfo(arg.activeRequests); - } - break; - case apiCmds.optimizeMemoryRegister: - setPreloadWindow(event.sender); - break; - case apiCmds.setIsInMeeting: - if (typeof arg.isInMeeting === 'boolean') { - setIsInMeeting(arg.isInMeeting); - } - break; - case apiCmds.setLocale: - if (typeof arg.locale === 'string') { - let browserWin = electron.BrowserWindow.fromWebContents(event.sender); - windowMgr.setLocale(browserWin, { language: arg.locale }); - } - break; - case apiCmds.keyPress: - if (typeof arg.keyCode === 'number') { - windowMgr.handleKeyPress(arg.keyCode); - } - break; - case apiCmds.isMisspelled: - if (typeof arg.text === 'string') { - /* eslint-disable no-param-reassign */ - event.returnValue = windowMgr.isMisspelled(arg.text); - /* eslint-enable no-param-reassign */ - } - break; - case apiCmds.openScreenSharingIndicator: - if (typeof arg.displayId === 'string' && typeof arg.id === 'number') { - openScreenSharingIndicator(event.sender, arg.displayId, arg.id); - } - break; - case apiCmds.cancelNetworkStatusCheck: - windowMgr.cancelNetworkStatusCheck(); - break; - case apiCmds.quitWindow: - app.quit(); - break; - default: - } - -}); - -// expose these methods primarily for testing... -module.exports = { - shouldCheckValidWindow: function(shouldCheck) { - checkValidWindow = shouldCheck; - } -}; \ No newline at end of file diff --git a/js/memoryMonitor.js b/js/memoryMonitor.js deleted file mode 100644 index d479648c..00000000 --- a/js/memoryMonitor.js +++ /dev/null @@ -1,143 +0,0 @@ -'use strict'; -const electron = require('electron'); -const app = electron.app; -const log = require('./log.js'); -const logLevels = require('./enums/logLevels.js'); -const { getMainWindow, setIsAutoReload, getIsOnline } = require('./windowMgr'); -const { getConfigField } = require('./config'); - -const memoryRefreshThreshold = 60 * 60 * 1000; -const maxIdleTime = 4 * 60 * 60 * 1000; -const memoryRefreshInterval = 60 * 60 * 1000; -const cpuUsageThreshold = 5; - -let isInMeeting = false; -let canReload = true; -let preloadMemory; -let preloadWindow; - -// once a minute -setInterval(gatherMemory, 1000 * 60); - -/** - * Gathers system memory and logs it to the remote system - */ -function gatherMemory() { - let appMetrics = app.getAppMetrics(); - log.send(logLevels.INFO, `Current App Metrics -> ${JSON.stringify(appMetrics)}`); -} - -/** - * Method that checks memory usage every minute - * and verify if the user in inactive if so it reloads the - * application to free up some memory consumption - */ -function optimizeMemory() { - - if (!preloadMemory.cpuUsage) { - log.send(logLevels.INFO, `cpu usage not available`); - return; - } - - const cpuUsagePercentage = preloadMemory.cpuUsage.percentCPUUsage; - const activeNetworkRequest = preloadMemory.activeRequests === 0; - - electron.powerMonitor.querySystemIdleTime((time) => { - const idleTime = time * 1000; - if (cpuUsagePercentage <= cpuUsageThreshold - && !isInMeeting - && getIsOnline() - && canReload - && idleTime > maxIdleTime - && activeNetworkRequest - ) { - getConfigField('memoryRefresh') - .then((enabled) => { - if (enabled) { - const mainWindow = getMainWindow(); - - if (mainWindow && !mainWindow.isDestroyed()) { - setIsAutoReload(true); - log.send(logLevels.INFO, `Reloading the app to optimize memory usage as - memory consumption is no longer detectable - CPU usage percentage was ${preloadMemory.cpuUsage.percentCPUUsage} - user was in a meeting? ${isInMeeting} - pending network request on the client was ${preloadMemory.activeRequests} - is network online? ${getIsOnline()}`); - mainWindow.reload(); - - // do not refresh for another 1hrs - canReload = false; - setTimeout(() => { - canReload = true; - }, memoryRefreshThreshold); - } - } else { - log.send(logLevels.INFO, `Memory refresh not enabled by the user so Not Reloading the app`); - } - }); - } else { - log.send(logLevels.INFO, `Not Reloading the app as - application was refreshed less than a hour ago? ${canReload ? 'no' : 'yes'} - memory consumption is no longer detectable - CPU usage percentage was ${preloadMemory.cpuUsage.percentCPUUsage} - user was in a meeting? ${isInMeeting} - pending network request on the client was ${preloadMemory.activeRequests} - is network online? ${getIsOnline()}`); - } - }); -} - -/** - * Sets the current user meeting status - * @param meetingStatus - Whether user is in an active meeting - */ -function setIsInMeeting(meetingStatus) { - isInMeeting = meetingStatus; -} - -/** - * Sets preload memory info and calls optimize memory func - * - * @param activeRequests - pending active network requests on the client - */ -function setPreloadMemoryInfo(activeRequests) { - log.send(logLevels.INFO, 'Memory info received from preload process now running optimize memory logic'); - const cpuUsage = process.getCPUUsage(); - preloadMemory = { cpuUsage, activeRequests }; - optimizeMemory(); -} - -/** - * Sets the preload window - * - * @param win - preload window - */ -function setPreloadWindow(win) { - log.send(logLevels.INFO, 'Preload window registered'); - preloadWindow = win; -} - -/** - * Request memory info from the registered preload window - * which invokes the optimize memory func - */ -function requestMemoryInfo() { - if (preloadWindow) { - log.send(logLevels.INFO, 'Requesting memory information from the preload script'); - preloadWindow.send('memory-info-request'); - } -} - -/** - * Requests memory info from the renderer every 4 hrs - */ -setInterval(() => { - requestMemoryInfo(); -}, memoryRefreshInterval); - -module.exports = { - setIsInMeeting, - setPreloadMemoryInfo, - setPreloadWindow, -}; \ No newline at end of file diff --git a/js/menus/menuTemplate.js b/js/menus/menuTemplate.js deleted file mode 100644 index 97a6d9fc..00000000 --- a/js/menus/menuTemplate.js +++ /dev/null @@ -1,582 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const electron = require('electron'); - -const { updateConfigField, getMultipleConfigField, readConfigFromFile } = require('../config.js'); -const { isMac, isWindowsOS } = require('../utils/misc.js'); -const archiveHandler = require('../utils/archiveHandler'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const eventEmitter = require('../eventEmitter'); -const aboutApp = require('../aboutApp'); -const moreInfo = require('../moreInfo'); -const titleBarStyles = require('../enums/titleBarStyles'); -const i18n = require('../translation/i18n'); -const autoLaunch = require('../autoLaunch'); - -const configFields = [ - 'minimizeOnClose', - 'launchOnStartup', - 'alwaysOnTop', - 'notificationSettings', - 'bringToFront', - 'memoryRefresh', - 'isCustomTitleBar' -]; - -let minimizeOnClose = false; -let launchOnStartup = false; -let isAlwaysOnTop = false; -let bringToFront = false; -let memoryRefresh = false; -let titleBarStyle = titleBarStyles.CUSTOM; - -const windowsAccelerator = Object.assign({ - undo: 'Ctrl+Z', - redo: 'Ctrl+Y', - cut: 'Ctrl+X', - copy: 'Ctrl+C', - paste: 'Ctrl+V', - pasteandmatchstyle: 'Ctrl+Shift+V', - selectall: 'Ctrl+A', - resetzoom: 'Ctrl+0', - zoomin: 'Ctrl+Shift+Plus', - zoomout: 'Ctrl+-', - togglefullscreen: 'F11', - minimize: 'Ctrl+M', - close: 'Ctrl+W', -}); - -function getTemplate(app) { - - const template = [{ - label: i18n.getMessageFor('Edit'), - submenu: [ - buildMenuItem('undo', i18n.getMessageFor('Undo')), - buildMenuItem('redo', i18n.getMessageFor('Redo')), - { type: 'separator' }, - buildMenuItem('cut', i18n.getMessageFor('Cut')), - buildMenuItem('copy', i18n.getMessageFor('Copy')), - buildMenuItem('paste', i18n.getMessageFor('Paste')), - buildMenuItem('pasteandmatchstyle', i18n.getMessageFor('Paste and Match Style')), - buildMenuItem('delete', i18n.getMessageFor('Delete')), - buildMenuItem('selectall', i18n.getMessageFor('Select All')) - ] - }, - { - label: i18n.getMessageFor('View'), - submenu: [{ - label: i18n.getMessageFor('Reload'), - accelerator: 'CmdOrCtrl+R', - click(item, focusedWindow) { - if (focusedWindow) { - focusedWindow.reload(); - } - } - }, - { type: 'separator' }, - buildMenuItem('resetzoom', i18n.getMessageFor('Actual Size')), - buildMenuItem('zoomin', i18n.getMessageFor('Zoom In')), - buildMenuItem('zoomout', i18n.getMessageFor('Zoom Out')), - { type: 'separator' }, - buildMenuItem('togglefullscreen', i18n.getMessageFor('Toggle Full Screen')), - ] - }, - { - role: 'window', - label: i18n.getMessageFor('Window'), - submenu: [ - buildMenuItem('minimize', i18n.getMessageFor('Minimize')), - buildMenuItem('close', i18n.getMessageFor('Close')), - ] - }, - { - role: 'help', - label: i18n.getMessageFor('Help'), - submenu: - [ - { - label: i18n.getMessageFor('Symphony Help'), - click() { - let helpUrl = i18n.getMessageFor('Help Url'); - electron.shell.openExternal(helpUrl); - } - }, - { - label: i18n.getMessageFor('Learn More'), - click() { - let symUrl = i18n.getMessageFor('Symphony Url'); - electron.shell.openExternal(symUrl); - } - }, - { - label: i18n.getMessageFor('Troubleshooting'), - submenu: [ - { - label: isMac ? i18n.getMessageFor('Show Logs in Finder') : i18n.getMessageFor('Show Logs in Explorer'), - click(item, focusedWindow) { - - const FILE_EXTENSIONS = ['.log']; - const MAC_LOGS_PATH = '/Library/Logs/Symphony/'; - const WINDOWS_LOGS_PATH = '\\AppData\\Roaming\\Symphony\\logs'; - - let logsPath = isMac ? MAC_LOGS_PATH : WINDOWS_LOGS_PATH; - let source = electron.app.getPath('home') + logsPath; - - if (!fs.existsSync(source) && focusedWindow && !focusedWindow.isDestroyed()) { - electron.dialog.showMessageBox(focusedWindow, { - type: 'error', - title: i18n.getMessageFor('Failed!'), - message: i18n.getMessageFor('No logs are available to share') - }); - return; - } - - let destPath = isMac ? '/logs_symphony_' : '\\logs_symphony_'; - let timestamp = new Date().getTime(); - - let destination = electron.app.getPath('downloads') + destPath + timestamp + '.zip'; - - archiveHandler.generateArchiveForDirectory(source, destination, FILE_EXTENSIONS) - .then(() => { - electron.shell.showItemInFolder(destination); - }) - .catch((err) => { - if (focusedWindow && !focusedWindow.isDestroyed()) { - electron.dialog.showMessageBox(focusedWindow, { - type: 'error', - title: i18n.getMessageFor('Failed!'), - message: i18n.getMessageFor('Unable to generate logs due to ') + err - }); - } - }); - - } - }, - { - label: isMac ? i18n.getMessageFor('Show crash dump in Finder') : i18n.getMessageFor('Show crash dump in Explorer'), - click(item, focusedWindow) { - const FILE_EXTENSIONS = isMac ? ['.dmp'] : ['.dmp', '.txt']; - const crashesDirectory = electron.crashReporter.getCrashesDirectory(); - let source = isMac ? crashesDirectory + '/completed' : crashesDirectory; - - // TODO: Add support to get diagnostic reports from ~/Library/Logs/DiagnosticReports - if (!fs.existsSync(source) || fs.readdirSync(source).length === 0 && focusedWindow && !focusedWindow.isDestroyed()) { - electron.dialog.showMessageBox(focusedWindow, { - type: 'error', - title: i18n.getMessageFor('Failed!'), - message: i18n.getMessageFor('No crashes available to share') - }); - return; - } - - let destPath = isMac ? '/crashes_symphony_' : '\\crashes_symphony_'; - let timestamp = new Date().getTime(); - - let destination = electron.app.getPath('downloads') + destPath + timestamp + '.zip'; - - archiveHandler.generateArchiveForDirectory(source, destination, FILE_EXTENSIONS) - .then(() => { - electron.shell.showItemInFolder(destination); - }) - .catch((err) => { - if (focusedWindow && !focusedWindow.isDestroyed()) { - electron.dialog.showMessageBox(focusedWindow, { - type: 'error', - title: i18n.getMessageFor('Failed!'), - message: i18n.getMessageFor('Unable to generate crash reports due to ') + err - }); - } - }); - } - }, - { - label: i18n.getMessageFor('Toggle Developer Tools'), - accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I', - click(item, focusedWindow) { - let devToolsEnabled = readConfigFromFile('devToolsEnabled'); - if (focusedWindow && !focusedWindow.isDestroyed()) { - if (devToolsEnabled) { - focusedWindow.webContents.toggleDevTools(); - } else { - log.send(logLevels.INFO, `dev tools disabled for ${focusedWindow.winName} window`); - electron.dialog.showMessageBox(focusedWindow, { - type: 'warning', - buttons: ['Ok'], - title: i18n.getMessageFor('Dev Tools disabled'), - message: i18n.getMessageFor('Dev Tools has been disabled! Please contact your system administrator to enable it!'), - }); - } - } - } - }, - { - label: i18n.getMessageFor('More Information'), - click(item, focusedWindow) { - let windowName = focusedWindow ? focusedWindow.name : ''; - moreInfo.openMoreInfoWindow(windowName); - } - } - ] - } - ] - } - ]; - - if (isMac && template[0].label !== app.getName()) { - template.unshift({ - label: app.getName(), - submenu: [{ - role: 'about', - label: i18n.getMessageFor('About Symphony') - }, - { - type: 'separator' - }, - { - role: 'services', - label: i18n.getMessageFor('Services'), - submenu: [] - }, - { - type: 'separator' - }, - { - role: 'hide', - label: i18n.getMessageFor('Hide Symphony') - }, - { - role: 'hideothers', - label: i18n.getMessageFor('Hide Others') - }, - { - role: 'unhide', - label: i18n.getMessageFor('Show All') - }, - { - type: 'separator' - }, - { - role: 'quit', - label: i18n.getMessageFor('Quit Symphony') - } - ] - }); - // Edit menu. - template[1].submenu.push({ - type: 'separator' - }, { - label: i18n.getMessageFor('Speech'), - submenu: [{ - role: 'startspeaking', - label: i18n.getMessageFor('Start Speaking') - }, - { - role: 'stopspeaking', - label: i18n.getMessageFor('Stop Speaking') - } - ] - }); - // Window menu. - template[3].submenu = [{ - accelerator: 'CmdOrCtrl+W', - role: 'close', - label: i18n.getMessageFor('Close') - }, - { - accelerator: 'CmdOrCtrl+M', - role: 'minimize', - label: i18n.getMessageFor('Minimize') - }, - { - role: 'zoom', - label: i18n.getMessageFor('Zoom') - }, - { - type: 'separator' - }, - { - role: 'front', - label: i18n.getMessageFor('Bring All to Front') - } - ]; - } - - let index = 2; - if (isMac && template[0].label !== app.getName()) { - index = 3; - } - - template[index].submenu.push({ - type: 'separator' - }); - - // Window menu -> launchOnStartup. - template[index].submenu.push({ - label: i18n.getMessageFor('Auto Launch On Startup'), - type: 'checkbox', - checked: launchOnStartup, - click: function (item, focusedWindow) { - if (item.checked) { - autoLaunch.enable() - .catch(function (err) { - let title = 'Error setting AutoLaunch configuration'; - log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err); - if (focusedWindow && !focusedWindow.isDestroyed()) { - electron.dialog.showMessageBox(focusedWindow, { - type: 'error', - title: i18n.getMessageFor(title), - message: i18n.getMessageFor(title) + ': ' + err - }); - } - }); - } else { - autoLaunch.disable() - .catch(function (err) { - let title = 'Error setting AutoLaunch configuration'; - log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err); - if (focusedWindow && !focusedWindow.isDestroyed()) { - electron.dialog.showMessageBox(focusedWindow, { - type: 'error', - title: i18n.getMessageFor(title), - message: i18n.getMessageFor(title) + ': ' + err - }); - } - }); - } - launchOnStartup = item.checked; - updateConfigField('launchOnStartup', launchOnStartup); - } - }); - - // Window menu -> alwaysOnTop. - template[index].submenu.push({ - label: i18n.getMessageFor('Always on Top'), - type: 'checkbox', - checked: isAlwaysOnTop, - click: (item) => { - isAlwaysOnTop = item.checked; - eventEmitter.emit('isAlwaysOnTop', { - isAlwaysOnTop, - shouldActivateMainWindow: true - }); - updateConfigField('alwaysOnTop', isAlwaysOnTop); - } - }); - - // Window menu -> minimizeOnClose. - // ToDo: Add behavior on Close. - template[index].submenu.push({ - label: i18n.getMessageFor('Minimize on Close'), - type: 'checkbox', - checked: minimizeOnClose, - click: function (item) { - minimizeOnClose = item.checked; - updateConfigField('minimizeOnClose', minimizeOnClose); - } - }); - - // Window menu -> bringToFront - template[index].submenu.push({ - label: isWindowsOS ? i18n.getMessageFor('Flash Notification in Taskbar') : i18n.getMessageFor('Bring to Front on Notifications'), - type: 'checkbox', - checked: bringToFront, - click: function (item) { - bringToFront = item.checked; - updateConfigField('bringToFront', bringToFront); - } - }); - - // Window/View menu -> separator - template[index].submenu.push({ - type: 'separator', - }); - - // Window - View menu -> memoryRefresh - template[index].submenu.push({ - label: i18n.getMessageFor('Refresh app when idle'), - type: 'checkbox', - checked: memoryRefresh, - click: function (item) { - memoryRefresh = item.checked; - updateConfigField('memoryRefresh', memoryRefresh); - } - }); - - // Window - View menu -> Clear Cache - template[index].submenu.push({ - label: i18n.getMessageFor('Clear cache and Reload'), - click: function (item, focusedWindow) { - if (focusedWindow && !focusedWindow.isDestroyed()) { - electron.session.defaultSession.clearCache(() => { - focusedWindow.reload(); - }); - } - } - }); - - if (!isMac) { - /* eslint-disable no-param-reassign */ - template[index].submenu.push({ - label: titleBarStyle === titleBarStyles.NATIVE ? - i18n.getMessageFor('Enable Hamburger menu') : - i18n.getMessageFor('Disable Hamburger menu'), - click: function () { - const isNativeStyle = titleBarStyle === titleBarStyles.NATIVE; - - titleBarStyle = isNativeStyle ? titleBarStyles.NATIVE : titleBarStyles.CUSTOM; - titleBarActions(app, isNativeStyle); - } - }, { - type: 'separator' - }); - /* eslint-enable no-param-reassign */ - - template[index].submenu.push({ - label: i18n.getMessageFor('Quit Symphony'), - click: function () { - app.quit(); - } - }); - - // This adds About Symphony under help menu for windows - template[3].submenu.push({ - label: i18n.getMessageFor('About Symphony'), - click(menuItem, focusedWindow) { - let windowName = focusedWindow ? focusedWindow.winName : ''; - aboutApp.openAboutWindow(windowName); - } - }); - } - - return template; -} - -/** - * Sets the checkbox values for different menu items - * based on configuration - */ -function setCheckboxValues() { - return new Promise((resolve) => { - /** - * Method that reads multiple config fields - */ - getMultipleConfigField(configFields) - .then(function (configData) { - for (let key in configData) { - if (configData.hasOwnProperty(key)) { // eslint-disable-line no-prototype-builtins - switch (key) { - case 'minimizeOnClose': - minimizeOnClose = configData[key]; - break; - case 'launchOnStartup': - launchOnStartup = configData[key]; - break; - case 'alwaysOnTop': - isAlwaysOnTop = configData[key]; - eventEmitter.emit('isAlwaysOnTop', { - isAlwaysOnTop: configData[key], - shouldActivateMainWindow: true - }); - break; - case 'notificationSettings': - eventEmitter.emit('notificationSettings', configData[key]); - break; - case 'bringToFront': - bringToFront = configData[key]; - break; - case 'isCustomTitleBar': - titleBarStyle = configData[key] ? titleBarStyles.CUSTOM : titleBarStyles.NATIVE; - break; - case 'memoryRefresh': - memoryRefresh = configData[key]; - break; - default: - break; - } - } - } - return resolve(); - }) - .catch((err) => { - let title = 'Error loading configuration'; - log.send(logLevels.ERROR, 'MenuTemplate: error reading configuration fields, error: ' + err); - if (electron.BrowserWindow.getFocusedWindow() && !electron.BrowserWindow.getFocusedWindow().isDestroyed()) { - electron.dialog.showMessageBox(electron.BrowserWindow.getFocusedWindow(), { - type: 'error', - title: i18n.getMessageFor(title), - message: i18n.getMessageFor(title) + ': ' + err - }); - } - return resolve(); - }); - }); -} - -/** - * Sets respective accelerators w.r.t roles for the menu template - * - * @param role {String} The action of the menu item - * @param label {String} Menu item name - * @return {Object} - * @return {Object}.role The action of the menu item - * @return {Object}.accelerator keyboard shortcuts and modifiers - */ -function buildMenuItem(role, label) { - - if (isMac) { - return label ? { role: role, label: label } : { role: role } - } - - if (isWindowsOS) { - return label ? { role: role, label: label, accelerator: windowsAccelerator[role] || '', registerAccelerator: true } - : { role: role, accelerator: windowsAccelerator[role] || '', registerAccelerator: true } - } - - return label ? { role: role, label: label } : { role: role } -} - -function getMinimizeOnClose() { - return minimizeOnClose; -} - -function getTitleBarStyle() { - return titleBarStyle; -} - -/** - * Displays an option to the user whether - * to relaunch application - * - * @param app - * @param isNativeStyle - */ -function titleBarActions(app, isNativeStyle) { - const options = { - type: 'question', - title: i18n.getMessageFor('Relaunch Application'), - message: i18n.getMessageFor('Updating Title bar style requires Symphony to relaunch.'), - detail: i18n.getMessageFor('Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.'), - buttons: [i18n.getMessageFor('Relaunch'), i18n.getMessageFor('Cancel')], - cancelId: 1 - }; - electron.dialog.showMessageBox(electron.BrowserWindow.getFocusedWindow(), options, function (index) { - if (index === 0) { - updateConfigField('isCustomTitleBar', !!isNativeStyle) - .then(() => { - app.relaunch(); - app.exit(); - }).catch((e) => { - log.send(logLevels.ERROR, `Unable to disable / enable hamburger menu due to error: ${e}`); - }); - } - }); -} - -module.exports = { - getTemplate: getTemplate, - getMinimizeOnClose: getMinimizeOnClose, - setCheckboxValues: setCheckboxValues, - getTitleBarStyle: getTitleBarStyle -}; diff --git a/js/moreInfo/index.js b/js/moreInfo/index.js deleted file mode 100644 index 970de4c9..00000000 --- a/js/moreInfo/index.js +++ /dev/null @@ -1,141 +0,0 @@ -'use strict'; - -const electron = require('electron'); -const BrowserWindow = electron.BrowserWindow; -const path = require('path'); -const fs = require('fs'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const { version, clientVersion, buildNumber } = require('../../package.json'); -const { initCrashReporterMain, initCrashReporterRenderer } = require('../crashReporter.js'); -const i18n = require('../translation/i18n'); -const { isMac } = require('../utils/misc'); - -let moreInfoWindow; - -let windowConfig = { - width: 800, - height: 600, - show: false, - modal: true, - autoHideMenuBar: true, - titleBarStyle: true, - resizable: false, - fullscreenable: false, - webPreferences: { - preload: path.join(__dirname, 'renderer.js'), - sandbox: true, - nodeIntegration: false, - devTools: false - } -}; - -/** - * method to get the HTML template path - * @returns {string} - */ -function getTemplatePath() { - let templatePath = path.join(__dirname, 'more-info.html'); - try { - fs.statSync(templatePath).isFile(); - } catch (err) { - log.send(logLevels.ERROR, 'more-info: Could not find template ("' + templatePath + '").'); - } - return 'file://' + templatePath; -} - -/** - * Opens the about application window for a specific window - * @param {String} windowName - name of the window upon - * which this window should show - */ -function openMoreInfoWindow(windowName) { - - // This prevents creating multiple instances of the - // about window - if (moreInfoWindow) { - if (moreInfoWindow.isMinimized()) { - moreInfoWindow.restore(); - } - moreInfoWindow.focus(); - return; - } - let allWindows = BrowserWindow.getAllWindows(); - allWindows = allWindows.find((window) => { return window.winName === windowName }); - - // if we couldn't find any window matching the window name - // it will render as a new window - if (allWindows) { - windowConfig.parent = allWindows; - } - - moreInfoWindow = new BrowserWindow(windowConfig); - moreInfoWindow.setVisibleOnAllWorkspaces(true); - moreInfoWindow.loadURL(getTemplatePath()); - - // sets the AlwaysOnTop property for the about window - // if the main window's AlwaysOnTop is true - let focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow && focusedWindow.isAlwaysOnTop()) { - moreInfoWindow.setAlwaysOnTop(true); - } - - moreInfoWindow.once('ready-to-show', () => { - moreInfoWindow.show(); - }); - - moreInfoWindow.webContents.on('did-finish-load', () => { - const moreInfoContext = i18n.getMessageFor('MoreInfo'); - moreInfoWindow.webContents.send('i18n-more-info', moreInfoContext); - // initialize crash reporter - initCrashReporterMain({ process: 'more info window' }); - initCrashReporterRenderer(moreInfoWindow, { process: 'render | more info window' }); - moreInfoWindow.webContents.send('versionInfo', { version, clientVersion, buildNumber }); - if (!isMac) { - // prevents from displaying menu items when "alt" key is pressed - moreInfoWindow.setMenu(null); - } - }); - - moreInfoWindow.webContents.on('crashed', function (event, killed) { - - log.send(logLevels.INFO, `More Info Window crashed! Killed? ${killed}`); - - if (killed) { - return; - } - - const options = { - type: 'error', - title: i18n.getMessageFor('Renderer Process Crashed'), - message: i18n.getMessageFor('Oops! Looks like we have had a crash.'), - buttons: ['Close'] - }; - - electron.dialog.showMessageBox(options, function () { - if (moreInfoWindow && !moreInfoWindow.isDestroyed()) { - moreInfoWindow.close(); - } - }); - }); - - moreInfoWindow.on('close', () => { - destroyWindow(); - }); - - moreInfoWindow.on('closed', () => { - destroyWindow(); - }); -} - -/** - * Destroys a window - */ -function destroyWindow() { - moreInfoWindow = null; -} - - -module.exports = { - openMoreInfoWindow: openMoreInfoWindow -}; diff --git a/js/moreInfo/more-info.html b/js/moreInfo/more-info.html deleted file mode 100644 index 1c70d49c..00000000 --- a/js/moreInfo/more-info.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - -
- - - - - - - - - - -
- - diff --git a/js/moreInfo/renderer.js b/js/moreInfo/renderer.js deleted file mode 100644 index 4b89da64..00000000 --- a/js/moreInfo/renderer.js +++ /dev/null @@ -1,66 +0,0 @@ -'use strict'; -const { ipcRenderer, crashReporter } = require('electron'); - -renderDom(); - -/** - * Method that renders application data - */ -function renderDom() { - document.addEventListener('DOMContentLoaded', function () { - const electronV = document.getElementById('electron'); - const chromiumV = document.getElementById('chromium'); - const v8V = document.getElementById('v8'); - const nodeV = document.getElementById('node'); - const opensslV = document.getElementById('openssl'); - const zlibV = document.getElementById('zlib'); - const uvV = document.getElementById('uv'); - const aresV = document.getElementById('ares'); - const httpparserV = document.getElementById('httpparser'); - - electronV.innerHTML = `Electron ${process.versions.electron}`; - chromiumV.innerHTML = `Chromium ${process.versions.chrome}`; - v8V.innerHTML = `V8 ${process.versions.v8}`; - nodeV.innerHTML = `Node ${process.versions.node}`; - opensslV.innerHTML = `OpenSSL ${process.versions.openssl}`; - zlibV.innerHTML = `ZLib ${process.versions.zlib}`; - uvV.innerHTML = `UV ${process.versions.uv}`; - aresV.innerHTML = `Ares ${process.versions.ares}`; - httpparserV.innerHTML = `HTTP Parser ${process.versions.http_parser}`; - }); -} - -ipcRenderer.on('register-crash-reporter', (event, arg) => { - if (arg && typeof arg === 'object') { - crashReporter.start(arg); - } -}); - -ipcRenderer.on('i18n-more-info', (event, content) => { - if (content && typeof content === 'object') { - const i18nNodes = document.querySelectorAll('[data-i18n-text]'); - - for (let node of i18nNodes) { - if (node.attributes['data-i18n-text'] && node.attributes['data-i18n-text'].value) { - node.innerText = content[node.attributes['data-i18n-text'].value] || node.attributes['data-i18n-text'].value; - } - } - } -}); - -// note: this is a workaround until -// https://github.com/electron/electron/issues/8841 -// is fixed on the electron. where 'will-navigate' -// is never fired in sandbox mode -// -// This is required in order to prevent from loading -// dropped content -window.addEventListener('drop', function(e) { - e.preventDefault(); - e.stopPropagation(); -}); - -window.addEventListener('dragover', function(e) { - e.preventDefault(); - e.stopPropagation(); -}); \ No newline at end of file diff --git a/js/networkError/contents.js b/js/networkError/contents.js deleted file mode 100644 index d285b28f..00000000 --- a/js/networkError/contents.js +++ /dev/null @@ -1,126 +0,0 @@ -const errorContent = (data) => { - return (`
-
-
- - - - Symphony_logo - - - - -
-
-

${data["Problem connecting to Symphony"] || "Problem connecting to Symphony"}

-

- ${data["Looks like you are not connected to the Internet. We'll try to reconnect automatically."] - || "Looks like you are not connected to the Internet. We'll try to reconnect automatically."} -

-
ERR_INTERNET_DISCONNECTED
- - -
-
-
`); -}; - - -module.exports = { - errorContent, -}; \ No newline at end of file diff --git a/js/networkError/index.js b/js/networkError/index.js deleted file mode 100644 index b4068c93..00000000 --- a/js/networkError/index.js +++ /dev/null @@ -1,47 +0,0 @@ -const { ipcRenderer } = require('electron'); - -const apiEnums = require('../enums/api.js'); -const apiCmds = apiEnums.cmds; -const apiName = apiEnums.apiName; -const htmlContents = require('./contents'); - -class NetworkError { - - constructor() { - this.domParser = new DOMParser(); - } - - showError(data) { - if (!data) { - return; - } - const { message, error } = data; - const errorContent = this.domParser.parseFromString(htmlContents.errorContent(message), 'text/html'); - errorContent.getElementById('error-code').innerText = error || "UNKNOWN_ERROR"; - - // Add event listeners for buttons - const cancelRetryButton = errorContent.getElementById('cancel-retry-button'); - const cancelRetry = () => { - ipcRenderer.send(apiName, { - cmd: apiCmds.cancelNetworkStatusCheck - }); - cancelRetryButton.classList.add('disabled'); - cancelRetryButton.removeEventListener('click', cancelRetry); - }; - cancelRetryButton.addEventListener('click', cancelRetry); - - const quitButton = errorContent.getElementById('quit-button'); - quitButton.addEventListener('click', () => { - ipcRenderer.send(apiName, { - cmd: apiCmds.quitWindow - }); - }); - - const mainFrame = errorContent.getElementById('main-frame'); - document.body.appendChild(mainFrame); - } -} - -module.exports = { - NetworkError, -}; diff --git a/js/networkError/style.css b/js/networkError/style.css deleted file mode 100644 index 527f74a3..00000000 --- a/js/networkError/style.css +++ /dev/null @@ -1,77 +0,0 @@ -body { - background-color: rgb(247, 247, 247); - color: rgb(100, 100, 100); - margin: 0; - display: flex; -} - -a { - color: rgb(17, 85, 204); - text-decoration: none; -} - -.NetworkError-header { - color: #333; - font-size: 1.6em; - font-weight: normal; - line-height: 1.25em; - margin-bottom: 16px; - margin-top: 0; - word-wrap: break-word; -} - -.NetworkError-icon { - background-repeat: no-repeat; - background-size: 100%; - height: 72px; - margin: 0 0 40px; - width: 72px; - -webkit-user-select: none; - display: inline-block; -} - -.NetworkError-error-code { - color: #646464; - text-transform: uppercase; - display: block; - font-size: .8em; - margin-top: 10px; -} - -.content-wrapper { - box-sizing: border-box; - line-height: 1.6em; - margin: 14vh auto 0; - max-width: 600px; - width: 100%; - -webkit-text-size-adjust: 100%; - font-size: 100%; - font-family: sans-serif, "Segoe UI", "Helvetica Neue", Arial; -} - -.NetworkError-reason { - display: inline; -} - -.NetworkError-button { - font-weight: 600; - margin: 20px 0; - text-transform: uppercase; - transform: translatez(0); - background: rgb(66, 133, 244); - border: 0; - border-radius: 2px; - box-sizing: border-box; - color: #fff; - cursor: pointer; - font-size: .875em; - padding: 10px 24px; - transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1); - user-select: none; -} - -.disabled { - cursor: not-allowed; - background: #cccccc; - color: #666666; -} diff --git a/js/notify/AnimationQueue.js b/js/notify/AnimationQueue.js deleted file mode 100644 index bfbdf8ca..00000000 --- a/js/notify/AnimationQueue.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); - -/** - * Manages one animation at a time - * @param options - * @constructor - */ -const AnimationQueue = function(options) { - this.options = options; - this.queue = []; - this.running = false; -}; - -/** - * Pushes each animation to a queue - * @param object - */ -AnimationQueue.prototype.push = function(object) { - if (this.running) { - this.queue.push(object); - } else { - this.running = true; - setTimeout(this.animate.bind(this, object), 0); - } -}; - -/** - * Animates an animation that is part of the queue - * @param object - */ -AnimationQueue.prototype.animate = function(object) { - object.func.apply(null, object.args) - .then(function() { - if (this.queue.length > 0) { - // Run next animation - this.animate.call(this, this.queue.shift()); - } else { - this.running = false; - } - }.bind(this)) - .catch(function(err) { - log.send(logLevels.ERROR, 'animationQueue: encountered an error: ' + err + - ' with stack trace:' + err.stack); - /* eslint-disable no-console */ - console.error('animation queue encountered an error: ' + err + - ' with stack trace:' + err.stack); - /* eslint-enable no-console */ - }) -}; - -/** - * Clears the queue - */ -AnimationQueue.prototype.clear = function() { - this.queue = []; -}; - -module.exports = AnimationQueue; diff --git a/js/notify/assets/symphony-logo-black.png b/js/notify/assets/symphony-logo-black.png deleted file mode 100644 index 8ba73198eb7bac6f09794d954670a9763008e387..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1768 zcmVP)@ydYN$h{lSMrO^6H2rcsO)E_4CTx)nD#>#*lK-DWSD}?|trl z`Ml@LifC?h(%DO=gIP2t=}gi&Z9=G~#vq+T_P11U#{O;tpe!&X5K%hAWdv+~lIxlU6YBdNzgwL-n z?mvVDB>*U~6{<-9$nU$B&*;ON#&rnPGyt=lKxZ0ElVt^9s*wVVhP>BJXHb_wO#^Ul z34n*s@v;IiV}4u}fKVfg+-esP6|KO-eLKoU={BE)sqUcZl|CCjLK4#W%A!l$M0yP1EivZ?k)ccW! zH4KqGrppF^RQM5gJjM=&8Bu(wVKM$PHAJMq+lbmy@dFFywr0uu9c;ouUi|1305b&g zCV@wq^A5frTKX;Z4+0Ad+`@B4>3nID1vXK2?JB;j!}Mm%&uyzdQ6cwtp!f(D=))R4 zT>yHxn9|M&5nGqN{fW;lO8`887+0`BvYb5tFu|sNX7O2s)?6z=H2JqWna8u8Z{!@hn}789_!V``?OXN_j3-`P&&mLjrKb1V2mXsrcB+ zqF&srWM}43et^a2h5$Uq=NnCve*y*NGhSq@cLCj`?8k!j5nDMaDac@N`{{gzzjuIk zQ5Nx`{FY(1A>OYO`DL2?4Hk2VK$jHA(_yhC6M!#J&o(B%k1U305P_=CFV_jG{#P?ve)|LW5Vo-0#Fu8pQwF}arnmaMTp$6^saQ{g`Zr{av`$YsNXOMM!SuW5Q)!w$q zd!9ql0o+2(xd#BOEN)QHj7oC*o~Zt2Xu`Qoio1_frh$!>9ZVKQF^Luoel>SJ-74*uF{UuekOQSxM&wsDpbYklm8abVhY{mAA>ZI9EJo=}z7KRiwZKW*=NYz76v|LX+aXa;+5ZCq{_!8X^&jl?OR~yuh^ql<(sD9{}zG?p7-R z3oJ4h|3BqHiaQ00)3v|g{?{cPA(O816kv{-`5&8DfUXNrgu4L;J!hV zg@R(J5qszroN-z=IjHafN^fZFxVpm+vGZpdJwY00=n;Icwz5kqP@|)>CI=+Otjc;W z^xhj%MyPVQxoTHgTo(WWWvxkbV!&Lj%E73qxO%rYxG5%RGfr6>Y=eO?*Kh&u3O(E( zTDI&y8!HXf0S**cV}WYI%3r^PW5|1U@qMqx_E=-T7g!)z9*L`Wg4A9F@^#~LocQyuj$Z$C+fAB+Dm!-(08XYxcNG9)eY~rDrvMPF6dnl6 zM((l#K%FI-4u7_3?RAz^dFmNh|9Gmigo^XV>b@-}SOx&7IqHe*Hk1-tp0EA4L3!gp0R{kzWc#B99SHCM0000< KMNUMnLSTX{Bsp&Y diff --git a/js/notify/assets/symphony-logo-white.png b/js/notify/assets/symphony-logo-white.png deleted file mode 100644 index 1a48419221975f777f181fcb6d1eaec908daca02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1796 zcmV+f2mAPmP)jE-pjE+Y)ic03{TlB$o`pR9q(F zT&fHa5q9r@3&gk1rkbj0OGoFj31bc*kY5f612 zb@d4V^o?oSYNg^l!r*0QzKa3i3D<7Tc}BPf7X<+AqPV0M03a8LY{h^k8Ne(U)Ci;_ z0QhKYK}`b?*JOZ8zEAPsQUR#!0!WV|o4fxR07Mx@XuA7!94{FF4qTmz!S85W4K@mZ zwD`Uu{at|eXnRdfGl1QoXL!#T!7us~05A%KV)uI}qJncQrpa1>gC9}3hw6}zo!H%2 z;rprW;MW)c5HGGIVI8T^N$_vi1^03hbb^w+8~_lwCZKx)3Wu^EgAuM`nj0`CIM+Jc zXE2<6_6~tA0=>hE(@miJ;~H!6#Qj?%09bX28-ogT_u;eKI+7LapmiK-Hel%UvslaC zgaNk{c|qfyLHda#v2&_PCQ5y&DTs(0J~9V0Zz8_W6;W3O$PW>9X|lQa?iLg4l)Mkv zxw{&>-$iUVR$$-{2(2v|C>t>HR#yj1AUeozR-hm)<_8#2211N`Mmpn5bbnxA{0uu! zyZn=xgG~v*=%Eq-Y4;COD{2oQsFo)t%n@8vAnf4|v$C-Y`fGK8X?}4opcklFy2=Jk ztS8}^pkjcR=XW%rIM+lg&}%_Kro=6{VR>grV;3%T)ExUA;~&HtcZ%T$u%MR<0E`)_ zei*>qW6T6Vj1=);1JyUHp!-8rpcAJLzb0nhYoyu}Vu4r0fEyjZgIf?2)|k+TTXb{A zhM3PQ0xBo?DRn>Gbb-w}5HodDBF3B~J3jzG6UMsrHl;@LH%gPTTjY$}p%r$gkx4vN z(*yH!4Y4Z9?TUlS>5jY^Spba7O^Brx?xlSDS5CCu;Odv;Zx>CEG|LPWPZQ+!RTXsz zJ(D`pBf6_hi>i`jCDtJ4uJP47?SRGZ5rn2Dx*xl>kaK&@!YKLx05>S=HXLw&Ry%kS zQ$nz$S3v)mcNT#zH@Dkt$jAU#>9tAPVTlM$T3`XAH~1Yt6Ay-1Lm(9~-&MK2#u3Y` zX8^jk0LZw-T9U>@<0*n>Rw|F-nnY)>nZ_I6NpeO=pM#< zaE%y2_eEOF5uk7|e#i-i9mE2Dgg%fy%g47OduUEE&j$c{RRf@|ThPu^MJDwMoJ=!> zgWv&R^x+Epu0Y>;-%d}ln`G#-REged^c<7Ca`;=t2NtHs4ot`jqmHE4Cz-`^N9hIZ zc`C{T!iKk|Yl$ss9sq7hd#b79fSpY%W+is7dC9mp#=OPv z=6FEw0|AIobFHCG09fPqOvFfz;crb+eDA{e2xC|(lW3Z9Lq&XMMLC9eN+edK1@(lH zrqKaFexMm^7(RqiL)mbt+?_%4?&U~LQkm}+*AtpB34qxHxH0aZA*BYHO&Lm?W(NRT z1dt!7nk6d3rNhs3i=3RQae>`l$-9iXjsTGbl`H-pgB2%4|I~!3x-lSvP}x>AoST+C zD7-|W(zZxpv%KHKy(FskSd$0wGpo{$*99O1+9M7`x`s<*J)J;CxgWkyuJEg9POyVn z!9mk?4BsKp0swkGL2E13HCzfzKH|qt^Y_T-2k`aMSK=U;2)LbTufXK}fJYcTM?j>0 z+)_iZ_{i}a!^LgOx=01AaLxt5KiKgDy0CkVjNk#ZooPLfU9ZwrK8p}ybeD$nbCZ*d z=$P&hT#viT;U&HzvqFlZ;Pf~8gd@W__J|+8&hts*QoV`t4!L_G)%=`VuaO$ps@;`Z zWKXt=vPJ>ONr~J8?CJm@#giH0NzixYT zMYeFA*;ix$yU+pB-2HL@uvB*be*vTW - - -
-
- -
- -
- -
-
- - - - -
-
- - diff --git a/js/notify/electron-notify.js b/js/notify/electron-notify.js deleted file mode 100644 index 7f357f0a..00000000 --- a/js/notify/electron-notify.js +++ /dev/null @@ -1,856 +0,0 @@ -'use strict'; -// -// code here adapted from https://www.npmjs.com/package/electron-notify -// made following changes: -// - place notification in corner of screen -// - notification color -// - notification flash/blink -// - custom design for symphony notification style -// - if screen added/removed or size change then close all notifications -// -const path = require('path'); -const fs = require('fs'); -const electron = require('electron'); -const asyncMap = require('async.map'); -const asyncMapSeries = require('async.mapseries'); -const app = electron.app; -const BrowserWindow = electron.BrowserWindow; -const ipc = electron.ipcMain; -const { isMac, isNodeEnv } = require('../utils/misc'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const i18n = require('../translation/i18n'); -// maximum number of notifications that can be queued, after limit is -// reached then error func callback will be invoked. -const MAX_QUEUE_SIZE = 30; - -let AnimationQueue = require('./AnimationQueue.js'); - -// Array of windows with currently showing notifications -let activeNotifications = []; - -// Recycle windows -let inactiveWindows = []; - -// If we cannot show all notifications, queue them -let notificationQueue = []; - -// To prevent executing mutliple animations at once -let animationQueue = new AnimationQueue(); - -// To prevent double-close notification window -let closedNotifications = {}; - -// Give each notification a unique id -let latestID = 0; - -let nextInsertPos = {}; -let externalDisplay; -// user selected display id for notification -let displayId; - -let sandboxed = false; - -let config = { - // corner to put notifications - // upper-right, upper-left, lower-right, lower-left - startCorner: 'upper-right', - width: 380, - height: 100, - borderRadius: 5, - displayTime: 5000, - animationSteps: 5, - animationStepMs: 5, - animateInParallel: true, - pathToModule: '', - logging: true, - defaultStyleContainer: { - display: 'flex', - justifyContent: 'center', - backgroundColor: '#f0f0f0', - overflow: 'hidden', - position: 'relative', - lineHeight: '15px', - boxSizing: 'border-box' - }, - defaultStyleHeader: { - width: 245, - minWidth: 230, - margin: 'auto', - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - }, - defaultStyleImageContainer: { - alignItems: 'center', - display: 'flex', - }, - defaultStyleImage: { - height: 43, - borderRadius: 4, - width: 43 - }, - defaultStyleClose: { - width: 16, - height: 80, - display: 'flex', - margin: 'auto', - opacity: 0.54, - fontSize: 12, - color: '#CCC' - }, - defaultStyleTitle: { - fontFamily: 'sans-serif', - fontSize: 14, - fontWeight: 700, - color: '#4a4a4a', - overflow: 'hidden', - display: '-webkit-box', - webkitLineClamp: 1, - webkitBoxOrient: 'vertical', - }, - defaultStyleCompany: { - fontFamily: 'sans-serif', - fontSize: 11, - color: '#adadad', - overflow: 'hidden', - filter: 'brightness(70%)', - display: '-webkit-box', - webkitLineClamp: 1, - webkitBoxOrient: 'vertical', - }, - defaultStyleText: { - fontFamily: 'sans-serif', - fontSize: 12, - color: '#4a4a4a', - marginTop: 5, - overflow: 'hidden', - display: '-webkit-box', - webkitLineClamp: 3, - webkitBoxOrient: 'vertical', - cursor: 'default', - textOverflow: 'ellipsis', - width: '100%', - overflowWrap: 'break-word', - }, - defaultStyleLogoContainer: { - display: 'flex', - alignItems: 'center', - }, - defaultStyleLogo: { - marginLeft: '5px', - opacity: 0.6, - width: '43px', - }, - defaultWindow: { - alwaysOnTop: true, - skipTaskbar: true, - resizable: false, - show: false, - frame: false, - transparent: true, - acceptFirstMouse: true, - webPreferences: { - preload: path.join(__dirname, 'electron-notify-preload.js'), - sandbox: sandboxed, - nodeIntegration: isNodeEnv, - devTools: false - } - } -}; - -if (app.isReady()) { - setup(); -} else { - app.on('ready', setup); -} - -/** - * Method to update notification config - * @param customConfig - */ -function updateConfig(customConfig) { - // Fetching user preferred notification position from config - if (customConfig.position) { - config = Object.assign(config, {startCorner: customConfig.position}); - - calcDimensions(); - } - - // Fetching user preferred notification screen from config - if (customConfig.display) { - displayId = customConfig.display; - } - // Reposition active notification on config changes - setupConfig(); - moveOneDown(0) - .then(() => { - log.send(logLevels.INFO, 'updateConfig: repositioned '+ activeNotifications.length +' active notification'); - }); -} - -/** - * Method to setup the notification configuration - */ -function setup() { - setupConfig(); - - // if display added/removed/changed then re-run setup and remove all existing - // notifications. - electron.screen.on('display-added', setupConfig); - electron.screen.on('display-removed', setupConfig); - electron.screen.on('display-metrics-changed', setupConfig); -} - -/** - * Method to get the notification template path - * @returns {string|*} - */ -function getTemplatePath() { - let templatePath = path.join(__dirname, 'electron-notify.html'); - try { - fs.statSync(templatePath).isFile(); - } catch (err) { - log.send(logLevels.ERROR, 'electron-notify: Could not find template ("' + templatePath + '").'); - } - config.templatePath = 'file://' + templatePath; - return config.templatePath; -} - -/** - * Calculates the dimensions of the screen - */ -function calcDimensions() { - const vertSpaceBetweenNotf = 8; - - // Calc totalHeight & totalWidth - config.totalHeight = config.height + vertSpaceBetweenNotf; - config.totalWidth = config.width; - - let firstPosX, firstPosY; - switch (config.startCorner) { - case 'upper-right': - firstPosX = config.corner.x - config.totalWidth; - firstPosY = config.corner.y; - break; - case 'lower-right': - firstPosX = config.corner.x - config.totalWidth; - firstPosY = config.corner.y - config.totalHeight; - break; - case 'lower-left': - firstPosX = config.corner.x; - firstPosY = config.corner.y - config.totalHeight; - break; - case 'upper-left': - default: - firstPosX = config.corner.x; - firstPosY = config.corner.y; - break; - } - - // Calc pos of first notification: - config.firstPos = { - x: firstPosX, - y: firstPosY - }; - - // Set nextInsertPos - nextInsertPos.x = config.firstPos.x; - nextInsertPos.y = config.firstPos.y -} - -/** - * Setup the notification config - */ -function setupConfig() { - - log.send(logLevels.INFO, `Either a display was added / removed / the metrics were changed`); - - // This feature only applies to windows - if (!isMac) { - let screens = electron.screen.getAllDisplays(); - if (screens && screens.length >= 0) { - externalDisplay = screens.find((screen) => { - let screenId = screen.id.toString(); - return screenId === displayId; - }); - } - } - - let display = externalDisplay ? externalDisplay : electron.screen.getPrimaryDisplay(); - config.corner = {}; - config.corner.x = display.workArea.x; - config.corner.y = display.workArea.y; - - // update corner x/y based on corner of screen where notf should appear - const workAreaWidth = display.workAreaSize.width; - const workAreaHeight = display.workAreaSize.height; - switch (config.startCorner) { - case 'upper-right': - config.corner.x += workAreaWidth; - break; - case 'lower-right': - config.corner.x += workAreaWidth; - config.corner.y += workAreaHeight; - break; - case 'lower-left': - config.corner.y += workAreaHeight; - break; - case 'upper-left': - default: - // no change needed - break; - } - - calcDimensions(); - - // Maximum amount of Notifications we can show: - config.maxVisibleNotifications = Math.floor(display.workAreaSize.height / config.totalHeight); -} - -/** - * Notifies the user - * @param notification - * @returns {*} - */ -function notify(notification) { - // Is it an object and only one argument? - if (arguments.length === 1 && typeof notification === 'object') { - let notf = Object.assign({}, notification); - // Use object instead of supplied parameters - notf.id = latestID; - incrementId(); - animationQueue.push({ - func: showNotification, - args: [ notf ] - }); - return notf.id - } - log.send(logLevels.ERROR, 'electron-notify: ERROR notify() only accepts a single object with notification parameters.'); - return null; -} - -/** - * Increment the notification - */ -function incrementId() { - latestID++; -} - -/** - * Shows the notification to the user - * @param notificationObj - * @returns {Promise} - */ -function showNotification(notificationObj) { - return new Promise(function(resolve) { - - if (notificationQueue.length >= MAX_QUEUE_SIZE) { - if (typeof notificationObj.onErrorFunc === 'function') { - setTimeout(function() { - notificationObj.onErrorFunc({ - id: notificationObj.id, - error: 'max notification queue size reached: ' + MAX_QUEUE_SIZE - }); - log.send(logLevels.INFO, 'showNotification: max notification queue size reached: ' + MAX_QUEUE_SIZE); - }, 0); - } - resolve(); - return; - } - - // check if tag id provided. should replace existing notification - // if has same grouping id. - let tag = notificationObj.tag; - if (tag) { - // first check queued notifications - for(let i = 0; i < notificationQueue.length; i++) { - if (tag === notificationQueue[ i ].tag) { - let existingNotfObj = notificationQueue[ i ]; - // be sure to call close event for existing, so it gets - // cleaned up. - if (typeof existingNotfObj.onCloseFunc === 'function') { - existingNotfObj.onCloseFunc({ - event: 'close', - id: notificationObj.id - }); - } - // update with new notf - notificationQueue[ i ] = notificationObj; - resolve(); - return; - } - } - - // next check notfs being shown - for(let i = 0; i < activeNotifications.length; i++) { - if (activeNotifications[ i ] && windowExists(activeNotifications[ i ])) { - let existingNotfyObj = activeNotifications[ i ].notfyObj; - if (existingNotfyObj && tag === existingNotfyObj.tag) { - let notificationWindow = activeNotifications[ i ]; - - // be sure to call close event for existing, so it gets - // cleaned up. - if (notificationWindow.electronNotifyOnCloseFunc) { - notificationWindow.electronNotifyOnCloseFunc({ - event: 'close', - id: existingNotfyObj.id - }); - delete notificationWindow.electronNotifyOnCloseFunc; - } - setNotificationContents(notificationWindow, notificationObj); - resolve(); - return; - } - } - } - } - - // Can we show it? - if (activeNotifications.length < config.maxVisibleNotifications) { - // Get inactiveWindow or create new: - getWindow().then(function(notificationWindow) { - // Move window to position - calcInsertPos(); - setWindowPosition(notificationWindow, nextInsertPos.x, nextInsertPos.y); - - let updatedNotfWindow = setNotificationContents(notificationWindow, notificationObj); - - activeNotifications.push(updatedNotfWindow); - - resolve(updatedNotfWindow); - }) - } else { - // Add to notificationQueue - notificationQueue.push(notificationObj); - resolve(); - } - }) -} - -/** - * Sets the HTML notification contents along with other options - * @param notfWindow - * @param notfObj - * @returns {*} - */ -function setNotificationContents(notfWindow, notfObj) { - - // Display time per notification basis. - let displayTime = notfObj.displayTime ? notfObj.displayTime : config.displayTime; - - let browserWindows = BrowserWindow.getAllWindows(); - const mainWindow = browserWindows.find((window) => { return window.winName === 'main' }); - if (mainWindow && windowExists(mainWindow)) { - if (mainWindow.isAlwaysOnTop()) { - notfWindow.setAlwaysOnTop(true); - } - } - - if (notfWindow.displayTimer) { - clearTimeout(notfWindow.displayTimer); - } - - const updatedNotificationWindow = notfWindow; - - updatedNotificationWindow.notfyObj = notfObj; - - let timeoutId; - let closeFunc = buildCloseNotification(notfWindow, notfObj, function() { - return timeoutId - }); - let closeNotificationSafely = buildCloseNotificationSafely(closeFunc); - - if (!notfObj.sticky) { - timeoutId = setTimeout(function() { - closeNotificationSafely('timeout'); - }, displayTime); - updatedNotificationWindow.displayTimer = timeoutId; - } - - // Trigger onShowFunc if existent - if (notfObj.onShowFunc) { - notfObj.onShowFunc({ - event: 'show', - id: notfObj.id, - closeNotification: closeNotificationSafely - }) - } - - // Save onClickFunc in notification window - if (notfObj.onClickFunc) { - updatedNotificationWindow.electronNotifyOnClickFunc = notfObj.onClickFunc - } else { - delete updatedNotificationWindow.electronNotifyOnClickFunc; - } - - if (notfObj.onCloseFunc) { - updatedNotificationWindow.electronNotifyOnCloseFunc = notfObj.onCloseFunc - } else { - delete updatedNotificationWindow.electronNotifyOnCloseFunc; - } - // Set strings about i18n - const translation = {}; - translation.close = i18n.getMessageFor('Close'); - - const windowId = notfWindow.id; - - // Set contents, ... - updatedNotificationWindow.webContents.send('electron-notify-set-contents', - Object.assign({ windowId: windowId, i18n: translation}, notfObj)); - // Show window - updatedNotificationWindow.showInactive(); - - return updatedNotificationWindow; -} - -/** - * Closes the notification - * @param notificationWindow - * @param notificationObj - * @param getTimeoutId - * @returns {Function} - */ -function buildCloseNotification(notificationWindow, notificationObj, getTimeoutId) { - return function(event) { - - // safety check to prevent from using an - // already destroyed notification window - if (!notificationWindow || typeof notificationWindow.isDestroyed !== 'function' || notificationWindow.isDestroyed()) { - return new Promise(function(exitEarly) { exitEarly() }) - } - - if (closedNotifications[notificationObj.id]) { - delete closedNotifications[notificationObj.id]; - return new Promise(function(exitEarly) { exitEarly() }); - } - closedNotifications[notificationObj.id] = true; - - - if (notificationWindow.electronNotifyOnCloseFunc) { - notificationWindow.electronNotifyOnCloseFunc({ - event: event, - id: notificationObj.id - }); - // ToDo: fix this: shouldn't delete method on arg - /* eslint-disable */ - delete notificationWindow.electronNotifyOnCloseFunc; - /* eslint-enable */ - } - - // reset content - notificationWindow.webContents.send('electron-notify-reset'); - if (getTimeoutId && typeof getTimeoutId === 'function') { - let timeoutId = getTimeoutId(); - clearTimeout(timeoutId); - } - - // Recycle window - let pos = activeNotifications.indexOf(notificationWindow); - activeNotifications.splice(pos, 1); - inactiveWindows.push(notificationWindow); - - // Hide notification - notificationWindow.hide(); - - checkForQueuedNotifications(); - - // Move notifications down - return moveOneDown(pos); - } -} - -/** - * Adds an active notification the close notification queue - * Always add to animationQueue to prevent erros (e.g. notification - * got closed while it was moving will produce an error) - * @param closeFunc - * @returns {Function} - */ -function buildCloseNotificationSafely(closeFunc) { - return function(reason) { - animationQueue.push({ - func: closeFunc, - args: [ reason || 'closedByAPI' ] - }); - } -} - -ipc.on('electron-notify-close', function (event, winId, notificationObj) { - log.send(logLevels.INFO, `Closing notification for ${winId}}`); - let closeFunc = buildCloseNotification(BrowserWindow.fromId(winId), notificationObj); - buildCloseNotificationSafely(closeFunc)('close'); -}); - -ipc.on('electron-notify-click', function (event, winId, notificationObj) { - log.send(logLevels.INFO, `Notification click event triggered for ${winId}}`); - let notificationWindow = BrowserWindow.fromId(winId); - if (notificationWindow && notificationWindow.electronNotifyOnClickFunc) { - let closeFunc = buildCloseNotification(notificationWindow, notificationObj); - notificationWindow.electronNotifyOnClickFunc({ - event: 'click', - id: notificationObj.id, - closeNotification: buildCloseNotificationSafely(closeFunc) - }); - delete notificationWindow.electronNotifyOnClickFunc; - } -}); - -/** - * Checks for queued notifications and add them - * to AnimationQueue if possible - */ -function checkForQueuedNotifications() { - if (notificationQueue.length > 0 && - activeNotifications.length < config.maxVisibleNotifications) { - // Add new notification to animationQueue - animationQueue.push({ - func: showNotification, - args: [ notificationQueue.shift() ] - }) - } -} - -/** - * Moves the notifications one position down, - * starting with notification at startPos - * @param startPos - * @returns {Promise} - */ -function moveOneDown(startPos) { - return new Promise(function(resolve) { - if (startPos >= activeNotifications || startPos === -1) { - resolve(); - return - } - // Build array with index of affected notifications - let notificationPosArray = []; - for (let i = startPos; i < activeNotifications.length; i++) { - notificationPosArray.push(i) - } - // Start to animate all notifications at once or in parallel - let asyncFunc = asyncMap; // Best performance - if (config.animateInParallel === false) { - asyncFunc = asyncMapSeries // Sluggish - } - asyncFunc(notificationPosArray, moveNotificationAnimation, function() { - resolve() - }) - }) -} - -/** - * Moves the notification animation - * @param i - * @param done - */ -function moveNotificationAnimation(i, done) { - // Get notification to move - let notificationWindow = activeNotifications[i]; - - // Calc new y position - let newY; - switch(config.startCorner) { - case 'upper-right': - case 'upper-left': - newY = config.corner.y + (config.totalHeight * i); - break; - default: - case 'lower-right': - case 'lower-left': - newY = config.corner.y - (config.totalHeight * (i + 1)); - break; - } - - // Get startPos, calc step size and start animationInterval - let startY = notificationWindow.getPosition()[1]; - let step = (newY - startY) / config.animationSteps; - let curStep = 1; - let animationInterval = setInterval(function() { - // Abort condition - if (curStep === config.animationSteps) { - setWindowPosition(notificationWindow, config.firstPos.x, newY); - clearInterval(animationInterval); - done(null, 'done'); - return; - } - // Move one step down - setWindowPosition(notificationWindow, config.firstPos.x, startY + curStep * step); - curStep++ - }, config.animationStepMs) -} - -/** - * Sets the window's position - * @param browserWin - * @param posX - * @param posY - */ -function setWindowPosition(browserWin, posX, posY) { - if (browserWin && windowExists(browserWin)) { - browserWin.setPosition(parseInt(posX, 10), parseInt(posY, 10)) - } -} - -/** - * Find next possible insert position (on top) - */ -function calcInsertPos() { - if (activeNotifications.length < config.maxVisibleNotifications) { - switch(config.startCorner) { - case 'upper-right': - case 'upper-left': - nextInsertPos.y = config.corner.y + (config.totalHeight * activeNotifications.length); - break; - - default: - case 'lower-right': - case 'lower-left': - nextInsertPos.y = config.corner.y - (config.totalHeight * (activeNotifications.length + 1)); - break; - } - } -} - -/** - * Get a window to display a notification. Use inactiveWindows or - * create a new window - * @returns {Promise} - */ -function getWindow() { - return new Promise(function(resolve) { - let notificationWindow; - // Are there still inactiveWindows? - if (inactiveWindows.length > 0) { - notificationWindow = inactiveWindows.pop(); - resolve(notificationWindow) - } else { - // Or create a new window - let windowProperties = config.defaultWindow; - windowProperties.width = config.width; - windowProperties.height = config.height; - notificationWindow = new BrowserWindow(windowProperties); - notificationWindow.winName = 'notification-window'; - notificationWindow.setVisibleOnAllWorkspaces(true); - notificationWindow.loadURL(getTemplatePath()); - notificationWindow.webContents.on('did-finish-load', function() { - // Done - notificationWindow.webContents.send('electron-notify-load-config', config); - resolve(notificationWindow) - }); - - notificationWindow.once('closed', cleanUpActiveNotification); - } - }) -} - -/** - * Once a minute, remove inactive windows to free up memory used. - */ -setInterval(cleanUpInactiveWindow, 60000); - -function cleanUpActiveNotification(event) { - - if (!event || !event.sender) { - return null; - } - - let pos = activeNotifications.indexOf(event.sender); - if (pos !== -1) { - activeNotifications.splice(pos, 1); - return moveOneDown(pos); - } - - return null; -} - -/** - * Cleans up inactive windows - */ -function cleanUpInactiveWindow() { - inactiveWindows.forEach(function(window) { - if (window && windowExists(window)) { - window.close(); - } - }); - inactiveWindows = []; -} - -/** - * Closes all the notification windows - * and cleans up the reference variables - */ -function closeAll() { - resetAnimationQueue(); - const notificationWin = Object.assign([], activeNotifications); - for (let activeNotification of notificationWin) { - if (activeNotification && windowExists(activeNotification)) { - activeNotification.close(); - } - } - - nextInsertPos = {}; - activeNotifications = []; - notificationQueue = []; - cleanUpInactiveWindow(); -} - -/** - * Resets the animation queue instance - */ -function resetAnimationQueue() { - animationQueue = new AnimationQueue(); -} - -/** - * Start a new timer to close the notification - * @param event - * @param winId - * @param notificationObj - */ -function onMouseLeave(event, winId, notificationObj) { - if (winId) { - log.send(logLevels.INFO, `Mouse was removed from the notification ${winId}`); - const notificationWindow = BrowserWindow.fromId(winId); - if (notificationWindow && windowExists(notificationWindow)) { - notificationWindow.displayTimer = setTimeout(function () { - let closeFunc = buildCloseNotification(BrowserWindow.fromId(winId), notificationObj); - buildCloseNotificationSafely(closeFunc)('close'); - }, 3000); - } - } -} - -/** - * Clears the timer for a specific notification window - * @param event - * @param winId - */ -function onMouseOver(event, winId) { - if (winId) { - log.send(logLevels.INFO, `Mouse hover on notification ${winId}`); - const notificationWindow = BrowserWindow.fromId(winId); - if (notificationWindow) { - clearTimeout(notificationWindow.displayTimer); - } - } -} - -/** - * Verifies if the passed window is valid and exists - * @param browserWindow {Electron.BrowserWindow} - */ -function windowExists(browserWindow) { - return !!browserWindow && typeof browserWindow.isDestroyed === 'function' && !browserWindow.isDestroyed(); -} - -// capturing mouse events -ipc.on('electron-notify-mouseleave', onMouseLeave); -ipc.on('electron-notify-mouseover', onMouseOver); - - -module.exports.notify = notify; -module.exports.updateConfig = updateConfig; -module.exports.reset = setupConfig; -module.exports.closeAll = closeAll; -module.exports.resetAnimationQueue = resetAnimationQueue; diff --git a/js/notify/notifyImpl.js b/js/notify/notifyImpl.js deleted file mode 100644 index b1b5782f..00000000 --- a/js/notify/notifyImpl.js +++ /dev/null @@ -1,246 +0,0 @@ -'use strict'; - -const EventEmitter = require('events'); -const { notify } = require('./electron-notify.js'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -/** - * implementation for notifications interface, - * wrapper around electron-notify. - */ -class Notify { - /** - * Dislays a notifications - * - * @param {String} title Title of notification - * @param {Object} options { - * body {string} main text to display in notifications - * image {string} url of image to show in notification - * icon {string} url of image to show in notification - * flash {bool} true if notification should flash (default false) - * color {string} background color for notification - * tag {string} non-empty string to unique identify notf, if another - * notification arrives with same tag then it's content will - * replace existing notification. - * sticky {bool} if true notification will stay until user closes. default - * is false. - * data {object} arbitrary object to be stored with notification - * } - */ - constructor(title, options) { - log.send(logLevels.INFO, 'creating notification text'); - - let emitter = new EventEmitter(); - this.emitter = Queue(emitter); - - let message = options.body; - - this._id = notify({ - title: title, - text: message || '', - image: options.image || options.icon, - flash: options.flash, - color: options.color, - tag: options.tag, - sticky: options.sticky || false, - company: options.company, - onShowFunc: onShow.bind(this), - onClickFunc: onClick.bind(this), - onCloseFunc: onClose.bind(this), - onErrorFunc: onError.bind(this) - }); - - log.send(logLevels.INFO, `created notification with id: ${this._id}`); - - this._data = options.data || null; - - /** - * Handles on show event - * @param arg - */ - function onShow(arg) { - if (arg.id === this._id) { - log.send(logLevels.INFO, 'showing notification, id=' + this._id); - this.emitter.queue('show', { - target: this - }); - this._closeNotification = arg.closeNotification; - } - } - - /** - * Handles on click event - * @param arg - */ - function onClick(arg) { - if (arg.id === this._id) { - log.send(logLevels.INFO, 'clicking notification, id=' + this._id); - this.emitter.queue('click', { - target: this - }); - } - } - - /** - * Handles on close event - * @param arg - */ - function onClose(arg) { - if (arg.id === this._id || arg.event === 'close-all') { - log.send(logLevels.INFO, 'closing notification, id=' + this._id); - this.emitter.queue('close', { - target: this - }); - this.destroy(); - } - } - - /** - * Handles on error event - * @param arg - */ - function onError(arg) { - if (arg.id === this._id) { - // don't raise error event if handler doesn't exist, node - // will throw an exception - log.send(logLevels.ERROR, 'error for notification, id=' + this._id + - ' error=' + (arg && arg.error)); - if (this.emitter.eventNames().includes('error')) { - this.emitter.queue('error', arg.error || 'notification error'); - } - this.destroy(); - } - } - - } - - /** - * Closes notification - */ - close() { - if (typeof this._closeNotification === 'function') { - this._closeNotification('close'); - } - this.destroy(); - } - - /** - * Always allow showing notifications. - * @return {string} 'granted' - */ - static get permission() { - return 'granted'; - } - - /** - * Returns data object passed in via constructor options - */ - get data() { - return this._data; - } - - /** - * Adds event listeners for 'click', 'close', 'show', 'error' events - * - * @param {String} event event to listen for - * @param {func} cb callback invoked when event occurs - */ - addEventListener(event, cb) { - if (event && typeof cb === 'function') { - this.emitter.on(event, cb); - } - } - - /** - * Removes event listeners for 'click', 'close', 'show', 'error' events - * - * @param {String} event event to stop listening for. - * @param {func} cb callback associated with original addEventListener - */ - removeEventListener(event, cb) { - if (event && typeof cb === 'function') { - this.emitter.removeListener(event, cb); - } - } - - /** - * Removes all event listeners - */ - removeAllEvents() { - this.destroy(); - } - - // - // private stuff below here - // - destroy() { - this.emitter.removeAllListeners(); - } - -} - -/** - * Allow emitter events to be queued before addEventListener called. - * Code adapted from: https://github.com/bredele/emitter-queue - * - * @param {Object} emitter Instance of node emitter that will get augmented. - * @return {Object} Modified emitter - */ -function Queue(emitter) { - /** - * Cache emitter on. - * @api private - */ - const cache = emitter.on; - let modifiedEmitter = emitter; - /** - * Emit event and store it if no - * defined callbacks. - * example: - * - * .queue('message', 'hi'); - * - * @param {String} topic - */ - modifiedEmitter.queue = function(topic) { - this._queue = this._queue || {}; - this._callbacks = this._callbacks || {}; - if (this._callbacks[topic]) { - this.emit.apply(this, arguments); - } else { - (this._queue[topic] = this._queue[topic] || []) - .push([].slice.call(arguments, 1)); - } - }; - - /** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Event} - */ - modifiedEmitter.on = modifiedEmitter.addEventListener = function(topic, fn) { - this._queue = this._queue || {}; - const topics = this._queue[topic]; - cache.apply(this, arguments); - - if (!this._callbacks) { - this._callbacks = {}; - } - this._callbacks[topic] = true; - - if (topics) { - let i = 0; - const l = topics.length; - for(; i < l; i++) { - fn.apply(this, topics[i]); - } - delete this._queue[topic]; - } - }; - - return modifiedEmitter; -} - -module.exports = Notify; diff --git a/js/notify/settings/configure-notification-position-preload.js b/js/notify/settings/configure-notification-position-preload.js deleted file mode 100644 index 75e59c6b..00000000 --- a/js/notify/settings/configure-notification-position-preload.js +++ /dev/null @@ -1,117 +0,0 @@ -'use strict'; - -const electron = require('electron'); -const ipc = electron.ipcRenderer; - -let availableScreens; -let selectedPosition; -let selectedDisplay; - -renderSettings(); - -/** - * Method that renders the data from user config - */ -function renderSettings() { - - document.addEventListener('DOMContentLoaded', function () { - let okButton = document.getElementById('ok-button'); - let cancel = document.getElementById('cancel'); - - okButton.addEventListener('click', function () { - selectedPosition = document.querySelector('input[name="position"]:checked').value; - let selector = document.getElementById('screen-selector'); - selectedDisplay = selector.options[selector.selectedIndex].value; - - // update the user selected data and close the window - updateAndClose(); - }); - - cancel.addEventListener('click', function () { - ipc.send('close-alert'); - }); - - }); - -} - -/** - * Updates the configuration and closes the alert - */ -function updateAndClose() { - ipc.send('update-config', {position: selectedPosition, display: selectedDisplay}); - ipc.send('close-alert'); -} - -ipc.on('notificationSettings', (event, args) => { - // update position from user config - if (args && args.position) { - document.getElementById(args.position).checked = true; - } - - // update selected display from user config - if (args && args.display) { - if (availableScreens) { - let index = availableScreens.findIndex((item) => { - let id = item.id.toString(); - return id === args.display; - }); - if (index !== -1){ - let option = document.getElementById(availableScreens[index].id); - - if (option){ - option.selected = true; - } - } - } - } -}); - -ipc.on('screens', (event, screens) => { - availableScreens = screens; - let screenSelector = document.getElementById('screen-selector'); - - if (screenSelector && screens){ - - // clearing the previously added content to - // make sure the content is not repeated - screenSelector.innerHTML = ''; - - screens.forEach((scr, index) => { - let option = document.createElement('option'); - option.value = scr.id; - option.id = scr.id; - option.innerHTML = index + 1; - screenSelector.appendChild(option); - }); - } -}); - -ipc.on('i18n-notification-settings', (event, content) => { - if (content && typeof content === 'object') { - const i18nNodes = document.querySelectorAll('[data-i18n-text]'); - - for (let node of i18nNodes) { - if (node.attributes['data-i18n-text'] && node.attributes['data-i18n-text'].value) { - node.innerText = content[node.attributes['data-i18n-text'].value] || node.attributes['data-i18n-text'].value; - } - } - } -}); - -// note: this is a workaround until -// https://github.com/electron/electron/issues/8841 -// is fixed on the electron. where 'will-navigate' -// is never fired in sandbox mode -// -// This is required in order to prevent from loading -// dropped content -window.addEventListener('drop', function(e) { - e.preventDefault(); - e.stopPropagation(); -}); - -window.addEventListener('dragover', function(e) { - e.preventDefault(); - e.stopPropagation(); -}); \ No newline at end of file diff --git a/js/notify/settings/configure-notification-position.css b/js/notify/settings/configure-notification-position.css deleted file mode 100644 index c3b329c9..00000000 --- a/js/notify/settings/configure-notification-position.css +++ /dev/null @@ -1,91 +0,0 @@ -html, body { - margin: 0; - height: 100%; - font-family: sans-serif; -} -.content { - border-radius: 2px; - width: 100%; - flex: 0 0 auto; -} -.header { - display: flex; - align-items: center; - line-height: 1.3; - justify-content: space-between; - border-bottom: 1px solid rgba(0, 0, 0, .1); - margin-bottom: 20px; - padding: 16px; -} - -.header__title { - font-weight: 500; - font-style: normal; - margin: 0 0 0 4px; - font-size: 1.4rem; - min-height: 13px; - text-overflow: ellipsis; - text-align: center; - white-space: nowrap; - overflow: hidden; - width: 100%; - color: rgba(0, 0, 0, .8); -} - -.form { - width: 95%; - margin: 0 auto; -} - -.selector { - padding: 0 9px 0 16px; - cursor: pointer; - margin-left: 10px; -} - -.main { - display: flex; - flex-direction: row; - margin-bottom: 20px; - height: 100%; - border: 1px solid #ccc !important; - padding: 10px; -} - -.main .first-set { - flex-grow: 1; -} - -.main .second-set { - flex-grow: 1; - text-align: right; -} - -.radio { - line-height: 1.7; -} - -.radio__label { - cursor: pointer; -} - -.radio__label input { - cursor: pointer; -} - -.footer { - padding: 12px 10px; - border-top: 1px solid rgba(0, 0, 0, .1); - display: flex; - align-items: center; -} - -.buttonLayout { - margin-left: auto; - display: flex; - align-items: center; -} - -.buttonDismiss { - margin-right: 10px; -} \ No newline at end of file diff --git a/js/notify/settings/configure-notification-position.html b/js/notify/settings/configure-notification-position.html deleted file mode 100644 index afe3ad48..00000000 --- a/js/notify/settings/configure-notification-position.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - -
-
- -
-
-
- -
- - -
- -
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
-
- -
-
- - -
-
- - diff --git a/js/notify/settings/configure-notification-position.js b/js/notify/settings/configure-notification-position.js deleted file mode 100644 index 8bc1105c..00000000 --- a/js/notify/settings/configure-notification-position.js +++ /dev/null @@ -1,211 +0,0 @@ -'use strict'; - -const path = require('path'); -const fs = require('fs'); -const electron = require('electron'); -const BrowserWindow = electron.BrowserWindow; -const app = electron.app; -const ipc = electron.ipcMain; -const log = require('../../log.js'); -const logLevels = require('../../enums/logLevels.js'); -const notify = require('./../electron-notify'); -const eventEmitter = require('./../../eventEmitter'); -const { updateConfigField } = require('../../config'); -const i18n = require('../../translation/i18n'); -const { isMac } = require('../../utils/misc'); - -let configurationWindow; -let screens; -let position; -let display; -let sandboxed = false; - -let windowConfig = { - width: 460, - height: 360, - show: false, - modal: true, - autoHideMenuBar: true, - webPreferences: { - preload: path.join(__dirname, 'configure-notification-position-preload.js'), - sandbox: sandboxed, - nodeIntegration: false, - devTools: false - } -}; - -app.on('ready', () => { - screens = electron.screen.getAllDisplays(); - - // if display added/removed update the list of screens - electron.screen.on('display-added', updateScreens); - electron.screen.on('display-removed', updateScreens); -}); - -/** - * Update all the screens - */ -function updateScreens() { - log.send(logLevels.INFO, `Updating screens as there is change in display connections`); - screens = electron.screen.getAllDisplays(); - - // Notifying renderer when a display is added/removed - if (configurationWindow) { - // Event that updates the DOM elements - // notification position checkbox and monitor selection drop-down - configurationWindow.webContents.send('notificationSettings', {position: position, display: display}); - - if (screens && screens.length >= 0) { - configurationWindow.webContents.send('screens', screens); - } - } -} - -/** - * Gets the template path - * @returns {string} - */ -function getTemplatePath() { - let templatePath = path.join(__dirname, 'configure-notification-position.html'); - try { - fs.statSync(templatePath).isFile(); - } catch (err) { - log.send(logLevels.ERROR, 'configure-notification-position: Could not find template ("' + templatePath + '").'); - } - return 'file://' + templatePath; -} - -/** - * Opens the configuration window for a specific window - * @param windowName - */ -function openConfigurationWindow(windowName) { - // This prevents creating multiple instances of the - // notification configuration window - if (configurationWindow && !configurationWindow.isDestroyed()) { - if (configurationWindow.isMinimized()) { - configurationWindow.restore(); - } - configurationWindow.focus(); - return; - } - - const allWindows = BrowserWindow.getAllWindows(); - const selectedParentWindow = allWindows.find((window) => { return window.winName === windowName }); - - // if we couldn't find any window matching the window name - // it will render as a new window - if (selectedParentWindow) { - windowConfig.parent = selectedParentWindow; - - /** - * This is a temporary work around until there - * is a fix for the modal window in windows from the electron - * issue - https://github.com/electron/electron/issues/10721 - */ - const { x, y, width, height } = selectedParentWindow.getBounds(); - - const windowWidth = Math.round(width * 0.5); - const windowHeight = Math.round(height * 0.5); - - // Calculating the center of the parent window - // to place the configuration window - const centerX = x + width / 2.0; - const centerY = y + height / 2.0; - windowConfig.x = Math.round(centerX - (windowWidth / 2.0)); - windowConfig.y = Math.round(centerY - (windowHeight / 2.0)); - } - - configurationWindow = new BrowserWindow(windowConfig); - configurationWindow.setVisibleOnAllWorkspaces(true); - configurationWindow.loadURL(getTemplatePath()); - - configurationWindow.once('ready-to-show', () => { - configurationWindow.show(); - }); - - configurationWindow.webContents.on('did-finish-load', () => { - const notificationSettingsContent = i18n.getMessageFor('NotificationSettings'); - configurationWindow.webContents.send('i18n-notification-settings', notificationSettingsContent); - if (screens && screens.length >= 0) { - configurationWindow.webContents.send('screens', screens); - } - configurationWindow.webContents.send('notificationSettings', {position: position, display: display}); - if (!isMac) { - // prevents from displaying menu items when "alt" key is pressed - configurationWindow.setMenu(null); - } - }); - - configurationWindow.on('close', () => { - destroyWindow(); - }); - - configurationWindow.on('closed', () => { - destroyWindow(); - }); -} - -/** - * Destroys a window - */ -function destroyWindow() { - log.send(logLevels.INFO, `Closing notification configure window`); - configurationWindow = null; -} - -/** - * Method to save 'position' & 'display' to the config file - */ -function updateConfig() { - let settings = { - position: position, - display: display - }; - updateConfigField('notificationSettings', settings); - updateNotification(position, display); - log.send(logLevels.INFO, `Updating notification position on screen`); -} - -/** - * Method to update the Notification class with the new 'position' & 'screen' - * @param mPosition - position to display the notifications - * ('upper-right, upper-left, lower-right, lower-left') - * @param mDisplay - id of the selected display - */ -function updateNotification(mPosition, mDisplay) { - notify.updateConfig({position: mPosition, display: mDisplay}); - eventEmitter.emit('notificationSettings', {position: mPosition, display: mDisplay}); - notify.reset(); -} - -ipc.on('close-alert', function () { - configurationWindow.close(); -}); - -ipc.on('update-config', (event, config) => { - - if (config) { - if (config.position) { - position = config.position; - } - if (config.display) { - display = config.display; - } - } - - updateConfig(); -}); - -/** - * Event to read 'Position' & 'Display' from config and - * updated the configuration view - */ -eventEmitter.on('notificationSettings', (notificationSettings) => { - position = notificationSettings.position; - display = notificationSettings.display; -}); - -module.exports = { - openConfigurationWindow: openConfigurationWindow -}; \ No newline at end of file diff --git a/js/preload/preloadMain.js b/js/preload/preloadMain.js deleted file mode 100644 index bf9f8be2..00000000 --- a/js/preload/preloadMain.js +++ /dev/null @@ -1,601 +0,0 @@ -'use strict'; - -// script run before others and still has access to node integration, even -// when turned off - allows us to leak only what want into window object. -// see: http://electron.atom.io/docs/api/browser-window/ -// -// to leak some node module into: -// https://medium.com/@leonli/securing-embedded-external-content-in-electron-node-js-8b6ef665cd8e#.fex4e68p7 -// https://slack.engineering/building-hybrid-applications-with-electron-dc67686de5fb#.tp6zz1nrk -// -// also to bring pieces of node.js: -// https://github.com/electron/electron/issues/2984 -// -const { ipcRenderer, remote, crashReporter, webFrame } = require('electron'); - -const throttle = require('../utils/throttle.js'); -const apiEnums = require('../enums/api.js'); -const apiCmds = apiEnums.cmds; -const apiName = apiEnums.apiName; -const getMediaSources = require('../desktopCapturer/getSources'); -const getMediaSource = require('../desktopCapturer/getSource'); -const showScreenSharingIndicator = require('../screenSharingIndicator/showScreenSharingIndicator'); -const { TitleBar } = require('../windowsTitlebar'); -const { NetworkError } = require('../networkError'); -const titleBar = new TitleBar(); -const networkError = new NetworkError(); -const { buildNumber } = require('../../package.json'); -const SnackBar = require('../snackBar').SnackBar; -const KeyCodes = { - Esc: 27, - Alt: 18, -}; - -let Search; -let SearchUtils; -let CryptoLib; -let isAltKey = false; -let isMenuOpen = false; - -try { - Search = remote.require('swift-search').Search; -} catch (e) { - // eslint-disable-next-line no-console - console.warn("Failed to initialize swift search. You'll need to include the search dependency. Contact the developers for more details"); -} - -try { - SearchUtils = remote.require('swift-search').SearchUtils; -} catch (e) { - // eslint-disable-next-line no-console - console.warn("Failed to initialize swift search (Utils). You'll need to include the search dependency. Contact the developers for more details"); -} - -try { - CryptoLib = remote.require('./cryptoLib.js'); -} catch (e) { - CryptoLib = null; - // eslint-disable-next-line no-console - console.warn("Failed to initialize Crypto Lib. You'll need to include the Crypto library. Contact the developers for more details"); -} - -require('../downloadManager'); -let snackBar; - -// hold ref so doesn't get GC'ed -const local = { - ipcRenderer: ipcRenderer -}; - -// throttle calls to this func to at most once per sec, called on leading edge. -const throttledSetBadgeCount = throttle(1000, function (count) { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.setBadgeCount, - count: count - }); -}); - -const throttledSetIsInMeetingStatus = throttle(1000, function (isInMeeting) { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.setIsInMeeting, - isInMeeting - }); -}); - -/** - * an event triggered by the main process onload event - */ -local.ipcRenderer.on('on-page-load', () => { - snackBar = new SnackBar(); - - webFrame.setSpellCheckProvider('en-US', true, { - spellCheck(text) { - return !local.ipcRenderer.sendSync(apiName, { - cmd: apiCmds.isMisspelled, - text - }); - } - }); - - // only registers main window's preload - if (window.name === 'main') { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.optimizeMemoryRegister, - }); - } -}); - -const throttledActivate = throttle(1000, function (windowName) { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.activate, - windowName: windowName - }); -}); - -const throttledBringToFront = throttle(1000, function (windowName, reason) { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.bringToFront, - windowName: windowName, - reason: reason - }); -}); - -const throttledSetLocale = throttle(1000, function (locale) { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.setLocale, - locale, - }); -}); - -createAPI(); - -// creates API exposed from electron. -// wrapped in a function so we can abort early in function coming from an iframe -function createAPI() { - // iframes (and any other non-top level frames) get no api access - // http://stackoverflow.com/questions/326069/how-to-identify-if-a-webpage-is-being-loaded-inside-an-iframe-or-directly-into-t/326076 - if (window.self !== window.top) { - return; - } - - // note: window.open from main window (if in the same domain) will get - // api access. window.open in another domain will be opened in the default - // browser (see: handler for event 'new-window' in windowMgr.js) - - // - // API exposed to renderer process. - // - window.ssf = { - getVersionInfo: function () { - return new Promise(function (resolve) { - let appName = remote.app.getName(); - let appVer = remote.app.getVersion(); - - const verInfo = { - containerIdentifier: appName, - containerVer: appVer, - buildNumber: buildNumber, - apiVer: '2.0.0', - searchApiVer: '3.0.0' - }; - resolve(verInfo); - }); - }, - - /** - * sets the count on the tray icon to the given number. - * @param {number} count count to be displayed - * note: count of 0 will remove the displayed count. - * note: for mac the number displayed will be 1 to infinity - * note: for windws the number displayed will be 1 to 99 and 99+ - */ - setBadgeCount: function (count) { - throttledSetBadgeCount(count); - }, - - /** - * provides api similar to html5 Notification, see details - * in notify/notifyImpl.js - */ - Notification: remote.require('./notify/notifyImpl.js'), - - /** - * provides api to allow user to capture portion of screen, see api - * details in screenSnipper/index.js - */ - ScreenSnippet: remote.require('./screenSnippet/index.js').ScreenSnippet, - - /** - * Provides api for client side searching - * using the SymphonySearchEngine library - * details in ./search/search.js & ./search/searchLibrary.js - */ - Search: Search || null, - - /** - * Provides api for search module utils - * like checking free space / search user config data to the client app - * details in ./search/searchUtils.js & ./search/searchConfig.js - */ - SearchUtils: SearchUtils || null, - - /** - * Native encryption and decryption. - */ - CryptoLib: CryptoLib, - - /** - * Brings window forward and gives focus. - * @param {String} windowName Name of window. Note: main window name is 'main' - */ - activate: function (windowName) { - if (typeof windowName === 'string') { - throttledActivate(windowName); - } - }, - - /** - * Brings window forward and gives focus. - * @param {String} windowName Name of window. Note: main window name is 'main' - * @param {String} reason, The reason for which the window is to be activated - */ - bringToFront: function (windowName, reason) { - if (typeof windowName === 'string') { - throttledBringToFront(windowName, reason); - } - }, - - /** - * Allows JS to register a callback to be invoked when size/positions - * changes for any pop-out window (i.e., window.open). The main - * process will emit IPC event 'boundsChange' (see below). Currently - * only one window can register for bounds change. - * @param {Function} callback Function invoked when bounds changes. - */ - registerBoundsChange: function (callback) { - if (typeof callback === 'function') { - local.boundsChangeCallback = callback; - local.ipcRenderer.send(apiName, { - cmd: apiCmds.registerBoundsChange - }); - } - }, - - /** - * allows JS to register a logger that can be used by electron main process. - * @param {Object} logger function that can be called accepting - * object: { - * logLevel: 'ERROR'|'CONFLICT'|'WARN'|'ACTION'|'INFO'|'DEBUG', - * logDetails: String - * } - */ - registerLogger: function (logger) { - if (typeof logger === 'function') { - local.logger = logger; - - // only main window can register - local.ipcRenderer.send(apiName, { - cmd: apiCmds.registerLogger - }); - } - }, - - /** - * allows JS to register a protocol handler that can be used by the - * electron main process. - * - * @param protocolHandler {Function} callback will be called when app is - * invoked with registered protocol (e.g., symphony). The callback - * receives a single string argument: full uri that the app was - * invoked with e.g., symphony://?streamId=xyz123&streamType=chatroom - * - * Note: this function should only be called after client app is fully - * able for protocolHandler callback to be invoked. It is possible - * the app was started using protocol handler, in this case as soon as - * this registration func is invoked then the protocolHandler callback - * will be immediately called. - */ - registerProtocolHandler: function (protocolHandler) { - if (typeof protocolHandler === 'function') { - - local.processProtocolAction = protocolHandler; - - local.ipcRenderer.send(apiName, { - cmd: apiCmds.registerProtocolHandler - }); - - } - }, - - /** - * allows JS to register a activity detector that can be used by electron main process. - * @param {Object} activityDetection - function that can be called accepting - * @param {Object} period - minimum user idle time in millisecond - * object: { - * period: Number - * systemIdleTime: Number - * } - */ - registerActivityDetection: function (period, activityDetection) { - if (typeof activityDetection === 'function') { - local.activityDetection = activityDetection; - - // only main window can register - local.ipcRenderer.send(apiName, { - cmd: apiCmds.registerActivityDetection, - period: period - }); - } - }, - - /** - * Implements equivalent of desktopCapturer.getSources - that works in - * a sandboxed renderer process. - * see: https://electron.atom.io/docs/api/desktop-capturer/ - * for interface: see documentation in desktopCapturer/getSources.js - * - * @deprecated instead use getMediaSource - */ - getMediaSources: getMediaSources, - - /** - * Implements equivalent of desktopCapturer.getSources - that works in - * a sandboxed renderer process. - * see: https://electron.atom.io/docs/api/desktop-capturer/ - * for interface: see documentation in desktopCapturer/getSource.js - * - * This opens a window and displays all the desktop sources - * and returns selected source - */ - getMediaSource: getMediaSource, - - /** - * Shows a banner that informs user that the screen is being shared. - * - * @param params object with following fields: - * - stream https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/MediaStream object. - * The indicator automatically destroys itself when stream becomes inactive (see MediaStream.active). - * - displayId id of the display that is being shared or that contains the shared app - * @param callback callback function that will be called to handle events. - * Callback receives event object { type: string }. Types: - * - 'error' - error occured. Event object contains 'reason' field. - * - 'stopRequested' - user clicked "Stop Sharing" button. - */ - showScreenSharingIndicator: showScreenSharingIndicator, - - /** - * Opens a modal window to configure notification preference. - */ - showNotificationSettings: function () { - let windowName = remote.getCurrentWindow().winName; - local.ipcRenderer.send(apiName, { - cmd: apiCmds.showNotificationSettings, - windowName: windowName - }); - }, - - /** - * Sets if the user is in an active meeting - * will be used to handle memory refresh functionality - */ - setIsInMeeting: function (isInMeeting) { - throttledSetIsInMeetingStatus(isInMeeting); - }, - - /** - * Sets the language which updates the application locale - * @param {string} locale - language identifier and a region identifier - * Ex: en-US, ja-JP - */ - setLocale: function (locale) { - if (typeof locale === 'string') { - throttledSetLocale(locale); - } - }, - - /** - * Allows JS to register activeRequests that can be used by electron to - * get the active network request from the client - * - * @param activeRequests - */ - registerActiveRequests: function (activeRequests) { - if (typeof activeRequests === 'function') { - local.activeRequests = activeRequests; - } - }, - }; - - // add support for both ssf and SYM_API name-space. - window.SYM_API = window.ssf; - Object.freeze(window.ssf); - Object.freeze(window.SYM_API); - - // listen for log message from main process - local.ipcRenderer.on('log', (event, arg) => { - if (arg && local.logger) { - local.logger(arg.msgs || [], arg.logLevel, arg.showInConsole); - } - }); - - // listen for notifications that some window size/position has changed - local.ipcRenderer.on('boundsChange', (event, arg) => { - if (local.boundsChangeCallback && arg.windowName && - arg.x && arg.y && arg.width && arg.height) { - local.boundsChangeCallback({ - x: arg.x, - y: arg.y, - width: arg.width, - height: arg.height, - windowName: arg.windowName - }); - } - }); - - // listen for user activity from main process - local.ipcRenderer.on('activity', (event, arg) => { - if (local.activityDetection && arg && typeof arg.systemIdleTime === 'number') { - local.activityDetection(arg.systemIdleTime); - } - }); - - /** - * Use render process to create badge count img and send back to main process. - * If number is greater than 99 then 99+ img is returned. - * note: with sandboxing turned on only get arg and no event passed in, so - * need to use ipcRenderer to callback to main process. - * @type {object} arg.count - number: count to be displayed - */ - local.ipcRenderer.on('createBadgeDataUrl', (event, arg) => { - const count = arg && arg.count || 0; - - // create 32 x 32 img - let radius = 16; - let canvas = document.createElement('canvas'); - canvas.height = radius * 2; - canvas.width = radius * 2; - - let ctx = canvas.getContext('2d'); - - ctx.fillStyle = 'red'; - ctx.beginPath(); - ctx.arc(radius, radius, radius, 0, 2 * Math.PI, false); - ctx.fill(); - - ctx.textAlign = 'center'; - ctx.fillStyle = 'white'; - - let text = count > 99 ? '99+' : count.toString(); - - if (text.length > 2) { - ctx.font = 'bold 18px sans-serif'; - ctx.fillText(text, radius, 22); - } else if (text.length > 1) { - ctx.font = 'bold 24px sans-serif'; - ctx.fillText(text, radius, 24); - } else { - ctx.font = 'bold 26px sans-serif'; - ctx.fillText(text, radius, 26); - } - - let dataUrl = canvas.toDataURL('image/png', 1.0); - - local.ipcRenderer.send(apiName, { - cmd: apiCmds.badgeDataUrl, - dataUrl: dataUrl, - count: count - }); - }); - - /** - * an event triggered by the main process for processing protocol urls - * @type {String} arg - the protocol url - */ - local.ipcRenderer.on('protocol-action', (event, arg) => { - if (local.processProtocolAction && arg) { - local.processProtocolAction(arg); - } - - }); - - /** - * an event triggered by the main process to start crash reporter - * in the render thread - */ - local.ipcRenderer.on('register-crash-reporter', (event, arg) => { - if (arg && typeof arg === 'object') { - crashReporter.start(arg); - } - }); - - // Adds custom title bar style for Windows 10 OS - local.ipcRenderer.on('initiate-windows-title-bar', (event, arg) => { - if (arg && typeof arg === 'string') { - titleBar.initiateWindowsTitleBar(arg); - } - }); - - // display network error info - local.ipcRenderer.on('network-error', (event, data) => { - if (data && typeof data === 'object') { - networkError.showError(data); - } - }); - - /** - * an event triggered by the main process when - * the locale is changed - * @param dataObj {Object} - locale content - */ - local.ipcRenderer.on('locale-changed', (event, dataObj) => { - if (dataObj && typeof dataObj === 'object') { - if (dataObj.titleBar) { - titleBar.updateLocale(dataObj.titleBar); - } - } - }); - - /** - * an event triggered by the main process when - * the window enters full screen - */ - local.ipcRenderer.on('window-enter-full-screen', (event, arg) => { - if (snackBar && typeof arg === 'object' && arg.snackBar) { - setTimeout(() => snackBar.showSnackBar(arg.snackBar), 500); - } - }); - - /** - * an event triggered by the main process when - * the window leave full screen - */ - local.ipcRenderer.on('window-leave-full-screen', () => { - if (snackBar) { - snackBar.removeSnackBar(); - } - }); - - local.ipcRenderer.on('memory-info-request', () => { - if (window.name === 'main') { - const activeRequests = typeof local.activeRequests === 'function' ? local.activeRequests() : 0; - local.ipcRenderer.send(apiName, { - cmd: apiCmds.optimizeMemoryConsumption, - activeRequests, - }); - } - }); - - function updateOnlineStatus() { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.isOnline, - isOnline: window.navigator.onLine - }); - } - - // Invoked whenever the app is reloaded/navigated - function sanitize() { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.sanitize, - windowName: window.name || 'main' - }); - } - - // Handle key down events - const throttledKeyDown = throttle(500, (event) => { - isAltKey = event.keyCode === KeyCodes.Alt; - if (event.keyCode === KeyCodes.Esc) { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.keyPress, - keyCode: event.keyCode - }); - } - }); - - // Handle key up events - const throttledKeyUp = throttle(500, (event) => { - if (isAltKey && (event.keyCode === KeyCodes.Alt || KeyCodes.Esc)) { - isMenuOpen = !isMenuOpen; - } - if (isAltKey && isMenuOpen && event.keyCode === KeyCodes.Alt) { - local.ipcRenderer.send(apiName, { - cmd: apiCmds.keyPress, - keyCode: event.keyCode - }); - } - }); - - const throttleMouseDown = throttle(500, () => { - if (isAltKey && isMenuOpen) { - isMenuOpen = !isMenuOpen; - } - }); - - window.addEventListener('offline', updateOnlineStatus, false); - window.addEventListener('online', updateOnlineStatus, false); - window.addEventListener('beforeunload', sanitize, false); - window.addEventListener('keyup', throttledKeyUp, true); - window.addEventListener('keydown', throttledKeyDown, true); - window.addEventListener('mousedown', throttleMouseDown, { capture: true }); - - updateOnlineStatus(); -} diff --git a/js/protocolHandler/index.js b/js/protocolHandler/index.js deleted file mode 100644 index ba97c8ed..00000000 --- a/js/protocolHandler/index.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict'; - -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); - -let protocolWindow; -let protocolUrl; - -/** - * processes a protocol uri - * @param {String} uri - the uri opened in the format 'symphony://...' - */ -function processProtocolAction(uri) { - log.send(logLevels.INFO, `protocol action, uri ${uri}`); - if (!protocolWindow) { - log.send(logLevels.INFO, `protocol window not yet initialized, caching the uri ${uri}`); - setProtocolUrl(uri); - return; - } - if (uri && uri.startsWith('symphony://')) { - protocolWindow.send('protocol-action', uri); - } -} - -/** - * sets the protocol window - * @param {Object} win - the renderer window - */ -function setProtocolWindow(win) { - protocolWindow = win; -} - -/** - * checks to see if the app was opened by a uri - */ -function checkProtocolAction() { - log.send(logLevels.INFO, `checking if we have a cached protocol url`); - if (protocolUrl) { - log.send(logLevels.INFO, `found a cached protocol url, processing it!`); - processProtocolAction(protocolUrl); - protocolUrl = undefined; - } -} - -/** - * caches the protocol uri - * @param {String} uri - the uri opened in the format 'symphony://...' - */ -function setProtocolUrl(uri) { - protocolUrl = uri; -} - -/** - * gets the protocol url set against an instance - * @returns {*} - */ -function getProtocolUrl() { - return protocolUrl; -} - -module.exports = { - processProtocolAction: processProtocolAction, - setProtocolWindow: setProtocolWindow, - checkProtocolAction: checkProtocolAction, - setProtocolUrl: setProtocolUrl, - getProtocolUrl: getProtocolUrl -}; diff --git a/js/screenSharingIndicator/index.js b/js/screenSharingIndicator/index.js deleted file mode 100755 index e1919237..00000000 --- a/js/screenSharingIndicator/index.js +++ /dev/null @@ -1,111 +0,0 @@ -'use strict'; - -const electron = require('electron'); -const ipcMain = electron.ipcMain; -const path = require('path'); -const fs = require('fs'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const { initCrashReporterMain, initCrashReporterRenderer } = require('../crashReporter.js'); -const i18n = require('../translation/i18n'); -const { isMac } = require('./../utils/misc.js'); - -const baseWindowConfig = { - width: 592, - height: 48, - show: false, - modal: true, - frame: false, - focusable: false, - transparent: true, - autoHideMenuBar: true, - resizable: false, - minimizable: false, - maximizable: false, - closable: true, - alwaysOnTop: true, - webPreferences: { - preload: path.join(__dirname, 'renderer.js'), - sandbox: true, - nodeIntegration: false, - devTools: false - } -}; - -function getTemplatePath() { - let templatePath = path.join(__dirname, 'screen-sharing-indicator.html'); - try { - fs.statSync(templatePath).isFile(); - } catch (err) { - log.send(logLevels.ERROR, `screen-sharing-indicator: Could not find template ("${templatePath}").`); - } - return `file://${templatePath}`; -} - -function openScreenSharingIndicator(eventSender, displayId, id) { - const indicatorScreen = (displayId && electron.screen.getAllDisplays().filter(d => displayId.includes(d.id))[0]) || electron.screen.getPrimaryDisplay(); - const screenRect = indicatorScreen.workArea; - const windowConfig = Object.assign({}, baseWindowConfig, { - x: screenRect.x + Math.round((screenRect.width - baseWindowConfig.width) / 2), - y: screenRect.y + screenRect.height - baseWindowConfig.height - }); - - const indicatorWindow = new electron.BrowserWindow(windowConfig); - indicatorWindow.setVisibleOnAllWorkspaces(true); - indicatorWindow.setMenu(null); - indicatorWindow.loadURL(getTemplatePath()); - - indicatorWindow.once('ready-to-show', () => { - indicatorWindow.show(); - }); - - indicatorWindow.webContents.on('did-finish-load', () => { - initCrashReporterMain({ process: 'screen sharing indicator window' }); - initCrashReporterRenderer(indicatorWindow, { process: 'render | screen sharing indicator window' }); - indicatorWindow.webContents.send('window-data', { - id, - i18n: i18n.getMessageFor('ScreenSharingIndicator'), - isMac - }); - }); - - indicatorWindow.webContents.on('crashed', (event, killed) => { - - log.send(logLevels.INFO, `Screen Sharing Indicator Window crashed! Killed? ${killed}`); - - if (killed) { - return; - } - - const errorDialogOptions = { - type: 'error', - title: i18n.getMessageFor('Renderer Process Crashed'), - message: i18n.getMessageFor('Oops! Looks like we have had a crash.'), - buttons: ['Close'] - }; - electron.dialog.showMessageBox(errorDialogOptions, () => indicatorWindow.close()); - }); - - const handleStopSharingClicked = (event, indicatorId) => { - if (indicatorId === id) { - eventSender.send('stop-sharing-requested', id); - } - }; - - const handleDestroyScreensharingIndicator = (event, indicatorId) => { - if (indicatorId === id) { - if (!indicatorWindow.isDestroyed()) { - indicatorWindow.close(); - } - ipcMain.removeListener('stop-sharing-clicked', handleStopSharingClicked); - ipcMain.removeListener('destroy-screensharing-indicator', handleDestroyScreensharingIndicator); - } - }; - - ipcMain.on('stop-sharing-clicked', handleStopSharingClicked); - ipcMain.on('destroy-screensharing-indicator', handleDestroyScreensharingIndicator); -} - -module.exports = { - openScreenSharingIndicator -}; diff --git a/js/screenSharingIndicator/renderer.js b/js/screenSharingIndicator/renderer.js deleted file mode 100644 index e5b3e19a..00000000 --- a/js/screenSharingIndicator/renderer.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -const { ipcRenderer, crashReporter } = require('electron'); - -let indicatorId; - -function renderDom() { - const stopSharingButton = document.getElementById('stop-sharing-button'); - stopSharingButton.addEventListener('click', () => { - ipcRenderer.send('stop-sharing-clicked', indicatorId); - }, false); - - const hideButton = document.getElementById('hide-button'); - hideButton.addEventListener('click', () => { - window.close(); - }, false); -} - -ipcRenderer.on('window-data', (event, content) => { - indicatorId = content.id; - const setText = (el, text) => { - document.getElementById(el).innerHTML = (content.i18n[text] || text).replace('Symphony', 'Symphony'); - }; - setText('stop-sharing-button', 'Stop sharing'); - setText('hide-button', 'Hide'); - setText('text-label', 'You are sharing your screen on Symphony'); - document.body.className = content.isMac ? 'mac' : ''; -}); - -ipcRenderer.on('register-crash-reporter', (event, arg) => { - if (arg && typeof arg === 'object') { - crashReporter.start(arg); - } -}); - -document.addEventListener('DOMContentLoaded', () => { - renderDom(); -}); diff --git a/js/screenSharingIndicator/screen-sharing-indicator.html b/js/screenSharingIndicator/screen-sharing-indicator.html deleted file mode 100755 index 20310db6..00000000 --- a/js/screenSharingIndicator/screen-sharing-indicator.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - -
- - You are sharing your screen on Symphony - - Hide - - -
- - \ No newline at end of file diff --git a/js/screenSharingIndicator/showScreenSharingIndicator.js b/js/screenSharingIndicator/showScreenSharingIndicator.js deleted file mode 100644 index 4e0a83ba..00000000 --- a/js/screenSharingIndicator/showScreenSharingIndicator.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -const { ipcRenderer } = require('electron'); -const apiEnums = require('../enums/api.js'); -const apiCmds = apiEnums.cmds; -const apiName = apiEnums.apiName; - -let nextIndicatorId = 0; - -function showScreenSharingIndicator(options, callback) { - const { stream, displayId } = options; - - if (!stream || !stream.active || stream.getVideoTracks().length !== 1) { - callback({type: 'error', reason: 'bad stream'}); - return; - } - if (displayId && typeof(displayId) !== 'string') { - callback({type: 'error', reason: 'bad displayId'}); - return; - } - - const id = ++nextIndicatorId; - ipcRenderer.send(apiName, { - cmd: apiCmds.openScreenSharingIndicator, - displayId: options.displayId, - id - }); - - const handleStopRequest = (e, indicatorId) => { - if (indicatorId === id) { - callback({type: 'stopRequested'}); - } - } - - const destroy = () => { - ipcRenderer.send('destroy-screensharing-indicator', id); - options.stream.removeEventListener('inactive', destroy); - ipcRenderer.removeListener('stop-sharing-requested', handleStopRequest); - }; - - ipcRenderer.on('stop-sharing-requested', handleStopRequest); - options.stream.addEventListener('inactive', destroy); -} - - -module.exports = showScreenSharingIndicator; \ No newline at end of file diff --git a/js/screenSnippet/index.js b/js/screenSnippet/index.js deleted file mode 100644 index 172f33a9..00000000 --- a/js/screenSnippet/index.js +++ /dev/null @@ -1,215 +0,0 @@ -'use strict'; - -const electron = require('electron'); -const app = electron.app; -const childProcess = require('child_process'); -const fs = require('fs'); -const Jimp = require('jimp'); -const os = require('os'); -const path = require('path'); - -const { isMac, isDevEnv, isWindowsOS } = require('../utils/misc.js'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); -const eventEmitter = require('.././eventEmitter'); -const { getLanguage } = require('../translation/i18n'); - -// static ref to child process, only allow one screen snippet at time, so -// hold ref to prev, so can kill before starting next snippet. -let child; -let isAlwaysOnTop; - -/** - * Captures a user selected portion of the monitor and returns jpeg image - * encoded in base64 format. - */ -class ScreenSnippet { - /** - * Returns promise. - * - * If successful will resolves with jpeg image encoded in base64 format: - * { - * type: 'image/jpg;base64', - * data: base64-data - * } - * - * Otherwise if not successful will reject with object - * containing: { type: ['WARN','ERROR'], message: String } - */ - capture() { - return new Promise((resolve, reject) => { - let captureUtil, captureUtilArgs; - - log.send(logLevels.INFO, 'ScreenSnippet: starting screen capture'); - - let tmpFilename = (isWindowsOS) ? 'symphonyImage-' + Date.now() + '.png' : 'symphonyImage-' + Date.now() + '.jpg'; - let tmpDir = os.tmpdir(); - - let outputFileName = path.join(tmpDir, tmpFilename); - - if (isMac) { - // utilize Mac OSX built-in screencapture tool which has been - // available since OSX ver 10.2. - captureUtil = '/usr/sbin/screencapture'; - captureUtilArgs = ['-i', '-s', '-t', 'jpg', outputFileName]; - } else { - // use custom built windows screen capture tool - if (isDevEnv) { - // for dev env pick up tool from node nodules - captureUtil = - path.join(__dirname, - '../../node_modules/screen-snippet/bin/Release/ScreenSnippet.exe'); - } else { - // for production gets installed next to exec. - let execPath = path.dirname(app.getPath('exe')); - captureUtil = path.join(execPath, 'ScreenSnippet.exe'); - } - - // Method to verify and disable always on top property - // as an issue with the ScreenSnippet.exe not being on top - // of the electron wrapper - const windows = electron.BrowserWindow.getAllWindows(); - if (windows && windows.length > 0) { - isAlwaysOnTop = windows[ 0 ].isAlwaysOnTop(); - if (isAlwaysOnTop) { - eventEmitter.emit('isAlwaysOnTop', { - isAlwaysOnTop: false, - shouldActivateMainWindow: false - }); - } - } - - captureUtilArgs = [outputFileName, getLanguage()]; - } - - log.send(logLevels.INFO, 'ScreenSnippet: starting screen capture util: ' + captureUtil + ' with args=' + captureUtilArgs); - - // only allow one screen capture at a time. - if (child) { - child.kill(); - } - - child = childProcess.execFile(captureUtil, captureUtilArgs, (error) => { - // Method to reset always on top feature - if (isAlwaysOnTop) { - eventEmitter.emit('isAlwaysOnTop', { - isAlwaysOnTop: true, - shouldActivateMainWindow: false - }); - } - // will be called when child process exits. - if (error && error.killed) { - // processs was killed, just resolve with no data. - resolve(); - } else { - readResult.call(this, outputFileName, resolve, reject, error); - } - }); - }); - } -} - -/** - * this function was moved outside of class since class is exposed to web - * client via preload API, we do NOT want web client to be able to call this - * method - then they could read any file on the disk! - * @param outputFileName - * @param resolve - * @param reject - * @param childProcessErr - */ -function readResult(outputFileName, resolve, reject, childProcessErr) { - fs.readFile(outputFileName, (readErr, data) => { - if (readErr) { - let returnErr; - if (readErr.code === 'ENOENT') { - // no such file exists, user likely aborted - // creating snippet. also include any error when - // creating child process. - returnErr = createWarn('file does not exist ' + - childProcessErr); - } else { - returnErr = createError(readErr + ',' + - childProcessErr); - } - - reject(returnErr); - return; - } - - if (!data) { - reject(createWarn('no file data provided')); - return; - } - - try { - // convert binary data to base64 encoded string - Jimp.read(outputFileName, (error, image) => { - if (error) throw error; - image.quality(100) - .getBufferAsync(Jimp.MIME_JPEG).then(src => { - const value = src.toString('base64'); - const resultOutput = { - type: 'image/jpg;base64', - data: value, - }; - resolve(resultOutput); - }).catch((e) => { - log.send(logLevels.ERROR, e); - }); - }); - } catch (error) { - reject(createError(error)); - } finally { - // remove tmp file (async) - fs.unlink(outputFileName, function(removeErr) { - // note: node complains if calling async - // func without callback. - if (removeErr) { - log.send(logLevels.ERROR, 'ScreenSnippet: error removing temp snippet file: ' + - outputFileName + ', err:' + removeErr); - } - }); - } - }); -} - -/* eslint-disable class-methods-use-this */ -/** - * Create an error object with the ERROR level - * @param message - * @returns {{message: string, type: string}} - */ -function createError(message) { - return { - message, - type: 'ERROR', - }; -} - -/** - * Create an error object with the WARN level - * @param message - * @returns {{message: string, type: string}} - */ -function createWarn(message) { - return { - message, - type: 'WARN', - }; -} -/* eslint-enable class-methods-use-this */ - -// terminates the screen snippet process wherever the -// main window is reloaded/navigated -eventEmitter.on('killScreenSnippet', function () { - if (child) { - child.kill(); - } -}); - -module.exports = { - ScreenSnippet: ScreenSnippet, - // note: readResult only exposed for testing purposes - readResult: readResult -}; \ No newline at end of file diff --git a/js/snackBar/content.js b/js/snackBar/content.js deleted file mode 100644 index 3fb45166..00000000 --- a/js/snackBar/content.js +++ /dev/null @@ -1,11 +0,0 @@ -const snackBarContent = ( - `
- - - -
` -); - -module.exports = { - snackBarContent -}; \ No newline at end of file diff --git a/js/snackBar/index.js b/js/snackBar/index.js deleted file mode 100644 index db24554a..00000000 --- a/js/snackBar/index.js +++ /dev/null @@ -1,54 +0,0 @@ -const snackBarContent = require('./content').snackBarContent; - -class SnackBar { - - constructor() { - this.body = document.getElementsByTagName('body'); - this.domParser = new DOMParser(); - - const snackBar = this.domParser.parseFromString(snackBarContent, 'text/html'); - this.snackBar = snackBar.getElementById('snackbar'); - } - - /** - * Method that displays a snack bar for 3 sec - */ - showSnackBar(content) { - if (content && typeof content === 'object') { - const i18nNodes = this.snackBar.querySelectorAll('[data-i18n-text]'); - - for (let node of i18nNodes) { - if (node.attributes['data-i18n-text'] && node.attributes['data-i18n-text'].value) { - node.innerText = content[node.attributes['data-i18n-text'].value] || node.attributes['data-i18n-text'].value; - } - } - } - - if (this.body && this.body.length > 0 && this.snackBar) { - this.body[0].appendChild(this.snackBar); - this.snackBar.className = "show"; - this.snackBarTimmer = setTimeout(() => { - this.body[0].removeChild(this.snackBar); - }, 3000); - } - } - - /** - * Method that removes snack bar from the DOM - */ - removeSnackBar() { - if (this.body && this.body.length > 0 && this.snackBar) { - if (document.getElementById('snackbar')) { - this.body[0].removeChild(this.snackBar); - if (this.snackBarTimmer) { - clearTimeout(this.snackBarTimmer); - } - } - } - } - -} - -module.exports = { - SnackBar, -}; \ No newline at end of file diff --git a/js/snackBar/style.css b/js/snackBar/style.css deleted file mode 100644 index 9950ae24..00000000 --- a/js/snackBar/style.css +++ /dev/null @@ -1,51 +0,0 @@ -#snackbar { - visibility: hidden; - min-width: 250px; - margin-left: -135px; - background-color: rgba(51,51,51, 1); - color: rgba(255,255,255, 1); - text-align: center; - border-radius: 2px; - padding: 16px; - position: fixed; - z-index: 2147483647; - left: 50%; - top: 30px; - font-size: 14px; - font-style: normal; -} - -#snackbar-esc { - padding: 5px; - border: 0.5px solid rgba(51,51,51, 1); - border-radius: 3px; -} - -#snackbar.show { - visibility: visible; - -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s; - animation: fadein 0.5s, fadeout 0.5s 2.5s; -} - -#snackbar-esc { - border: 2px solid white; - background-color: rgba(51,51,51, 1); -} - -@-webkit-keyframes fadein { - from { - top: 0; opacity: 0; - } - to { - top: 30px; opacity: 1; - } -} - -@-webkit-keyframes fadeout { - from { - top: 30px; opacity: 1; - } - to { - top: 0; opacity: 0; - } -} \ No newline at end of file diff --git a/js/spellChecker/index.js b/js/spellChecker/index.js deleted file mode 100644 index 180c3087..00000000 --- a/js/spellChecker/index.js +++ /dev/null @@ -1,122 +0,0 @@ -const { app, MenuItem } = require('electron'); -const path = require('path'); -const { isMac, isDevEnv } = require('./../utils/misc'); -const { SpellCheckHandler, DictionarySync } = require('electron-spellchecker'); -const stringFormat = require('./../utils/stringFormat'); - -class SpellCheckHelper { - - /** - * A constructor to create an instance of the spell checker - */ - constructor() { - const dictionariesDirName = 'dictionaries'; - let dictionaryPath; - if (isDevEnv) { - dictionaryPath = path.join(app.getAppPath(), dictionariesDirName); - } else { - let execPath = path.dirname(app.getPath('exe')); - dictionaryPath = path.join(execPath, isMac ? '..' : '', dictionariesDirName); - } - this.dictionarySync = new DictionarySync(dictionaryPath); - this.spellCheckHandler = new SpellCheckHandler(this.dictionarySync); - } - - /** - * Method to initialize spell checker - */ - initializeSpellChecker() { - this.spellCheckHandler.automaticallyIdentifyLanguages = false; - - // This is only for window as in mac the - // language is switched w.r.t to the current system language. - // - // In windows we need to implement RxJS observable - // in order to switch language dynamically - if (!isMac) { - const sysLocale = app.getLocale() || 'en-US'; - this.spellCheckHandler.switchLanguage(sysLocale); - } - } - - /** - * Updates the locale for context menu labels - * @param content {Object} - locale content for context menu - */ - updateContextMenuLocale(content) { - this.localeContent = content; - } - - /** - * Builds the string table for context menu - * - * @param content {Object} - locale content for context menu - * @return {Object} - String table for context menu - */ - // eslint-disable-next-line class-methods-use-this - getStringTable(content) { - return { - copyMail: () => content['Copy Email Address'] || `Copy Email Address`, - copyLinkUrl: () => content['Copy Link'] || 'Copy Link', - openLinkUrl: () => content['Open Link'] || 'Open Link', - copyImageUrl: () => content['Copy Image URL'] || 'Copy Image URL', - copyImage: () => content['Copy Image'] || 'Copy Image', - addToDictionary: () => content['Add to Dictionary'] || 'Add to Dictionary', - lookUpDefinition: (lookup) => { - const formattedString = stringFormat(content['Look Up {searchText}'], { searchText: lookup.word }); - return formattedString || `Look Up ${lookup.word}`; - }, - searchGoogle: () => content['Search with Google'] || 'Search with Google', - cut: () => content.Cut || 'Cut', - copy: () => content.Copy || 'Copy', - paste: () => content.Paste || 'Paste', - inspectElement: () => content['Inspect Element'] || 'Inspect Element', - }; - } - - /** - * Method to add default menu items to the - * menu that was generated by ContextMenuBuilder - * - * This method will be invoked by electron-spellchecker - * before showing the context menu - * - * @param menu - * @returns menu - */ - processMenu(menu) { - - let isLink = false; - menu.items.map((item) => { - if (item.label === 'Copy Link'){ - isLink = true; - } - return item; - }); - - if (!isLink){ - menu.append(new MenuItem({ type: 'separator' })); - menu.append(new MenuItem({ - role: 'reload', - accelerator: 'CmdOrCtrl+R', - label: this.localeContent && this.localeContent.Reload || 'Reload', - })); - } - return menu; - } - - /** - * Method that checks if a text is misspelled - * - * @param text {string} - * @returns {*} - */ - isMisspelled(text) { - return this.spellCheckHandler.isMisspelled(text); - } - -} - -module.exports = { - SpellCheckHelper: SpellCheckHelper -}; diff --git a/js/stats.js b/js/stats.js deleted file mode 100644 index 3054aa23..00000000 --- a/js/stats.js +++ /dev/null @@ -1,94 +0,0 @@ -const os = require('os'); -const { app } = require('electron'); - -const config = require('./config'); - -const log = require('./log.js'); -const logLevels = require('./enums/logLevels.js'); - -const MB_IN_BYTES = 1048576; - -const logSystemStats = () => { - log.send(logLevels.INFO, `-----------------Gathering system information-----------------`); - log.send(logLevels.INFO, `Network Info -> ${JSON.stringify(os.networkInterfaces())}`); - log.send(logLevels.INFO, `CPU Info -> ${JSON.stringify(os.cpus())}`); - log.send(logLevels.INFO, `Operating System -> ${JSON.stringify(os.type())}`); - log.send(logLevels.INFO, `Platform -> ${JSON.stringify(os.platform())}`); - log.send(logLevels.INFO, `Architecture -> ${JSON.stringify(os.arch())}`); - log.send(logLevels.INFO, `Hostname -> ${JSON.stringify(os.hostname())}`); - log.send(logLevels.INFO, `Temp Directory -> ${JSON.stringify(os.tmpdir())}`); - log.send(logLevels.INFO, `Home Directory -> ${JSON.stringify(os.homedir())}`); - log.send(logLevels.INFO, `Total Memory (MB) -> ${JSON.stringify(os.totalmem() / MB_IN_BYTES)}`); - log.send(logLevels.INFO, `Free Memory (MB) -> ${JSON.stringify(os.freemem() / MB_IN_BYTES)}`); - log.send(logLevels.INFO, `Load Average -> ${JSON.stringify(os.loadavg())}`); - log.send(logLevels.INFO, `Uptime -> ${JSON.stringify(os.uptime())}`); - log.send(logLevels.INFO, `User Info (OS Returned) -> ${JSON.stringify(os.userInfo())}`); -}; - -const logGPUStats = () => { - log.send(logLevels.INFO, `-----------------Gathering GPU information-----------------`); - log.send(logLevels.INFO, `GPU Feature Status -> ${JSON.stringify(app.getGPUFeatureStatus())}`); -}; - -const logPodStats = () => { - const fields = ['url', 'minimizeOnClose', 'launchOnStartup', 'alwaysOnTop', 'bringToFront', 'whitelistUrl', 'isCustomTitleBar', 'memoryRefresh', 'devToolsEnabled', 'ctWhitelist', 'notificationSettings', 'crashReporter', 'customFlags', 'permissions', 'autoLaunchPath']; - config.getMultipleConfigField(fields) - .then((data) => { - log.send(logLevels.INFO, `-----------------Gathering POD & App information-----------------`); - log.send(logLevels.INFO, `Is app packaged? ${app.isPackaged}`); - for (let field in data) { - if (Object.prototype.hasOwnProperty.call(data, field)) { - log.send(logLevels.INFO, `${field} -> ${JSON.stringify(data[field])}`); - } - } - }); -}; - -const logAppMetrics = () => { - log.send(logLevels.INFO, `-----------------Gathering App Metrics-----------------`); - const metrics = app.getAppMetrics(); - metrics.forEach((metric) => { - log.send(logLevels.INFO, `PID -> ${metric.pid}, Type -> ${metric.type}, CPU Usage -> ${JSON.stringify(metric.cpu)}`); - }); -}; - -const logAppEvents = () => { - - const events = [ - 'will-finish-launching', 'ready', 'window-all-closed', 'before-quit', 'will-quit', 'quit', - 'open-file', 'open-url', 'activate', - 'browser-window-created', 'web-contents-created', 'certificate-error', 'login', 'gpu-process-crashed', - 'accessibility-support-changed', 'session-created', 'second-instance' - ]; - - events.forEach((appEvent) => { - app.on(appEvent, () => { - log.send(logLevels.INFO, `App Event Occurred: ${appEvent}`) - }); - }); -}; - -const logProcessInfo = () => { - log.send(logLevels.INFO, `Is default app? ${process.defaultApp}`); - log.send(logLevels.INFO, `Is Mac Store app? ${process.mas}`); - log.send(logLevels.INFO, `Is Windows Store app? ${process.windowsStore}`); - log.send(logLevels.INFO, `Resources Path? ${process.resourcesPath}`); - log.send(logLevels.INFO, `Sandboxed? ${process.sandboxed}`); - log.send(logLevels.INFO, `Chrome Version? ${process.versions.chrome}`); - log.send(logLevels.INFO, `Electron Version? ${process.versions.electron}`); - log.send(logLevels.INFO, `Creation Time? ${process.getCreationTime()}`); -} - -logSystemStats(); -logGPUStats(); -logPodStats(); -logAppMetrics(); -logAppEvents(); -logProcessInfo(); - -module.exports = { - getSystemStats: logSystemStats, - getGPUStats: logGPUStats, - getPodStats: logPodStats, - getAppMetrics: logAppMetrics -}; \ No newline at end of file diff --git a/js/translation/i18n.js b/js/translation/i18n.js deleted file mode 100644 index d225e51d..00000000 --- a/js/translation/i18n.js +++ /dev/null @@ -1,52 +0,0 @@ -const electron = require('electron'); -const app = electron.app; -const path = require("path"); -const fs = require('fs'); - -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); - -let language; -let loadedTranslations = {}; - -const getMessageFor = function(phrase) { - let translation = loadedTranslations[phrase]; - if(translation === undefined) { - translation = phrase; - } - return translation; -}; - -const setLanguage = function(lng) { - language = lng ? lng : 'en-US'; - let file = path.join(__dirname, '..', '..', 'locale', language + '.json'); - if (!fs.existsSync(file)) { - file = path.join(__dirname, '..', '..', 'locale', 'en-US.json'); - } - let data = fs.readFileSync(file, 'utf8'); - try { - loadedTranslations = JSON.parse(data); - } catch (e) { - loadedTranslations = {} - } -}; - -/** - * Returns the current locale - * @return {*|string} - */ -const getLanguage = function() { - let sysLocale; - try { - sysLocale = app.getLocale(); - } catch (err) { - log.send(logLevels.WARN, `i18n: Failed to fetch app.getLocale with an ${err}`); - } - return language || sysLocale || 'en-US'; -}; - -module.exports = { - setLanguage: setLanguage, - getMessageFor: getMessageFor, - getLanguage: getLanguage, -}; \ No newline at end of file diff --git a/js/utils/archiveHandler.js b/js/utils/archiveHandler.js deleted file mode 100644 index 9d3b17d5..00000000 --- a/js/utils/archiveHandler.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const archiver = require('archiver'); - -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); - -/** - * Archives files in the source directory - * that matches the given file extension - * - * @param source {String} source path - * @param destination {String} destination path - * @param fileExtensions {Array} array of file ext - * @return {Promise} - */ -function generateArchiveForDirectory(source, destination, fileExtensions) { - - return new Promise((resolve, reject) => { - - let output = fs.createWriteStream(destination); - let archive = archiver('zip', {zlib: {level: 9}}); - - output.on('close', function () { - log.send(logLevels.INFO, `Successfully archived the files`); - resolve(); - }); - - archive.on('error', function(err){ - log.send(logLevels.INFO, `Error archiving ${JSON.stringify(err)}`); - reject(err); - }); - - archive.pipe(output); - - let files = fs.readdirSync(source); - files - .filter((file) => fileExtensions.indexOf(path.extname(file)) !== -1) - .forEach((file) => { - switch (path.extname(file)) { - case '.log': - archive.file(source + '/' + file, { name: 'logs/' + file }); - break; - case '.dmp': - case '.txt': // on Windows .txt files will be created as part of crash dump - archive.file(source + '/' + file, { name: 'crashes/' + file }); - break; - default: - break; - } - }); - - archive.finalize(); - }); - -} - -module.exports = { - generateArchiveForDirectory: generateArchiveForDirectory -}; \ No newline at end of file diff --git a/js/utils/compareSemVersions.js b/js/utils/compareSemVersions.js deleted file mode 100644 index 88adc2a6..00000000 --- a/js/utils/compareSemVersions.js +++ /dev/null @@ -1,86 +0,0 @@ -// regex match the semver (semantic version) this checks for the pattern X.Y.Z -// ex-valid v1.2.0, 1.2.0, 2.3.4-r51 -const semver = /^v?(?:\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+)(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?)?)?$/i; -const patch = /-([0-9A-Za-z-.]+)/; - -/** - * This function splits the versions - * into major, minor and patch - * @param v - * @returns {T[]} - */ -function split(v) { - const temp = v.replace(/^v/, '').split('.'); - const arr = temp.splice(0, 2); - arr.push(temp.join('.')); - return arr; -} - -function tryParse(v) { - return Number.isNaN(Number(v)) ? v : Number(v); -} - -/** - * This validates the version - * with the semver regex and returns - * -1 if not valid else 1 - * @param version - * @returns {number} - */ -function validate(version) { - if (typeof version !== 'string') { - return -1; - } - if (!semver.test(version)) { - return -1; - } - return 1; -} - -/** - * This function compares the v1 version - * with the v2 version for all major, minor, patch - * if v1 > v2 returns 1 - * if v1 < v2 returns -1 - * if v1 = v2 returns 0 - * @param v1 - * @param v2 - * @returns {number} - */ -function check(v1, v2) { - if (validate(v1) === -1 || validate(v2) === -1) { - return -1; - } - - const s1 = split(v1); - const s2 = split(v2); - - for (let i = 0; i < 3; i++) { - const n1 = parseInt(s1[i] || '0', 10); - const n2 = parseInt(s2[i] || '0', 10); - - if (n1 > n2) return 1; - if (n2 > n1) return -1; - } - - if ([ s1[2], s2[2] ].every(patch.test.bind(patch))) { - const p1 = patch.exec(s1[2])[1].split('.').map(tryParse); - const p2 = patch.exec(s2[2])[1].split('.').map(tryParse); - - for (let k = 0; k < Math.max(p1.length, p2.length); k++) { - if (p1[k] === undefined || typeof p2[k] === 'string' && typeof p1[k] === 'number') return -1; - if (p2[k] === undefined || typeof p1[k] === 'string' && typeof p2[k] === 'number') return 1; - - if (p1[k] > p2[k]) return 1; - if (p2[k] > p1[k]) return -1; - } - } else if ([ s1[2], s2[2] ].some(patch.test.bind(patch))) { - return patch.test(s1[2]) ? -1 : 1; - } - - return 0; -} - -module.exports = { - check -}; diff --git a/js/utils/getCmdLineArg.js b/js/utils/getCmdLineArg.js deleted file mode 100644 index 7f22659f..00000000 --- a/js/utils/getCmdLineArg.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); - -/** - * Search given argv for argName using exact match or starts with. Comparison is case insensitive - * @param {Array} argv Array of strings - * @param {String} argName Arg name to search for. - * @param {Boolean} exactMatch If true then look for exact match otherwise - * try finding arg that starts with argName. - * @return {Array} If found, returns the arg, otherwise null. - */ -function getCmdLineArg(argv, argName, exactMatch) { - if (!Array.isArray(argv)) { - log.send(logLevels.WARN, 'getCmdLineArg: TypeError invalid func arg, must be an array: '+ argv); - return null; - } - let argNameToFind = argName.toLocaleLowerCase(); - for (let i = 0, len = argv.length; i < len; i++) { - let arg = argv[i].toLocaleLowerCase(); - if ((exactMatch && arg === argNameToFind) || - (!exactMatch && arg.startsWith(argNameToFind))) { - return argv[i]; - } - } - - return null; -} - -module.exports = getCmdLineArg; \ No newline at end of file diff --git a/js/utils/getGuid.js b/js/utils/getGuid.js deleted file mode 100644 index 09d14fa5..00000000 --- a/js/utils/getGuid.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -/** - * Generates a guid, - * http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript - * - * @return {String} guid value in string - */ -function getGuid() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, - function (c) { - let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); -} - -module.exports = getGuid; diff --git a/js/utils/getRegistry.js b/js/utils/getRegistry.js deleted file mode 100644 index 0f8d7478..00000000 --- a/js/utils/getRegistry.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; - -const symphonyRegistry = '\\Software\\Symphony\\Symphony\\'; -const { isMac } = require('./misc.js'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); - -let Registry = require('winreg'); -let symphonyRegistryHKCU = new Registry({ - hive: Registry.HKCU, - key: symphonyRegistry -}); - -let symphonyRegistryHKLM = new Registry({ - key: symphonyRegistry -}); - -let symphonyRegistryHKLM6432 = new Registry({ - key: symphonyRegistry.replace('\\Software','\\Software\\WOW6432Node') -}); - -/** - * Reads Windows Registry key. This Registry holds the Symphony registry keys - * that are intended to be used as global (or default) value for all users - * running this app. - */ -let getRegistry = function (name) { - return new Promise(function (resolve, reject) { - if (isMac) { - reject(new Error('registry is not supported for mac osx.')); - return; - } - - //Try to get registry on HKEY_CURRENT_USER - symphonyRegistryHKCU.get(name, function (err1, reg1) { - if (!err1 && reg1 !== null && reg1.value) { - log.send(logLevels.WARN, 'getRegistry: Cannot find ' + name + ' Registry. Using HKCU'); - resolve(reg1.value); - return; - } - - //Try to get registry on HKEY_LOCAL_MACHINE - symphonyRegistryHKLM.get(name, function (err2, reg2) { - if (!err2 && reg2 !== null && reg2.value) { - log.send(logLevels.WARN, 'getRegistry: Cannot find ' + name + ' Registry. Using HKLM'); - resolve(reg2.value); - return; - } - - // Try to get registry on HKEY_LOCAL_MACHINE in case 32bit app installed on 64bit system. - // winreg does not merge keys as normally windows does. - symphonyRegistryHKLM6432.get(name, function (err3, reg3) { - if (!err3 && reg3 !== null && reg3.value) { - resolve(reg3.value); - } else { - reject(new Error('Cannot find PodUrl Registry. Using default url.')); - } - }); - }); - }); - }); -}; - -module.exports = getRegistry; diff --git a/js/utils/isInDisplayBounds.js b/js/utils/isInDisplayBounds.js deleted file mode 100644 index be8e9ac8..00000000 --- a/js/utils/isInDisplayBounds.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -const electron = require('electron'); - - -/** - * Returns true if given rectangle is contained within the workArea of at - * least one of the screens. - * @param {Object} rect - ex:- {x: Number, y: Number, width: Number, height: Number} - * @return {Boolean} true if condition in desc is met. - */ -function isInDisplayBounds(rect) { - if (!rect) { - return false; - } - let displays = electron.screen.getAllDisplays(); - - for(let i = 0, len = displays.length; i < len; i++) { - let workArea = displays[i].workArea; - if (rect.x >= workArea.x && rect.y >= workArea.y && - ((rect.x + rect.width) <= (workArea.x + workArea.width)) && - ((rect.y + rect.height) <= (workArea.y + workArea.height))) { - return true; - } - } - - return false; -} - -module.exports = isInDisplayBounds; diff --git a/js/utils/misc.js b/js/utils/misc.js deleted file mode 100644 index 94340474..00000000 --- a/js/utils/misc.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -const isDevEnv = process.env.ELECTRON_DEV ? - process.env.ELECTRON_DEV.trim().toLowerCase() === 'true' : false; - -const isMac = (process.platform === 'darwin'); -const isWindowsOS = (process.platform === 'win32'); - -const isNodeEnv = !!process.env.NODE_ENV; - -module.exports = { - isDevEnv: isDevEnv, - isMac: isMac, - isWindowsOS: isWindowsOS, - isNodeEnv: isNodeEnv -}; diff --git a/js/utils/stringFormat.js b/js/utils/stringFormat.js deleted file mode 100644 index 9282b2c4..00000000 --- a/js/utils/stringFormat.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Injects content into string - * @param string {String} - * @param data {Object} - content to replace - * @return {*} - */ -function stringFormat(string, data) { - for (let key in data) { - if (Object.prototype.hasOwnProperty.call(data, key)) { - return string.replace(/({([^}]+)})/g, function (i) { - let replacedKey = i.replace(/{/, '').replace(/}/, ''); - if (!data[replacedKey]) { - return i; - } - return data[replacedKey]; - }); - } - } - return null; -} - -module.exports = stringFormat; \ No newline at end of file diff --git a/js/utils/throttle.js b/js/utils/throttle.js deleted file mode 100644 index 6c74e88b..00000000 --- a/js/utils/throttle.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -/** - * throttles calls to given function at most once a second. - * @param {number} throttleTime minimum time between calls - * @param {function} func function to invoke - */ -function throttle(throttleTime, func) { - if (typeof throttleTime !== 'number' || throttleTime <= 0) { - throw Error('throttle: invalid throttleTime arg, must be a number: ' + throttleTime); - } - if (typeof func !== 'function') { - throw Error('throttle: invalid func arg, must be a function: ' + func); - } - let timer, lastInvoke = 0; - return function() { - let args = arguments; - - function invoke(argsToInvoke) { - timer = null; - lastInvoke = Date.now(); - func.apply(null, argsToInvoke); - } - - function cancel() { - if (timer) { - clearTimeout(timer); - } - } - - let now = Date.now(); - if (now - lastInvoke < throttleTime) { - cancel(); - timer = setTimeout(function() { - invoke(args); - }, lastInvoke + throttleTime - now); - } else { - cancel(); - invoke(args); - } - } -} - -module.exports = throttle; diff --git a/js/utils/whitelistHandler.js b/js/utils/whitelistHandler.js deleted file mode 100644 index 01498cf0..00000000 --- a/js/utils/whitelistHandler.js +++ /dev/null @@ -1,166 +0,0 @@ -'use strict'; - -const { getGlobalConfigField } = require('./../config.js'); -const isEqual = require('lodash.isequal'); -const log = require('../log.js'); -const logLevels = require('../enums/logLevels.js'); - -let customTlds = []; - -getGlobalConfigField('customTlds') - .then((domains) => { - - if (domains && Array.isArray(domains) && domains.length > 0) { - log.send(logLevels.INFO, `setting custom tlds that are -> ${domains}`); - customTlds = domains; - } - - }) - .catch((err) => { - log.send(logLevels.INFO, `error setting custom tlds -> ${err}`); - }); - -const urlParts = /^(https?:\/\/)?([^/]*@)?(.+?)(:\d{2,5})?([/?].*)?$/; -const dot = /\./g; - -/** - * Loops through the list of whitelist urls - * @param url {String} - url the electron is navigated to - * @returns {Promise} - */ -function isWhitelisted(url) { - - return new Promise((resolve, reject) => { - getGlobalConfigField('whitelistUrl').then((whitelist) => { - - if (checkWhitelist(url, whitelist)) { - return resolve(); - } - - return reject(new Error('URL does not match with the whitelist')); - - }).catch((err) => { - reject(err); - }); - }); -} - -/** - * Method that compares url against a list of whitelist - * returns true if hostName or domain present in the whitelist - * @param url {String} - url the electron is navigated to - * @param whitelist {String} - coma separated whitelists - * @returns {boolean} - */ -function checkWhitelist(url, whitelist) { - let whitelistArray = whitelist.split(','); - const parsedUrl = parseDomain(url, {customTlds}); - - if (!parsedUrl) { - return false; - } - - if (!whitelist) { - return false; - } - - if (!whitelistArray.length || whitelistArray.indexOf('*') !== -1) { - return true; - } - - return whitelistArray.some((whitelistHost) => { - let parsedWhitelist = parseDomain(whitelistHost); - - if (!parsedWhitelist) { - return false; - } - - return matchDomains(parsedUrl, parsedWhitelist); - }); -} - -/** - * Matches the respective hostName - * @param parsedUrl {Object} - parsed url - * @param parsedWhitelist {Object} - parsed whitelist - * - * example: - * matchDomain({ subdomain: www, domain: example, tld: com }, { subdomain: app, domain: example, tld: com }) - * - * @returns {*} - */ -function matchDomains(parsedUrl, parsedWhitelist) { - - if (isEqual(parsedUrl, parsedWhitelist)) { - return true; - } - - const hostNameFromUrl = parsedUrl.domain + parsedUrl.tld; - const hostNameFromWhitelist = parsedWhitelist.domain + parsedWhitelist.tld; - - if (!parsedWhitelist.subdomain) { - return hostNameFromUrl === hostNameFromWhitelist - } - - return hostNameFromUrl === hostNameFromWhitelist && matchSubDomains(parsedUrl.subdomain, parsedWhitelist.subdomain); - -} - -/** - * Matches the last occurrence in the sub-domain - * @param subDomainUrl {String} - sub-domain from url - * @param subDomainWhitelist {String} - sub-domain from whitelist - * - * example: matchSubDomains('www', 'app') - * - * @returns {boolean} - */ -function matchSubDomains(subDomainUrl, subDomainWhitelist) { - - if (subDomainUrl === subDomainWhitelist) { - return true; - } - - const subDomainUrlArray = subDomainUrl.split('.'); - const lastCharSubDomainUrl = subDomainUrlArray[subDomainUrlArray.length - 1]; - - const subDomainWhitelistArray = subDomainWhitelist.split('.'); - const lastCharWhitelist = subDomainWhitelistArray[subDomainWhitelistArray.length - 1]; - - return lastCharSubDomainUrl === lastCharWhitelist; -} - -/** - * Splits the url into tld, domain, subdomain - * @param url - * @return {{tld: string | *, domain: string | *, subdomain: string}} - */ -function parseDomain(url) { - let urlSplit = url.match(urlParts); - let domain = urlSplit[3]; - - // capture top level domain - const tld = domain.slice(domain.lastIndexOf('.')); - urlSplit = domain.slice(0, -tld.length).split(dot); - - // capture domain - domain = urlSplit.pop(); - - // capture subdomain - const subdomain = urlSplit.join("."); - - return { - tld: tld.trim(), - domain: domain.trim(), - subdomain: subdomain.trim() - } -} - -module.exports = { - isWhitelisted, - parseDomain, - - // items below here are only exported for testing, do NOT use! - checkWhitelist - -}; diff --git a/js/windowMgr.js b/js/windowMgr.js deleted file mode 100644 index cae29893..00000000 --- a/js/windowMgr.js +++ /dev/null @@ -1,1374 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const electron = require('electron'); -const app = electron.app; -const electronSession = electron.session; -const globalShortcut = electron.globalShortcut; -const BrowserWindow = electron.BrowserWindow; -const path = require('path'); -const nodeURL = require('url'); -const querystring = require('querystring'); -const filesize = require('filesize'); -const fetch = require('electron-fetch').default; - -const { getTemplate, getMinimizeOnClose, getTitleBarStyle } = require('./menus/menuTemplate.js'); -const isInDisplayBounds = require('./utils/isInDisplayBounds.js'); -const getGuid = require('./utils/getGuid.js'); -const log = require('./log.js'); -const logLevels = require('./enums/logLevels.js'); -const notify = require('./notify/electron-notify.js'); -const eventEmitter = require('./eventEmitter'); -const throttle = require('./utils/throttle.js'); -const { getConfigField, updateConfigField, readConfigFileSync, getMultipleConfigField, readConfigFromFile } = require('./config.js'); -const { isMac, isWindowsOS, isDevEnv } = require('./utils/misc'); -const { isWhitelisted, parseDomain } = require('./utils/whitelistHandler'); -const { initCrashReporterMain, initCrashReporterRenderer } = require('./crashReporter.js'); -const i18n = require('./translation/i18n'); -const getCmdLineArg = require('./utils/getCmdLineArg'); -const config = readConfigFileSync(); - -let spellchecker; - -// show dialog when certificate errors occur -require('./dialogs/showCertError.js'); -require('./dialogs/showBasicAuth.js'); - -// 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 mainWindow; -let windows = {}; -let willQuitApp = false; -let isOnline = true; -let boundsChangeWindow; -let alwaysOnTop = false; -let position = 'lower-right'; -let display; -let isAutoReload = false; -let devToolsEnabled = true; -let isCustomTitleBarEnabled = true; -let titleBarStyles; -let networkStatusCheckIntervalId; -const networkStatusCheckInterval = 5000; - -const KeyCodes = { - Esc: 27, - Alt: 18, -}; - -// Application menu -let menu; -let lang; - -// note: this file is built using browserify in prebuild step. -const preloadMainScript = path.join(__dirname, 'preload/_preloadMain.js'); - -const MIN_WIDTH = 300; -const MIN_HEIGHT = 300; - -// Default window size for pop-out windows -const DEFAULT_WIDTH = 300; -const DEFAULT_HEIGHT = 600; - -// Certificate transparency whitelist -let ctWhitelist = []; -let lastLoadFailError; - -/** - * Adds a window key - * @param key - * @param browserWin - */ -function addWindowKey(key, browserWin) { - windows[key] = browserWin; -} - -/** - * Removes a window key - * @param key - */ -function removeWindowKey(key) { - delete windows[key]; -} - -/** - * Gets the parsed url - * @returns {Url} - * @param appUrl - */ -function getParsedUrl(appUrl) { - let parsedUrl = nodeURL.parse(appUrl); - if (!parsedUrl.protocol || parsedUrl.protocol !== 'https:') { - parsedUrl.protocol = 'https:'; - parsedUrl.slashes = true - } - let url = nodeURL.format(parsedUrl); - return nodeURL.parse(url); -} - -/** - * Creates and returns the Spellchecker instance - * @returns {SpellCheckHelper} - */ -function getSpellchecker() { - if (!spellchecker) { - // initialze spellchecker - // eslint-disable-next-line global-require - const SpellChecker = require('./spellChecker').SpellCheckHelper; - spellchecker = new SpellChecker(); - } - return spellchecker; -} - -/** - * Method that invokes native module that - * verifies missed spelled word - * @param text {string} - * @returns {*} - */ -function isMisspelled(text) { - if (!spellchecker) { - return false; - } - return spellchecker.isMisspelled(text); -} - -/** - * Creates the main window - * @param initialUrl - */ -function createMainWindow(initialUrl) { - - let configParams = ['mainWinPos', 'isCustomTitleBar', 'locale', 'devToolsEnabled']; - - getMultipleConfigField(configParams) - .then(configData => { - lang = configData && configData.locale || app.getLocale(); - devToolsEnabled = configData && configData.devToolsEnabled; - doCreateMainWindow(initialUrl, configData.mainWinPos, configData.isCustomTitleBar); - }) - .catch(() => { - // failed use default bounds and frame - lang = app.getLocale(); - doCreateMainWindow(initialUrl, null, false); - }); -} - -/** - * ELECTRON-955: Always on Top - Toast notification does not show on top (front) of the Electron app after it is minimized and maximized again - * Bring to front all notification windows - */ -function bringToFrontNotification() { - if (mainWindow && !mainWindow.isDestroyed()) { - let allBrowserWindows = BrowserWindow.getAllWindows(); - const notificationWindow = allBrowserWindows.filter((item) => item.winName === 'notification-window'); - if (mainWindow.isAlwaysOnTop()) { - notificationWindow.forEach((item) => { - if (item && !item.isDestroyed()) { - item.setAlwaysOnTop(true); - } - }); - } - } -} - - -/** - * Creates the main window with bounds - * @param initialUrl - * @param initialBounds - * @param isCustomTitleBar - */ -function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { - let url = initialUrl; - let key = getGuid(); - - // condition whether to enable custom Windows 10 title bar - isCustomTitleBarEnabled = typeof isCustomTitleBar === 'boolean' - && isCustomTitleBar - && isWindowsOS; - - ctWhitelist = config && config.ctWhitelist; - - log.send(logLevels.INFO, `creating main window for ${url}`); - - if (config && config.customFlags) { - - if (config.customFlags.authServerWhitelist && config.customFlags.authServerWhitelist !== "") { - electronSession.defaultSession.allowNTLMCredentialsForDomains(config.customFlags.authServerWhitelist); - } - - } - - let newWinOpts = { - title: 'Symphony', - show: true, - minWidth: MIN_WIDTH, - minHeight: MIN_HEIGHT, - frame: !isCustomTitleBarEnabled, - alwaysOnTop: false, - webPreferences: { - sandbox: true, - nodeIntegration: false, - preload: preloadMainScript, - backgroundThrottling: false - } - }; - - // set size and position - let bounds = initialBounds; - - // if bounds if not fully contained in some display then use default size - // and position. - if (!isInDisplayBounds(bounds) || initialBounds.isMaximized || initialBounds.isFullScreen) { - bounds = null; - } - - if (bounds && bounds.width && bounds.height) { - newWinOpts.width = bounds.width; - newWinOpts.height = bounds.height; - } else { - newWinOpts.width = 900; - newWinOpts.height = 900; - } - - // will center on screen if values not provided - if (bounds && bounds.x && bounds.y) { - newWinOpts.x = bounds.x; - newWinOpts.y = bounds.y; - } - - // will set the main window on top as per the user prefs - if (alwaysOnTop) { - newWinOpts.alwaysOnTop = alwaysOnTop; - } - - // note: augmenting with some custom values - newWinOpts.winKey = key; - - mainWindow = new BrowserWindow(newWinOpts); - mainWindow.winName = 'main'; - logBrowserWindowEvents(mainWindow, mainWindow.winName); - - let throttledMainWinBoundsChange = throttle(1000, saveMainWinBounds); - mainWindow.on('move', throttledMainWinBoundsChange); - mainWindow.on('resize', throttledMainWinBoundsChange); - mainWindow.on('enter-full-screen', () => { - const snackBarContent = i18n.getMessageFor('SnackBar'); - // event sent to renderer process to show snack bar - mainWindow.webContents.send('window-enter-full-screen', { snackBar: snackBarContent }); - }); - mainWindow.on('leave-full-screen', () => { - // event sent to renderer process to remove snack bar - mainWindow.webContents.send('window-leave-full-screen'); - }); - - if (initialBounds) { - // maximizes the application if previously maximized - if (initialBounds.isMaximized) { - mainWindow.maximize(); - } - - // Sets the application to full-screen if previously set to full-screen - if (isMac && initialBounds.isFullScreen) { - mainWindow.setFullScreen(true); - } - } - - // Event needed to hide native menu bar on Windows 10 as we use custom menu bar - mainWindow.webContents.once('did-start-loading', () => { - if ((isCustomTitleBarEnabled || isWindowsOS) && mainWindow && !mainWindow.isDestroyed()) { - mainWindow.setMenuBarVisibility(false); - } - }); - - // content can be cached and will still finish load but - // we might not have network connectivity, so warn the user. - mainWindow.webContents.on('did-finish-load', function () { - // Initialize crash reporter - initCrashReporterMain({ process: 'main window' }); - initCrashReporterRenderer(mainWindow, { process: 'render | main window' }); - - url = mainWindow.webContents.getURL(); - mainWindow.webContents.send('on-page-load'); - // initializes and applies styles required for snack bar - mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '/snackBar/style.css'), 'utf8').toString()); - if (isCustomTitleBarEnabled) { - if (!titleBarStyles) { - let titleBarStylesPath; - let stylesFileName = path.join('config', 'titleBarStyles.css'); - if (isDevEnv) { - titleBarStylesPath = path.join(app.getAppPath(), stylesFileName); - } else { - const execPath = path.dirname(app.getPath('exe')); - titleBarStylesPath = path.join(execPath, stylesFileName); - } - if (fs.existsSync(titleBarStylesPath)) { - titleBarStyles = fs.readFileSync(titleBarStylesPath, 'utf8').toString(); - } else { - titleBarStyles = fs.readFileSync(path.join(__dirname, '/windowsTitleBar/style.css'), 'utf8').toString(); - } - } - mainWindow.webContents.insertCSS(titleBarStyles); - // This is required to initiate Windows title bar only after insertCSS - const titleBarStyle = getTitleBarStyle(); - mainWindow.webContents.send('initiate-windows-title-bar', titleBarStyle); - } - - if (!isOnline) { - return; - } - // updates the notify config with user preference - notify.updateConfig({ position: position, display: display }); - // removes all existing notifications when main window reloads - notify.reset(); - log.send(logLevels.INFO, 'loaded main window url: ' + url); - - // ELECTRON-540 - needed to automatically - // select desktop capture source - const screenShareArg = getCmdLineArg(process.argv, '--auto-select-desktop-capture-source', false); - if (screenShareArg && typeof screenShareArg === 'string') { - mainWindow.webContents.send('screen-share-argv', screenShareArg); - } - - if (config && config.permissions) { - const permission = ' screen sharing'; - const fullMessage = i18n.getMessageFor('Your administrator has disabled ') + permission + '. ' + i18n.getMessageFor('Please contact your admin for help'); - const dialogContent = { type: 'error', title: i18n.getMessageFor('Permission Denied') + '!', message: fullMessage }; - mainWindow.webContents.send('is-screen-share-enabled', config.permissions.media, dialogContent); - } - }); - - mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDesc, validatedURL) => { - log.send(logLevels.ERROR, `Failed to load ${validatedURL}, with an error: ${errorCode}::${errorDesc}`); - lastLoadFailError = errorDesc - }); - - mainWindow.webContents.on('did-stop-loading', () => { - if (mainWindow && !mainWindow.isDestroyed()) { - // Verify if SDA ended up in a blank page - mainWindow.webContents.executeJavaScript('document.location.href').then((href) => { - if (href === 'data:text/html,chromewebdata' || href === 'chrome-error://chromewebdata/') { - const message = i18n.getMessageFor('NetworkError'); - mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '/networkError/style.css'), 'utf8').toString()); - mainWindow.webContents.send('network-error', { message, error: lastLoadFailError || "PAGE_STOPPED_LOADING"}); - isSymphonyReachable(mainWindow, config.url); - } - }).catch((error) => { - log.send(logLevels.ERROR, `Could not read document.location error: ${error}`); - }); - } - }); - - // In case a renderer process crashes, provide an - // option for the user to either reload or close the window - mainWindow.webContents.on('crashed', function (event, killed) { - - log.send(logLevels.INFO, `Main Window crashed! Killed? ${killed}`); - - if (killed) { - return; - } - - const options = { - type: 'error', - title: i18n.getMessageFor('Renderer Process Crashed'), - message: i18n.getMessageFor('Oops! Looks like we have had a crash. Please reload or close this window.'), - buttons: ['Reload', 'Close'] - }; - - electron.dialog.showMessageBox(options, function (index) { - if (index === 0) { - mainWindow.reload(); - } - else { - mainWindow.close(); - } - }); - }); - - registerShortcuts(); - handlePermissionRequests(mainWindow.webContents); - - addWindowKey(key, mainWindow); - mainWindow.loadURL(url); - - setLocale(mainWindow, { language: lang }); - - mainWindow.on('close', function (e) { - if (willQuitApp) { - destroyAllWindows(); - return; - } - - if (getMinimizeOnClose()) { - e.preventDefault(); - mainWindow.minimize(); - } else if (isMac) { - e.preventDefault(); - mainWindow.hide(); - } else { - app.quit(); - } - }); - - function destroyAllWindows() { - let keys = Object.keys(windows); - for (let i = 0, len = keys.length; i < len; i++) { - let winKey = keys[i]; - removeWindowKey(winKey); - } - - mainWindow = null; - } - - mainWindow.on('closed', destroyAllWindows); - - // Manage File Downloads - mainWindow.webContents.session.on('will-download', (event, item, webContents) => { - // When download is in progress, send necessary data to indicate the same - webContents.send('downloadProgress'); - - // Send file path when download is complete - item.once('done', (e, state) => { - if (state === 'completed') { - let data = { - _id: getGuid(), - savedPath: item.getSavePath() ? item.getSavePath() : '', - total: filesize(item.getTotalBytes() ? item.getTotalBytes() : 0), - fileName: item.getFilename() ? item.getFilename() : 'No name' - }; - webContents.send('downloadCompleted', data); - } - }); - }); - - // open external links in default browser - a tag with href='_blank' or window.open - const enforceInheritance = (topWebContents) => { - const handleNewWindow = (webContents) => { - webContents.on('new-window', (event, newWinUrl, frameName, disposition, newWinOptions) => { - log.send(logLevels.INFO, `Creating a pop-out window for the url ${newWinUrl} with frame name ${frameName}, disposition ${disposition} and options ${newWinOptions}`); - if (!newWinOptions.webPreferences) { - // eslint-disable-next-line no-param-reassign - newWinOptions.webPreferences = {}; - } - - Object.assign(newWinOptions.webPreferences, topWebContents); - - let newWinParsedUrl = getParsedUrl(newWinUrl); - let mainWinParsedUrl = getParsedUrl(url); - - let newWinHost = newWinParsedUrl && newWinParsedUrl.host; - let mainWinHost = mainWinParsedUrl && mainWinParsedUrl.host; - - let emptyUrlString = 'about:blank'; - let dispositionWhitelist = ['new-window', 'foreground-tab']; - - let fullMainUrl = `${mainWinParsedUrl.protocol}//${mainWinParsedUrl.host}/`; - - // If the main url and new window url are the same, - // we open that in a browser rather than a separate window - if (newWinUrl === fullMainUrl) { - event.preventDefault(); - openUrlInDefaultBrowser(newWinUrl); - return; - } - - // only allow window.open to succeed is if coming from same host, - // otherwise open in default browser. - if ((newWinHost === mainWinHost || newWinUrl === emptyUrlString || (newWinHost.indexOf(mainWinHost) !== -1 && frameName !== "")) && dispositionWhitelist.includes(disposition)) { - // handle: window.open - - if (!frameName) { - // abort - no frame name provided. - return; - } - - log.send(logLevels.INFO, 'creating pop-out window url: ' + newWinParsedUrl); - - let x = 0; - let y = 0; - - let width = newWinOptions.width || DEFAULT_WIDTH; - let height = newWinOptions.height || DEFAULT_HEIGHT; - - // try getting x and y position from query parameters - let query = newWinParsedUrl && querystring.parse(newWinParsedUrl.query); - if (query && query.x && query.y) { - let newX = Number.parseInt(query.x, 10); - let newY = Number.parseInt(query.y, 10); - - let newWinRect = { x: newX, y: newY, width, height }; - - // only accept if both are successfully parsed. - if (Number.isInteger(newX) && Number.isInteger(newY) && - isInDisplayBounds(newWinRect)) { - x = newX; - y = newY; - } else { - x = 0; - y = 0; - } - } else { - // create new window at slight offset from main window. - ({ x, y } = getWindowSizeAndPosition(mainWindow)); - x += 50; - y += 50; - } - - /* eslint-disable no-param-reassign */ - newWinOptions.x = x; - newWinOptions.y = y; - newWinOptions.width = Math.max(width, DEFAULT_WIDTH); - newWinOptions.height = Math.max(height, DEFAULT_HEIGHT); - newWinOptions.minWidth = MIN_WIDTH; - newWinOptions.minHeight = MIN_HEIGHT; - newWinOptions.alwaysOnTop = alwaysOnTop; - newWinOptions.frame = true; - newWinOptions.parent = null; - newWinOptions.webPreferences.backgroundThrottling = false; - - let newWinKey = getGuid(); - - newWinOptions.winKey = newWinKey; - /* eslint-enable no-param-reassign */ - - let childWebContents = newWinOptions.webContents; - - // Event needed to hide native menu bar - childWebContents.once('did-start-loading', () => { - let browserWin = BrowserWindow.fromWebContents(childWebContents); - if (isWindowsOS && browserWin && !browserWin.isDestroyed()) { - browserWin.setMenuBarVisibility(false); - } - }); - - childWebContents.once('did-finish-load', function () { - let browserWin = BrowserWindow.fromWebContents(childWebContents); - - if (browserWin) { - log.send(logLevels.INFO, `loaded pop-out window url: ${JSON.stringify(newWinParsedUrl)}`); - - browserWin.webContents.send('on-page-load'); - // applies styles required for snack bar - browserWin.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '/snackBar/style.css'), 'utf8').toString()); - - initCrashReporterMain({ process: 'pop-out window' }); - initCrashReporterRenderer(browserWin, { process: 'render | pop-out window' }); - - browserWin.winName = frameName; - browserWin.setAlwaysOnTop(alwaysOnTop); - logBrowserWindowEvents(browserWin, browserWin.winName); - - let handleChildWindowCrashEvent = (e, killed) => { - log.send(logLevels.INFO, `Child Window crashed! Killed? ${killed}`); - - if (killed) { - return; - } - const options = { - type: 'error', - title: i18n.getMessageFor('Renderer Process Crashed'), - message: i18n.getMessageFor('Oops! Looks like we have had a crash. Please reload or close this window.'), - buttons: ['Reload', 'Close'] - }; - - let childBrowserWindow = BrowserWindow.fromWebContents(e.sender); - if (childBrowserWindow && !childBrowserWindow.isDestroyed()) { - electron.dialog.showMessageBox(childBrowserWindow, options, function (index) { - if (index === 0) { - childBrowserWindow.reload(); - } else { - childBrowserWindow.close(); - } - }); - } - }; - - browserWin.webContents.on('crashed', handleChildWindowCrashEvent); - browserWin.webContents.on('will-navigate', (e, navigatedURL) => { - if (!navigatedURL.startsWith('http' || 'https')) { - e.preventDefault(); - } - }); - - // In case we navigate to an external link from inside a pop-out, - // we open that link in an external browser rather than creating - // a new window - if (browserWin.webContents) { - handleNewWindow(browserWin.webContents); - } - - addWindowKey(newWinKey, browserWin); - - // Method that sends bound changes as soon - // as a new window is created - // issue https://perzoinc.atlassian.net/browse/ELECTRON-172 - sendChildWinBoundsChange(browserWin); - - // throttle full screen - let throttledFullScreen = throttle(1000, - handleChildWindowFullScreen.bind(null, browserWin)); - - // throttle leave full screen - let throttledLeaveFullScreen = throttle(1000, - handleChildWindowLeaveFullScreen.bind(null, browserWin)); - - // throttle changes so we don't flood client. - let throttledBoundsChange = throttle(1000, - sendChildWinBoundsChange.bind(null, browserWin)); - - browserWin.on('move', throttledBoundsChange); - browserWin.on('resize', throttledBoundsChange); - browserWin.on('enter-full-screen', throttledFullScreen); - browserWin.on('leave-full-screen', throttledLeaveFullScreen); - - let handleChildWindowClosed = () => { - removeWindowKey(newWinKey); - browserWin.removeListener('move', throttledBoundsChange); - browserWin.removeListener('resize', throttledBoundsChange); - browserWin.removeListener('enter-full-screen', throttledFullScreen); - browserWin.removeListener('leave-full-screen', throttledLeaveFullScreen); - }; - - browserWin.on('close', () => { - browserWin.webContents.removeListener('crashed', handleChildWindowCrashEvent); - }); - - browserWin.once('closed', () => { - handleChildWindowClosed(); - }); - - handlePermissionRequests(browserWin.webContents); - - if (!isDevEnv) { - browserWin.webContents.session.setCertificateVerifyProc(handleCertificateTransparencyChecks); - } - } - }); - } else { - event.preventDefault(); - openUrlInDefaultBrowser(newWinUrl); - } - }); - }; - handleNewWindow(topWebContents); - }; - - if (!isDevEnv) { - mainWindow.webContents.session.setCertificateVerifyProc(handleCertificateTransparencyChecks); - } - - // enforce main window's webPreferences to child windows - if (mainWindow.webContents) { - enforceInheritance(mainWindow.webContents); - } - - // whenever the main window is navigated for ex: window.location.href or url redirect - mainWindow.webContents.on('will-navigate', function (event, navigatedURL) { - - if (!navigatedURL.startsWith('http' || 'https')) { - event.preventDefault(); - return; - } - - isWhitelisted(navigatedURL) - .catch(() => { - event.preventDefault(); - electron.dialog.showMessageBox(mainWindow, { - type: 'warning', - buttons: ['Ok'], - title: i18n.getMessageFor('Not Allowed'), - message: i18n.getMessageFor('Sorry, you are not allowed to access this website') + ' (' + navigatedURL + '), ' + i18n.getMessageFor('please contact your administrator for more details'), - }); - }); - }); - - /** - * Register shortcuts for the app - */ - function registerShortcuts() { - - function devTools() { - const focusedWindow = BrowserWindow.getFocusedWindow(); - devToolsEnabled = readConfigFromFile('devToolsEnabled'); - if (focusedWindow && !focusedWindow.isDestroyed()) { - if (devToolsEnabled) { - focusedWindow.webContents.toggleDevTools(); - } else { - log.send(logLevels.INFO, `dev tools disabled for ${focusedWindow.winName} window`); - focusedWindow.webContents.closeDevTools(); - electron.dialog.showMessageBox(focusedWindow, { - type: 'warning', - buttons: ['Ok'], - title: i18n.getMessageFor('Dev Tools disabled'), - message: i18n.getMessageFor('Dev Tools has been disabled! Please contact your system administrator to enable it!'), - }); - } - } - } - - // This will initially register the global shortcut - globalShortcut.register(isMac ? 'Cmd+Alt+I' : 'Ctrl+Shift+I', devTools); - - app.on('browser-window-focus', function () { - globalShortcut.register(isMac ? 'Cmd+Alt+I' : 'Ctrl+Shift+I', devTools); - }); - - app.on('browser-window-blur', function () { - globalShortcut.unregister(isMac ? 'Cmd+Alt+I' : 'Ctrl+Shift+I'); - }); - - } - - /** - * Sets permission requests for the window - * @param webContents Web contents of the window - */ - function handlePermissionRequests(webContents) { - - let session = webContents.session; - - getConfigField('permissions') - .then((permissions) => { - - if (!permissions) { - log.send(logLevels.ERROR, 'permissions configuration is invalid, so, everything will be true by default!'); - return; - } - - session.setPermissionRequestHandler((sessionWebContents, permission, callback) => { - - function handleSessionPermissions(userPermission, message, cb) { - - log.send(logLevels.INFO, 'permission is -> ' + userPermission); - - if (!userPermission) { - const fullMessage = `${i18n.getMessageFor('Your administrator has disabled')} ${message}. ${i18n.getMessageFor('Please contact your admin for help')}`; - const browserWindow = BrowserWindow.getFocusedWindow(); - if (browserWindow && !browserWindow.isDestroyed()) { - electron.dialog.showMessageBox(browserWindow, { type: 'error', title: `${i18n.getMessageFor('Permission Denied')}!`, message: fullMessage }); - } - } - - return cb(userPermission); - - } - - let PERMISSION_MEDIA = 'media'; - let PERMISSION_LOCATION = 'geolocation'; - let PERMISSION_NOTIFICATIONS = 'notifications'; - let PERMISSION_MIDI_SYSEX = 'midiSysex'; - let PERMISSION_POINTER_LOCK = 'pointerLock'; - let PERMISSION_FULL_SCREEN = 'fullscreen'; - let PERMISSION_OPEN_EXTERNAL = 'openExternal'; - - switch (permission) { - - case PERMISSION_MEDIA: - return handleSessionPermissions(permissions.media, 'sharing your camera, microphone, and speakers', callback); - - case PERMISSION_LOCATION: - return handleSessionPermissions(permissions.geolocation, 'sharing your location', callback); - - case PERMISSION_NOTIFICATIONS: - return handleSessionPermissions(permissions.notifications, 'notifications', callback); - - case PERMISSION_MIDI_SYSEX: - return handleSessionPermissions(permissions.midiSysex, 'MIDI Sysex', callback); - - case PERMISSION_POINTER_LOCK: - return handleSessionPermissions(permissions.pointerLock, 'Pointer Lock', callback); - - case PERMISSION_FULL_SCREEN: - return handleSessionPermissions(permissions.fullscreen, 'Full Screen', callback); - - case PERMISSION_OPEN_EXTERNAL: - return handleSessionPermissions(permissions.openExternal, 'Opening External App', callback); - - default: - return callback(false); - } - - }); - - }).catch((error) => { - log.send(logLevels.ERROR, 'unable to get permissions configuration, so, everything will be true by default! ' + error); - }) - - } - - function handleCertificateTransparencyChecks(request, callback) { - - const { hostname: hostUrl, errorCode } = request; - - if (errorCode === 0) { - return callback(0); - } - - let { tld, domain } = parseDomain(hostUrl); - let host = domain + tld; - - if (ctWhitelist && Array.isArray(ctWhitelist) && ctWhitelist.length > 0 && ctWhitelist.indexOf(host) > -1) { - return callback(0); - } - - return callback(-2); - } - -} - -/** - * ELECTRON-956: App is not minimized upon "Configure Desktop Alert Position" modal when "Always on Top" = True - */ -app.on('browser-window-created', (event, window) => { - const parentWindow = window.getParentWindow(); - if (parentWindow && !parentWindow.isDestroyed()) { - if (parentWindow.winName === 'main') { - window.setMinimizable(false); - window.setMaximizable(false); - } - } -}); - -/** - * Handles the event before-quit emitted by electron - */ -app.on('before-quit', function () { - willQuitApp = true; -}); - -/** - * Saves the main window bounds - */ -function saveMainWinBounds() { - let newBounds = getWindowSizeAndPosition(mainWindow); - - // set application full-screen and maximized state - if (mainWindow && !mainWindow.isDestroyed()) { - newBounds.isMaximized = mainWindow.isMaximized(); - newBounds.isFullScreen = mainWindow.isFullScreen(); - bringToFrontNotification(); - } - - if (newBounds) { - updateConfigField('mainWinPos', newBounds); - } -} - -/** - * Gets the main window - * @returns {*} - */ -function getMainWindow() { - return mainWindow; -} - -/** - * Gets the application menu - * @returns {*} - */ -function getMenu() { - return menu; -} - -/** - * Gets a window's size and position - * @param window - * @returns {*} - */ -function getWindowSizeAndPosition(window) { - if (window && !window.isDestroyed()) { - let newPos = window.getPosition(); - let newSize = window.getSize(); - - if (newPos && newPos.length === 2 && - newSize && newSize.length === 2) { - return { - x: newPos[0], - y: newPos[1], - width: newSize[0], - height: newSize[1], - }; - } - } - - return null; -} - -/** - * Shows the main window - */ -function showMainWindow() { - if (mainWindow) { - mainWindow.show(); - } -} - -/** - * Tells if a window is the main window - * @param win - * @returns {boolean} - */ -function isMainWindow(win) { - return mainWindow === win; -} - -/** - * Checks if the window and a key has a window - * @param win - * @param winKey - * @returns {*} - */ -function hasWindow(win, winKey) { - if (win instanceof BrowserWindow) { - let browserWin = windows[winKey]; - return browserWin && win === browserWin; - } - - return false; -} - -/** - * Sets if a user is online - * @param status - */ -function setIsOnline(status) { - isOnline = status; -} - -/** - * Returns user's online status - * @return {boolean} - */ -function getIsOnline() { - return isOnline; -} - -/** - * Tries finding a window we have created with given name. If found, then - * brings to front and gives focus. - * @param {String} windowName Name of target window. Note: main window has - * name 'main'. - * @param {Boolean} shouldFocus whether to get window to focus or just show - * without giving focus - */ -function activate(windowName, shouldFocus = true) { - - // don't activate when the app is reloaded programmatically - // Electron-136 - if (isAutoReload) { - return null; - } - - for (const key in windows) { - if (Object.prototype.hasOwnProperty.call(windows, key)) { - const window = windows[ key ]; - if (window && !window.isDestroyed() && window.winName === windowName) { - - // Bring the window to the top without focusing - // Flash task bar icon in Windows for windows - if (!shouldFocus) { - return isMac ? window.showInactive() : window.flashFrame(true); - } - - // Note: On window just focusing will preserve window snapped state - // Hiding the window and just calling the focus() won't display the window - if (isWindowsOS) { - return window.isMinimized() ? window.restore() : window.focus(); - } - - return window.isMinimized() ? window.restore() : window.show(); - } - } - } - return null; -} - -/** - * Sets if is auto reloading the app - * @param reload - */ -function setIsAutoReload(reload) { - if (typeof reload === 'boolean') { - isAutoReload = reload - } -} - -/** - * name of renderer window to notify when bounds of child window changes. - * @param {object} window Renderer window to use IPC with to inform about size/ - * position change. - */ -function setBoundsChangeWindow(window) { - boundsChangeWindow = window; -} - -/** - * Called when bounds of child window changes size/position - * @param {object} window Child window which has changed size/position. - */ -function sendChildWinBoundsChange(window) { - let newBounds = getWindowSizeAndPosition(window); - if (newBounds && boundsChangeWindow) { - newBounds.windowName = window.winName; - // ipc msg back to renderer to inform bounds has changed. - boundsChangeWindow.send('boundsChange', newBounds); - } -} - -/** - * Called when the child window is set to full screen - */ -function handleChildWindowFullScreen(browserWindow) { - const snackBarContent = i18n.getMessageFor('SnackBar'); - browserWindow.webContents.send('window-enter-full-screen', { snackBar: snackBarContent }); -} - -/** - * Called when the child window left full screen - */ -function handleChildWindowLeaveFullScreen(browserWindow) { - browserWindow.webContents.send('window-leave-full-screen'); -} - -/** - * Opens an external url in the system's default browser - * @param urlToOpen - */ -function openUrlInDefaultBrowser(urlToOpen) { - if (urlToOpen) { - electron.shell.openExternal(urlToOpen); - } -} - -/** - * Called when an event is received from menu - * @param {boolean} boolean whether to enable or disable alwaysOnTop. - * @param {boolean} shouldActivateMainWindow whether to activate main window - */ -function isAlwaysOnTop(boolean, shouldActivateMainWindow = true) { - alwaysOnTop = boolean; - let browserWins = BrowserWindow.getAllWindows(); - if (browserWins.length > 0) { - browserWins - .filter((browser) => typeof browser.notfyObj !== 'object') - .forEach(function (browser) { - browser.setAlwaysOnTop(boolean); - }); - - // An issue where changing the alwaysOnTop property - // focus the pop-out window - // Issue - Electron-209/470 - if (mainWindow && mainWindow.winName && shouldActivateMainWindow) { - activate(mainWindow.winName); - } - } -} - -// node event emitter to update always on top -eventEmitter.on('isAlwaysOnTop', (params) => { - isAlwaysOnTop(params.isAlwaysOnTop, params.shouldActivateMainWindow); - log.send(logLevels.INFO, `Updating settings for always on top ${params}`); -}); - -// node event emitter for notification settings -eventEmitter.on('notificationSettings', (notificationSettings) => { - position = notificationSettings.position; - display = notificationSettings.display; -}); - -/** - * Sets the locale settings - * - * @param browserWindow {Electron.BrowserWindow} - * @param opts {Object} - * @param opts.language {String} - locale string ex: en-US - */ -function setLocale(browserWindow, opts) { - const language = opts && opts.language || app.getLocale(); - const localeContent = {}; - log.send(logLevels.INFO, `language changed to ${language}. Updating menu and user config`); - - setLanguage(language); - if (browserWindow && !browserWindow.isDestroyed()) { - if (isMainWindow(browserWindow)) { - - menu = electron.Menu.buildFromTemplate(getTemplate(app)); - electron.Menu.setApplicationMenu(menu); - - if (isWindowsOS) { - browserWindow.setMenuBarVisibility(false); - - // update locale for custom title bar - if (isCustomTitleBarEnabled) { - localeContent.titleBar = i18n.getMessageFor('TitleBar'); - } - } - } - - localeContent.contextMenu = i18n.getMessageFor('ContextMenu'); - - localeContent.downloadManager = i18n.getMessageFor('DownloadManager'); - if (isMac) { - localeContent.downloadManager['Show in Folder'] = localeContent.downloadManager['Reveal in Finder']; - } - - browserWindow.webContents.send('locale-changed', localeContent); - } - - updateConfigField('locale', language); -} - -/** - * Sets language for i18n - * @param language {String} - locale string ex: en-US - */ -function setLanguage(language) { - i18n.setLanguage(language); -} - -/** - * Method that gets invoked when an external display - * is removed using electron 'display-removed' event. - */ -function verifyDisplays() { - - log.send(logLevels.INFO, `Display removed`); - // This is only for Windows, macOS handles this by itself - if (!mainWindow || isMac) { - return; - } - - const bounds = mainWindow.getBounds(); - if (bounds) { - let isXAxisValid = true; - let isYAxisValid = true; - - // checks to make sure the x,y are valid pairs - if ((bounds.x === undefined && (bounds.y || bounds.y === 0))) { - isXAxisValid = false; - } - if ((bounds.y === undefined && (bounds.x || bounds.x === 0))) { - isYAxisValid = false; - } - - if (!isXAxisValid && !isYAxisValid) { - return; - } - - let externalDisplay = checkExternalDisplay(bounds); - - // If external window doesn't exists, reposition main window - if (!externalDisplay) { - repositionMainWindow(); - } - } -} - -/** - * Method that verifies if wrapper exists in any of the available - * external display by comparing the app bounds with the display bounds - * if not exists returns false otherwise true - * @param appBounds {Electron.Rectangle} - current electron wrapper bounds - * @returns {boolean} - */ -function checkExternalDisplay(appBounds) { - const x = appBounds.x; - const y = appBounds.y; - const width = appBounds.width; - const height = appBounds.height; - const factor = 0.2; - - // Loops through all the available displays and - // verifies if the wrapper exists within the display bounds - // returns false if not exists otherwise true - return !!electron.screen.getAllDisplays().find(({ bounds }) => { - - const leftMost = x + (width * factor); - const topMost = y + (height * factor); - const rightMost = x + width - (width * factor); - const bottomMost = y + height - (height * factor); - - if (leftMost < bounds.x || topMost < bounds.y) { - return false; - } - - return !(rightMost > bounds.x + bounds.width || bottomMost > bounds.y + bounds.height); - - }); -} - -/** - * Method that resets the main window bounds when an external display - * was removed and if the wrapper was contained within that bounds - */ -function repositionMainWindow() { - - const { workArea } = electron.screen.getPrimaryDisplay(); - const bounds = workArea; - - if (!bounds) { - return; - } - - const windowWidth = Math.round(bounds.width * 0.6); - const windowHeight = Math.round(bounds.height * 0.8); - - // Calculating the center of the primary display - // to place the wrapper - const centerX = bounds.x + bounds.width / 2.0; - const centerY = bounds.y + bounds.height / 2.0; - const x = Math.round(centerX - (windowWidth / 2.0)); - const y = Math.round(centerY - (windowHeight / 2.0)); - - let rectangle = { x, y, width: windowWidth, height: windowHeight }; - - // resetting the main window bounds - if (mainWindow) { - if (!mainWindow.isVisible()) { - mainWindow.show(); - } - - if (mainWindow.isMinimized()) { - mainWindow.restore(); - } - - mainWindow.focus(); - mainWindow.flashFrame(false); - mainWindow.setBounds(rectangle, true); - } -} - -/** - * Method that handles key press - * @param keyCode {number} - */ -function handleKeyPress(keyCode) { - switch (keyCode) { - case KeyCodes.Esc: { - const focusedWindow = BrowserWindow.getFocusedWindow(); - - if (focusedWindow && !focusedWindow.isDestroyed() && focusedWindow.isFullScreen()) { - focusedWindow.setFullScreen(false); - } - break; - } - case KeyCodes.Alt: - if (isWindowsOS && !isCustomTitleBarEnabled) { - popupMenu(); - } - break; - default: - break; - } -} - -/** - * Finds all the child window and closes it - */ -function cleanUpChildWindows() { - const browserWindows = BrowserWindow.getAllWindows(); - notify.resetAnimationQueue(); - if (browserWindows && browserWindows.length) { - browserWindows.forEach(browserWindow => { - // Closes only child windows - if (browserWindow && !browserWindow.isDestroyed() && browserWindow.winName !== 'main') { - // clean up notification windows - if (browserWindow.winName === 'notification-window') { - notify.closeAll(); - } else { - browserWindow.close(); - } - } - }); - } -} - -/** - * Method that popup the menu on top of the native title bar - * whenever Alt key is pressed - */ -function popupMenu() { - const focusedWindow = BrowserWindow.getFocusedWindow(); - if (mainWindow && !mainWindow.isDestroyed() && isMainWindow(focusedWindow)) { - const { x, y } = mainWindow.isFullScreen() ? { x: 0, y: 0 } : { x: 10, y: -20 }; - const popupOpts = { browserWin: mainWindow, x, y }; - getMenu().popup(popupOpts); - } -} - -const logBrowserWindowEvents = (browserWindow, windowName) => { - - const events = [ - 'page-title-updated', 'close', 'session-end', 'unresponsive', 'responsive', 'blur', 'focus', - 'show', 'hide', 'ready-to-show', 'maximize', 'unmaximize', 'minimize', 'restore', 'resize', 'move', 'moved', - 'enter-full-screen', 'leave-full-screen', 'enter-html-full-screen', 'leave-html-full-screen', 'app-command' - ]; - - events.forEach((browserWindowEvent) => { - browserWindow.on(browserWindowEvent, () => { - log.send(logLevels.INFO, `Browser Window Event Occurred for window (${windowName}) -> ${browserWindowEvent}`); - }); - }); - -}; - -/** - * Validates the network by fetching the pod url - * every 5sec, on active reloads the given window - * - * @param window - * @param url - */ -const isSymphonyReachable = (window, url) => { - if (networkStatusCheckIntervalId) { - return; - } - networkStatusCheckIntervalId = setInterval(() => { - fetch(config.url).then((rsp) => { - if (rsp.status === 200 && isOnline) { - window.loadURL(url); - if (networkStatusCheckIntervalId) { - clearInterval(networkStatusCheckIntervalId); - networkStatusCheckIntervalId = null; - } - } else { - log.send(logLevels.INFO, `Symphony down! statusCode: ${rsp.status} is online: ${isOnline}`); - } - }).catch((error) => { - log.send(logLevels.INFO, `Network status check: No active network connection ${error}`); - }); - }, networkStatusCheckInterval); -}; - -/** - * Clears the network status check interval - */ -const cancelNetworkStatusCheck = () => { - if (networkStatusCheckIntervalId) { - clearInterval(networkStatusCheckIntervalId); - networkStatusCheckIntervalId = null; - } -}; - -module.exports = { - createMainWindow: createMainWindow, - getMainWindow: getMainWindow, - showMainWindow: showMainWindow, - isMainWindow: isMainWindow, - hasWindow: hasWindow, - setIsOnline: setIsOnline, - activate: activate, - setBoundsChangeWindow: setBoundsChangeWindow, - verifyDisplays: verifyDisplays, - getMenu: getMenu, - setIsAutoReload: setIsAutoReload, - handleKeyPress: handleKeyPress, - cleanUpChildWindows: cleanUpChildWindows, - setLocale: setLocale, - getIsOnline: getIsOnline, - getSpellchecker: getSpellchecker, - isMisspelled: isMisspelled, - cancelNetworkStatusCheck: cancelNetworkStatusCheck, -}; diff --git a/js/windowsTitleBar/contents.js b/js/windowsTitleBar/contents.js deleted file mode 100644 index 2dda87a0..00000000 --- a/js/windowsTitleBar/contents.js +++ /dev/null @@ -1,144 +0,0 @@ -const titleBar = (` -
-
- -
-
- - - - Symphony_logo - - - - - -

Symphony

-
- -`); - -const button = (` -
-
- -
-
- -
-
- -
-
-`); - -const unMaximizeButton = (` - - - -`); - -const maximizeButton = (` - - - -`); - - -module.exports = { - titleBar: titleBar, - button: button, - unMaximizeButton: unMaximizeButton, - maximizeButton: maximizeButton -}; \ No newline at end of file diff --git a/js/windowsTitleBar/index.js b/js/windowsTitleBar/index.js deleted file mode 100644 index 45856e6e..00000000 --- a/js/windowsTitleBar/index.js +++ /dev/null @@ -1,286 +0,0 @@ -const { ipcRenderer, remote } = require('electron'); -const apiEnums = require('../enums/api.js'); -const apiCmds = apiEnums.cmds; -const apiName = apiEnums.apiName; -const htmlContents = require('./contents'); -const titleBarStyles = require('../enums/titleBarStyles'); - -// Default title bar height -const titleBarHeight = '32px'; - -class TitleBar { - - constructor() { - this.window = remote.getCurrentWindow(); - this.domParser = new DOMParser(); - - const titleBarParsed = this.domParser.parseFromString(htmlContents.titleBar, 'text/html'); - this.titleBar = titleBarParsed.getElementById('title-bar'); - } - - initiateWindowsTitleBar(titleBarStyle) { - - const actionItemsParsed = this.domParser.parseFromString(htmlContents.button, 'text/html'); - - if (titleBarStyle === titleBarStyles.CUSTOM) { - const buttons = actionItemsParsed.getElementsByClassName('action-items'); - - let items = Array.from(buttons[0].children); - for (let i of items) { - this.titleBar.appendChild(i); - } - } - - const updateIcon = TitleBar.updateIcons; - - // Event to capture and update icons - this.window.on('maximize', updateIcon.bind(this, true)); - this.window.on('unmaximize', updateIcon.bind(this, false)); - this.window.on('enter-full-screen', this.updateTitleBar.bind(this, true)); - this.window.on('leave-full-screen', this.updateTitleBar.bind(this, false)); - - window.addEventListener('beforeunload', () => { - this.window.removeListener('maximize', updateIcon); - this.window.removeListener('unmaximize', updateIcon); - this.window.removeListener('enter-full-screen', this.updateTitleBar); - this.window.removeListener('leave-full-screen', this.updateTitleBar); - }); - - document.body.appendChild(this.titleBar); - - switch (titleBarStyle) { - case titleBarStyles.CUSTOM: - TitleBar.setTitleBarTitle(); - TitleBar.addWindowBorders(); - break; - case titleBarStyles.NATIVE: - TitleBar.hideTitleContainer(); - break; - default: - break; - } - this.hamburgerMenuButton = document.getElementById('hamburger-menu-button'); - this.minimizeButton = document.getElementById('title-bar-minimize-button'); - this.maximizeButton = document.getElementById('title-bar-maximize-button'); - this.closeButton = document.getElementById('title-bar-close-button'); - - this.initiateEventListeners(); - - this.updateTitleBar(this.window.isFullScreen()); - } - - /** - * Method that attaches Event Listeners for elements - */ - initiateEventListeners() { - attachEventListeners(this.titleBar, 'dblclick', this.maximizeOrUnmaximize.bind(this)); - attachEventListeners(this.hamburgerMenuButton, 'click', this.popupMenu.bind(this)); - attachEventListeners(this.closeButton, 'click', this.closeWindow.bind(this)); - attachEventListeners(this.maximizeButton, 'click', this.maximizeOrUnmaximize.bind(this)); - attachEventListeners(this.minimizeButton, 'click', this.minimize.bind(this)); - attachEventListeners(this.minimizeButton, 'contextmenu', this.removeContextMenu.bind(this)); - attachEventListeners(this.maximizeButton, 'contextmenu', this.removeContextMenu.bind(this)); - attachEventListeners(this.closeButton, 'contextmenu', this.removeContextMenu.bind(this)); - - attachEventListeners(this.hamburgerMenuButton, 'mousedown', TitleBar.handleMouseDown.bind(this)); - attachEventListeners(this.closeButton, 'mousedown', TitleBar.handleMouseDown.bind(this)); - attachEventListeners(this.maximizeButton, 'mousedown', TitleBar.handleMouseDown.bind(this)); - attachEventListeners(this.minimizeButton, 'mousedown', TitleBar.handleMouseDown.bind(this)); - } - - /** - * Update button's title w.r.t current locale - * @param content {Object} - */ - updateLocale(content) { - this.hamburgerMenuButton.title = content.Menu || 'Menu'; - this.minimizeButton.title = content.Minimize || 'Minimize'; - this.maximizeButton.title = content.Maximize || 'Maximize'; - this.closeButton.title = content.Close || 'Close'; - } - - /** - * Method that remove context menu - * ELECTRON-769: Hamburger Menu - Copy/ Reload in menu context displays when right - * clicking on minimize, maximize, close button of the hamburger menu - * - */ - // eslint-disable-next-line class-methods-use-this - removeContextMenu(event) { - event.preventDefault(); - return false; - } - - /** - * Method that adds borders - */ - static addWindowBorders() { - const borderBottom = document.createElement('div'); - borderBottom.className = 'bottom-window-border'; - - document.body.appendChild(borderBottom); - document.body.classList.add('window-border'); - } - - /** - * Method that sets the title bar title - * from document.title - */ - static setTitleBarTitle() { - const titleBarTitle = document.getElementById('title-bar-title'); - - if (titleBarTitle) { - titleBarTitle.innerText = document.title || 'Symphony'; - } - } - - /** - * Method that hides the title container - * if the title bar style is NATIVE - */ - static hideTitleContainer() { - const titleContainer = document.getElementById('title-container'); - - if (titleContainer) { - titleContainer.style.visibility = 'hidden'; - } - } - - /** - * Method that updates the state of the maximize or - * unmaximize icons - * @param isMaximized - */ - static updateIcons(isMaximized) { - const button = document.getElementById('title-bar-maximize-button'); - - if (!button) { - return - } - - if (isMaximized) { - button.innerHTML = htmlContents.unMaximizeButton; - } else { - button.innerHTML = htmlContents.maximizeButton; - } - } - - /** - * Method that updates the title bar display property - * based on the full screen event - * @param isFullScreen {Boolean} - */ - updateTitleBar(isFullScreen) { - if (isFullScreen) { - this.titleBar.style.display = 'none'; - updateContentHeight('0px'); - } else { - this.titleBar.style.display = 'flex'; - updateContentHeight(); - } - - } - - /** - * Method that popup the application menu - */ - popupMenu() { - if (this.isValidWindow()) { - ipcRenderer.send(apiName, { - cmd: apiCmds.popupMenu - }); - } - } - - /** - * Method that minimizes browser window - */ - minimize() { - if (this.isValidWindow()) { - this.window.minimize(); - } - } - - /** - * Method that maximize or unmaximize browser window - */ - maximizeOrUnmaximize() { - - if (!this.isValidWindow()) { - return; - } - - if (this.window.isMaximized()) { - this.window.unmaximize(); - } else { - this.window.maximize(); - } - } - - /** - * Method that closes the browser window - */ - closeWindow() { - if (this.isValidWindow()) { - this.window.close(); - } - } - - /** - * Verifies if the window exists and is not destroyed - * @returns {boolean} - */ - isValidWindow() { - return !!(this.window && !this.window.isDestroyed()); - } - - /** - * Prevent default to make sure buttons don't take focus - * @param e - */ - static handleMouseDown(e) { - e.preventDefault(); - } -} - -/** - * Will attach event listeners for a given element - * @param element - * @param eventName - * @param func - */ -function attachEventListeners(element, eventName, func) { - - if (!element || !eventName) { - return; - } - - eventName.split(" ").forEach((name) => { - element.addEventListener(name, func, false); - }); -} - -/** - * Method that adds margin property to the push - * the client content below the title bar - * @param height - */ -function updateContentHeight(height = titleBarHeight) { - const contentWrapper = document.getElementById('content-wrapper'); - const titleBar = document.getElementById('title-bar'); - - if (!titleBar) { - return; - } - - if (contentWrapper) { - contentWrapper.style.marginTop = titleBar ? height : '0px'; - document.body.style.removeProperty('margin-top'); - } else { - document.body.style.marginTop = titleBar ? height : '0px' - } -} - -module.exports = { - TitleBar -}; \ No newline at end of file diff --git a/js/windowsTitleBar/style.css b/js/windowsTitleBar/style.css deleted file mode 100644 index 334a19b9..00000000 --- a/js/windowsTitleBar/style.css +++ /dev/null @@ -1,106 +0,0 @@ -#title-bar { - display: flex; - position: fixed; - background: rgba(74,74,74,1); - top: 0; - left: 0; - width: 100%; - height: 32px; - padding-left: 0; - justify-content: center; - align-items: center; - -webkit-app-region: drag; - -webkit-user-select: none; - box-sizing: content-box; - z-index: 1000; -} - -#hamburger-menu-button { - color: rgba(255,255,255,1); - text-align: center; - width: 40px; - height: 32px; - background: center; - border: none; - border-image: initial; - display: inline-grid; - border-radius: 0; - padding: 11px; - box-sizing: border-box; - cursor: default; -} - -#hamburger-menu-button:focus { - outline: none; -} - -#title-container { - height: 32px; - flex: 1; - display: flex; - justify-content: flex-start; - align-items: center; - white-space: nowrap; - overflow: hidden; -} - -#title-bar-title { - font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; - color: white; - margin: 0; - padding-left: 10px; - font-size: 13px; -} - -.title-bar-button-container { - justify-content: center; - align-items: center; - right: 0; - color: rgba(255,255,255,1); - -webkit-app-region: no-drag; - text-align: center; - width: 45px; - height: 32px; - margin: 0; - box-sizing: border-box !important; - cursor: default; -} - -.title-bar-button { - color: rgba(255,255,255,1); - text-align: center; - width: 45px; - height: 32px; - background: center; - border: none; - border-image: initial; - display: inline-grid; - border-radius: 0; - padding: 10px 15px; - cursor: default; -} - -.title-bar-button:hover { - background-color: rgba(255,255,255,0.2); -} - -.title-bar-button:focus { - outline: none; -} - -.title-bar-button-container:hover { - background-color: rgba(255,255,255,0.2); -} - -.window-border { - border-left: 1px solid rgba(74,74,74,1); - border-right: 1px solid rgba(74,74,74,1); -} - -.bottom-window-border { - position: fixed; - border-bottom: 1px solid rgba(74,74,74,1); - width: 100%; - z-index: 3000; - bottom: 0; -} \ No newline at end of file diff --git a/locale/en-US.json b/locale/en-US.json deleted file mode 100644 index 38916764..00000000 --- a/locale/en-US.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "About Symphony": "About Symphony", - "Actual Size": "Actual Size", - "Always on Top": "Always on Top", - "Auto Launch On Startup": "Auto Launch On Startup", - "BasicAuth": { - "Authentication Request": "Authentication Request", - "Cancel": "Cancel", - "hostname": "hostname", - "Invalid user name/password": "Invalid user name/password", - "Log In": "Log In", - "Password:": "Password:", - "Please provide your login credentials for:": "Please provide your login credentials for:", - "User name:": "User name:" - }, - "MoreInfo": { - "More Information": "More Information", - "Version Information": "Version Information" - }, - "Bring All to Front": "Bring All to Front", - "Bring to Front on Notifications": "Bring to Front on Notifications", - "Certificate Error": "Certificate Error", - "Close": "Close", - "Cancel": "Cancel", - "ContextMenu": { - "Add to Dictionary": "Add to Dictionary", - "Copy": "Copy", - "Copy Email Address": "Copy Email Address", - "Copy Image": "Copy Image", - "Copy Image URL": "Copy Image URL", - "Copy Link": "Copy Link", - "Cut": "Cut", - "Inspect Element": "Inspect Element", - "Look Up {searchText}": "Look Up \"{searchText}\"", - "Open Link": "Open Link", - "Paste": "Paste", - "Reload": "Reload", - "Search with Google": "Search with Google" - }, - "DownloadManager": { - "Show in Folder": "Show in Folder", - "Reveal in Finder": "Reveal in Finder", - "Open": "Open", - "Downloaded": "Downloaded", - "File not Found": "File not Found", - "The file you are trying to open cannot be found in the specified path.": "The file you are trying to open cannot be found in the specified path." - }, - "Copy": "Copy", - "Custom": "Custom", - "Cut": "Cut", - "Delete": "Delete", - "Disable Hamburger menu": "Disable Hamburger menu", - "Dev Tools disabled": "Dev Tools disabled", - "Dev Tools has been disabled! Please contact your system administrator to enable it!": "Dev Tools has been disabled! Please contact your system administrator to enable it!", - "Edit": "Edit", - "Enable Hamburger menu": "Enable Hamburger menu", - "Error loading configuration": "Error loading configuration", - "Error loading URL": "Error loading URL", - "Error loading window": "Error loading window", - "Error setting AutoLaunch configuration": "Error setting AutoLaunch configuration", - "Failed!": "Failed!", - "Flash Notification in Taskbar": "Flash Notification in Taskbar", - "Help": "Help", - "Help Url": "https://support.symphony.com", - "Symphony Url": "https://symphony.com/en-US", - "Hide Others": "Hide Others", - "Hide Symphony": "Hide Symphony", - "Ignore": "Ignore", - "Learn More": "Learn More", - "Loading Error": "Loading Error", - "Minimize": "Minimize", - "Minimize on Close": "Minimize on Close", - "Native": "Native", - "Network connectivity has been lost. Check your internet connection.": "Network connectivity has been lost. Check your internet connection.", - "NetworkError": { - "Problem connecting to Symphony": "Problem connecting to Symphony", - "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "Looks like you are not connected to the Internet. We'll try to reconnect automatically.", - "Cancel Retry": "Cancel Retry", - "Quit Symphony": "Quit Symphony" - }, - "No crashes available to share": "No crashes available to share", - "No logs are available to share": "No logs are available to share", - "Not Allowed": "Not Allowed", - "Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.": "Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the \"Alt\" key.", - "NotificationSettings": { - "Bottom Left": "Bottom Left", - "Bottom Right": "Bottom Right", - "CANCEL": "CANCEL", - "Monitor": "Monitor", - "Notification Settings": "Notification Settings", - "Notification shown on Monitor: ": "Notification shown on Monitor: ", - "OK": "OK", - "Position": "Position", - "Symphony - Configure Notification Position": "Symphony - Configure Notification Position", - "Top Left": "Top Left", - "Top Right": "Top Right" - }, - "Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.", - "Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.", - "Paste": "Paste", - "Paste and Match Style": "Paste and Match Style", - "Permission Denied": "Permission Denied", - "Please contact your admin for help": "Please contact your admin for help", - "please contact your administrator for more details": "please contact your administrator for more details", - "Quit Symphony": "Quit Symphony", - "Redo": "Redo", - "Refresh app when idle": "Refresh app when idle", - "Clear cache and Reload": "Clear cache and Reload", - "Relaunch Application": "Relaunch Application", - "Relaunch": "Relaunch", - "Reload": "Reload", - "Renderer Process Crashed": "Renderer Process Crashed", - "ScreenPicker": { - "Applications": "Applications", - "Cancel": "Cancel", - "Choose what you'd like to share": "Choose what you'd like to share", - "No screens or applications are currently available.": "No screens or applications are currently available.", - "Screen Picker": "Screen Picker", - "Screens": "Screens", - "Select Application": "Select Application", - "Select Screen": "Select Screen", - "Share": "Share", - "Entire screen": "Entire screen", - "Screen {number}": "Screen {number}" - }, - "ScreenSharingIndicator": { - "You are sharing your screen on Symphony": "You are sharing your screen on Symphony", - "Stop sharing": "Stop sharing", - "Hide": "Hide" - }, - "ScreenSnippet": { - "Done": "Done", - "Erase": "Erase", - "Highlight": "Highlight", - "Pen": "Pen", - "Snipping Tool": "Snipping Tool" - }, - "AboutSymphony": { - "About Symphony": "About Symphony", - "Version": "Version" - }, - "Select All": "Select All", - "Services": "Services", - "Show All": "Show All", - "Show crash dump in Explorer": "Show crash dump in Explorer", - "Show crash dump in Finder": "Show crash dump in Finder", - "Toggle Developer Tools": "Toggle Developer Tools", - "More Information": "More Information", - "Show Logs in Explorer": "Show Logs in Explorer", - "Show Logs in Finder": "Show Logs in Finder", - "SnackBar": { - " to exit full screen": " to exit full screen", - "esc": "esc", - "Press ": "Press " - }, - "Sorry, you are not allowed to access this website": "Sorry, you are not allowed to access this website", - "Speech": "Speech", - "Start Speaking": "Start Speaking", - "Stop Speaking": "Stop Speaking", - "Symphony Help": "Symphony Help", - "TitleBar": { - "Close": "Close", - "Maximize": "Maximize", - "Menu": "Menu", - "Minimize": "Minimize" - }, - "Title Bar Style": "Title Bar Style", - "Toggle Full Screen": "Toggle Full Screen", - "Troubleshooting": "Troubleshooting", - "Unable to generate crash reports due to ": "Unable to generate crash reports due to ", - "Unable to generate logs due to ": "Unable to generate logs due to ", - "Undo": "Undo", - "Updating Title bar style requires Symphony to relaunch.": "Updating Title bar style requires Symphony to relaunch.", - "View": "View", - "Window": "Window", - "Your administrator has disabled": "Your administrator has disabled", - "Zoom": "Zoom", - "Zoom In": "Zoom In", - "Zoom Out": "Zoom Out" -} diff --git a/locale/en.json b/locale/en.json deleted file mode 100644 index 38916764..00000000 --- a/locale/en.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "About Symphony": "About Symphony", - "Actual Size": "Actual Size", - "Always on Top": "Always on Top", - "Auto Launch On Startup": "Auto Launch On Startup", - "BasicAuth": { - "Authentication Request": "Authentication Request", - "Cancel": "Cancel", - "hostname": "hostname", - "Invalid user name/password": "Invalid user name/password", - "Log In": "Log In", - "Password:": "Password:", - "Please provide your login credentials for:": "Please provide your login credentials for:", - "User name:": "User name:" - }, - "MoreInfo": { - "More Information": "More Information", - "Version Information": "Version Information" - }, - "Bring All to Front": "Bring All to Front", - "Bring to Front on Notifications": "Bring to Front on Notifications", - "Certificate Error": "Certificate Error", - "Close": "Close", - "Cancel": "Cancel", - "ContextMenu": { - "Add to Dictionary": "Add to Dictionary", - "Copy": "Copy", - "Copy Email Address": "Copy Email Address", - "Copy Image": "Copy Image", - "Copy Image URL": "Copy Image URL", - "Copy Link": "Copy Link", - "Cut": "Cut", - "Inspect Element": "Inspect Element", - "Look Up {searchText}": "Look Up \"{searchText}\"", - "Open Link": "Open Link", - "Paste": "Paste", - "Reload": "Reload", - "Search with Google": "Search with Google" - }, - "DownloadManager": { - "Show in Folder": "Show in Folder", - "Reveal in Finder": "Reveal in Finder", - "Open": "Open", - "Downloaded": "Downloaded", - "File not Found": "File not Found", - "The file you are trying to open cannot be found in the specified path.": "The file you are trying to open cannot be found in the specified path." - }, - "Copy": "Copy", - "Custom": "Custom", - "Cut": "Cut", - "Delete": "Delete", - "Disable Hamburger menu": "Disable Hamburger menu", - "Dev Tools disabled": "Dev Tools disabled", - "Dev Tools has been disabled! Please contact your system administrator to enable it!": "Dev Tools has been disabled! Please contact your system administrator to enable it!", - "Edit": "Edit", - "Enable Hamburger menu": "Enable Hamburger menu", - "Error loading configuration": "Error loading configuration", - "Error loading URL": "Error loading URL", - "Error loading window": "Error loading window", - "Error setting AutoLaunch configuration": "Error setting AutoLaunch configuration", - "Failed!": "Failed!", - "Flash Notification in Taskbar": "Flash Notification in Taskbar", - "Help": "Help", - "Help Url": "https://support.symphony.com", - "Symphony Url": "https://symphony.com/en-US", - "Hide Others": "Hide Others", - "Hide Symphony": "Hide Symphony", - "Ignore": "Ignore", - "Learn More": "Learn More", - "Loading Error": "Loading Error", - "Minimize": "Minimize", - "Minimize on Close": "Minimize on Close", - "Native": "Native", - "Network connectivity has been lost. Check your internet connection.": "Network connectivity has been lost. Check your internet connection.", - "NetworkError": { - "Problem connecting to Symphony": "Problem connecting to Symphony", - "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "Looks like you are not connected to the Internet. We'll try to reconnect automatically.", - "Cancel Retry": "Cancel Retry", - "Quit Symphony": "Quit Symphony" - }, - "No crashes available to share": "No crashes available to share", - "No logs are available to share": "No logs are available to share", - "Not Allowed": "Not Allowed", - "Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.": "Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the \"Alt\" key.", - "NotificationSettings": { - "Bottom Left": "Bottom Left", - "Bottom Right": "Bottom Right", - "CANCEL": "CANCEL", - "Monitor": "Monitor", - "Notification Settings": "Notification Settings", - "Notification shown on Monitor: ": "Notification shown on Monitor: ", - "OK": "OK", - "Position": "Position", - "Symphony - Configure Notification Position": "Symphony - Configure Notification Position", - "Top Left": "Top Left", - "Top Right": "Top Right" - }, - "Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.", - "Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.", - "Paste": "Paste", - "Paste and Match Style": "Paste and Match Style", - "Permission Denied": "Permission Denied", - "Please contact your admin for help": "Please contact your admin for help", - "please contact your administrator for more details": "please contact your administrator for more details", - "Quit Symphony": "Quit Symphony", - "Redo": "Redo", - "Refresh app when idle": "Refresh app when idle", - "Clear cache and Reload": "Clear cache and Reload", - "Relaunch Application": "Relaunch Application", - "Relaunch": "Relaunch", - "Reload": "Reload", - "Renderer Process Crashed": "Renderer Process Crashed", - "ScreenPicker": { - "Applications": "Applications", - "Cancel": "Cancel", - "Choose what you'd like to share": "Choose what you'd like to share", - "No screens or applications are currently available.": "No screens or applications are currently available.", - "Screen Picker": "Screen Picker", - "Screens": "Screens", - "Select Application": "Select Application", - "Select Screen": "Select Screen", - "Share": "Share", - "Entire screen": "Entire screen", - "Screen {number}": "Screen {number}" - }, - "ScreenSharingIndicator": { - "You are sharing your screen on Symphony": "You are sharing your screen on Symphony", - "Stop sharing": "Stop sharing", - "Hide": "Hide" - }, - "ScreenSnippet": { - "Done": "Done", - "Erase": "Erase", - "Highlight": "Highlight", - "Pen": "Pen", - "Snipping Tool": "Snipping Tool" - }, - "AboutSymphony": { - "About Symphony": "About Symphony", - "Version": "Version" - }, - "Select All": "Select All", - "Services": "Services", - "Show All": "Show All", - "Show crash dump in Explorer": "Show crash dump in Explorer", - "Show crash dump in Finder": "Show crash dump in Finder", - "Toggle Developer Tools": "Toggle Developer Tools", - "More Information": "More Information", - "Show Logs in Explorer": "Show Logs in Explorer", - "Show Logs in Finder": "Show Logs in Finder", - "SnackBar": { - " to exit full screen": " to exit full screen", - "esc": "esc", - "Press ": "Press " - }, - "Sorry, you are not allowed to access this website": "Sorry, you are not allowed to access this website", - "Speech": "Speech", - "Start Speaking": "Start Speaking", - "Stop Speaking": "Stop Speaking", - "Symphony Help": "Symphony Help", - "TitleBar": { - "Close": "Close", - "Maximize": "Maximize", - "Menu": "Menu", - "Minimize": "Minimize" - }, - "Title Bar Style": "Title Bar Style", - "Toggle Full Screen": "Toggle Full Screen", - "Troubleshooting": "Troubleshooting", - "Unable to generate crash reports due to ": "Unable to generate crash reports due to ", - "Unable to generate logs due to ": "Unable to generate logs due to ", - "Undo": "Undo", - "Updating Title bar style requires Symphony to relaunch.": "Updating Title bar style requires Symphony to relaunch.", - "View": "View", - "Window": "Window", - "Your administrator has disabled": "Your administrator has disabled", - "Zoom": "Zoom", - "Zoom In": "Zoom In", - "Zoom Out": "Zoom Out" -} diff --git a/locale/fr-FR.json b/locale/fr-FR.json deleted file mode 100644 index e104b0be..00000000 --- a/locale/fr-FR.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "About Symphony": "À propos de Symphony", - "Actual Size": "Taille actuelle", - "Always on Top": "Toujours au top", - "Auto Launch On Startup": "Lancement Automatique au démarrage", - "BasicAuth": { - "Authentication Request": "Demande d'authentification", - "Cancel": "Annuler", - "hostname": "hostname", - "Invalid user name/password": "Nom d'utilisateur/mot de passe invalide", - "Log In": "S'identifier", - "Password:": "Mot de passe:", - "Please provide your login credentials for:": "Fournissez vos identifiants de connexion pour:", - "User name:": "Nom d'utilisateur:" - }, - "MoreInfo": { - "More Information": "Plus d’informations", - "Version Information": "Information sur la version" - }, - "Bring All to Front": "Amener tous au front", - "Bring to Front on Notifications": "Mettre les notifications au premier plan", - "Certificate Error": "Erreur de certificat", - "Close": "Fermer", - "Cancel": "Annuler", - "ContextMenu": { - "Add to Dictionary": "Ajouter au dictionnaire", - "Copy": "Copier", - "Copy Email Address": "Copier l'adresse e-mail", - "Copy Image": "Copier l'image", - "Copy Image URL": "Copier l'URL de l'image", - "Copy Link": "Copier le lien", - "Cut": "Couper", - "Inspect Element": "Inspecter l'élément", - "Look Up {searchText}": "Rechercher \"{searchText}\"", - "Open Link": "Ouvrir le lien", - "Paste": "Coller", - "Reload": "Recharger", - "Search with Google": "Rechercher avec Google" - }, - "DownloadManager": { - "Show in Folder": "Afficher dans le dossier", - "Reveal in Finder": "Révéler dans le Finder", - "Open": "Ouvrir", - "Downloaded": "Téléchargé", - "File not Found": "Fichier non trouvé", - "The file you are trying to open cannot be found in the specified path.": "Le fichier que vous essayez d'ouvrir est introuvable dans le chemin spécifié." - }, - "Copy": "Copier", - "Custom": "Personnalisé", - "Cut": "Couper", - "Delete": "Effacer", - "Disable Hamburger menu": "Désactiver le menu Hamburger", - "Dev Tools disabled": "Outils de développement désactivés", - "Dev Tools has been disabled! Please contact your system administrator to enable it!": "Dev Tools a été désactivé ! Veuillez contacter votre administrateur système pour l’activer !", - "Edit": "Modifier", - "Enable Hamburger menu": "Activer le menu Hamburger", - "Error loading configuration": "Erreur de chargement de la configuration", - "Error loading URL": "Erreur de chargement de l'URL", - "Error loading window": "Erreur de chargement de la fenêtre", - "Error setting AutoLaunch configuration": "Erreur de configuration dans le Lancement Automatique", - "Failed!": "Échoué!", - "Flash Notification in Taskbar": "Notification Flash dans la barre des tâches", - "Help": "Aide", - "Help Url": "https://support.symphony.com", - "Symphony Url": "https://symphony.com/fr-FR", - "Hide Others": "Cacher les autres", - "Hide Symphony": "Cacher Symphony", - "Ignore": "Ignorer", - "Learn More": "Apprendre encore plus", - "Loading Error": "Erreur lors du chargement", - "Minimize": "Minimiser", - "Minimiser on Close": "Minimiser à la fermeture", - "Native": "Originaire", - "Network connectivity has been lost. Check your internet connection.": "La connectivité a été perdue. Vérifiez votre connection à l'internet.", - "NetworkError": { - "Problem connecting to Symphony": "Problème de connexion à Symphony", - "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "On dirait que vous n'êtes pas connecté à Internet. Nous allons essayer de vous reconnecter automatiquement.", - "Cancel Retry": "Annuler nouvelle tentative", - "Quit Symphony": "Quitter Symphony" - }, - "No crashes available to share": "Pas de crash à partager", - "No logs are available to share": "Pas de journal à partager", - "Not Allowed": "Interdit", - "Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.": "Remarque: lorsque le menu Hamburger est désactivé, vous pouvez activer le menu principal en appuyant sur la touche \"Alt\" key.", - "NotificationSettings": { - "Bottom Left": "Gauche inférieure", - "Bottom Right": "Droite inférieure", - "CANCEL": "ANNULER", - "Monitor": "Moniteur", - "Notification Settings": "Paramètres des notifications", - "Notification shown on Monitor: ": "Notification affichée sur le Moniteur: ", - "OK": "OK", - "Position": "Position", - "Symphony - Configure Notification Position": "Symphony - Configurer la position des notifications", - "Top Left": "Gauche supérieure", - "Top Right": "Droite supérieure" - }, - "Oops! Looks like we have had a crash.": "Oops! On dirait que nous avons eu un crash.", - "Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! On dirait que nous avons eu un crash. Veuillez recharger ou fermer cette fenêtre.", - "Paste": "Coller", - "Paste and Match Style": "Coller et appliquer le style", - "Permission Denied": "Permission refusée", - "Please contact your admin for help": "Veuillez contacter votre administrateur pour obtenir de l'aide.", - "please contact your administrator for more details": "veuillez contacter votre administrateur pour plus de détails", - "Quit Symphony": "Quitter Symphony", - "Redo": "Refaire", - "Refresh app when idle": "Actualiser l'application en mode inactive", - "Clear cache and Reload": "Vider le cache et recharger", - "Relaunch Application": "Redémarrer l'application", - "Relaunch": "Redémarrer", - "Reload": "Recharger", - "Renderer Process Crashed": "Processus de rendu a eu un crash", - "ScreenPicker": { - "Applications": "Applications", - "Cancel": "Annuler", - "Choose what you'd like to share": "Choisissez ce que vous souhaitez partager", - "No screens or applications are currently available.": "Aucun écran ou application n'est actuellement disponible.", - "Screen Picker": "Sélecteur d'écran", - "Screens": "Écrans", - "Select Application": "Sélectionnez une application", - "Select Screen": "Sélectionnez l'écran", - "Share": "Partager", - "Entire screen": "Écran entier", - "Screen {number}": "Écran {number}" - }, - "ScreenSharingIndicator": { - "You are sharing your screen on Symphony": "Vous partagez votre écran sur Symphony", - "Stop sharing": "Arrêter le partage", - "Hide": "Masquer" - }, - "ScreenSnippet": { - "Done": "Terminé", - "Erase": "Effacer", - "Highlight": "Surligner", - "Pen": "Stylo", - "Snipping Tool": "Outil Capture" - }, - "AboutSymphony": { - "About Symphony": "À propos de Symphony", - "Version": "Version" - }, - "Select All": "Tout sélectionner", - "Services": "Services", - "Show All": "Tout afficher", - "Show crash dump in Explorer": "Afficher rapport de crash dans Explorateur", - "Show crash dump in Finder": "Afficher rapport de crash dans Finder", - "Toggle Developer Tools": "Basculer, les outils de développement", - "More Information": "Plus d'information", - "Show Logs in Explorer": "Afficher journal d'évenements dans Explorateur", - "Show Logs in Finder": "Afficher journal d'évenements dans Finder", - "SnackBar": { - " to exit full screen": " pour quitter le plein écran", - "esc": "esc", - "Press ": "Appuyer sur " - }, - "Sorry, you are not allowed to access this website": "Désolé, vous n'êtes pas autorisé à accéder à ce site.", - "Speech": "Dictée vocale", - "Start Speaking": "Commencer à dicter", - "Stop Speaking": "Arreter de dicter", - "Symphony Help": "Aide Symphony", - "TitleBar": { - "Close": "Fermer", - "Maximize": "Maximiser", - "Menu": "Menu", - "Minimize": "Minimiser" - }, - "Title Bar Style": "Style de la barre de titre", - "Toggle Full Screen": "Basculer plein écran", - "Troubleshooting": "Dépannage", - "Unable to generate crash reports due to ": "Impossible de générer le rapport de crash en raison de ", - "Unable to generate logs due to ": "Impossible de générer le journal d'évenements en raison de", - "Undo": "Défaire", - "Updating Title bar style requires Symphony to relaunch.": "La mise à jour du style de la barre de titre nécessite le redémarrage de Symphony.", - "View": "Visualiser", - "Window": "Fenêtre", - "Your administrator has disabled": "Votre administrateur a désactivé", - "Zoom": "Zoom", - "Zoom In": "Zoom Avant", - "Zoom Out": "Zoom Arrière" -} diff --git a/locale/fr.json b/locale/fr.json deleted file mode 100644 index e104b0be..00000000 --- a/locale/fr.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "About Symphony": "À propos de Symphony", - "Actual Size": "Taille actuelle", - "Always on Top": "Toujours au top", - "Auto Launch On Startup": "Lancement Automatique au démarrage", - "BasicAuth": { - "Authentication Request": "Demande d'authentification", - "Cancel": "Annuler", - "hostname": "hostname", - "Invalid user name/password": "Nom d'utilisateur/mot de passe invalide", - "Log In": "S'identifier", - "Password:": "Mot de passe:", - "Please provide your login credentials for:": "Fournissez vos identifiants de connexion pour:", - "User name:": "Nom d'utilisateur:" - }, - "MoreInfo": { - "More Information": "Plus d’informations", - "Version Information": "Information sur la version" - }, - "Bring All to Front": "Amener tous au front", - "Bring to Front on Notifications": "Mettre les notifications au premier plan", - "Certificate Error": "Erreur de certificat", - "Close": "Fermer", - "Cancel": "Annuler", - "ContextMenu": { - "Add to Dictionary": "Ajouter au dictionnaire", - "Copy": "Copier", - "Copy Email Address": "Copier l'adresse e-mail", - "Copy Image": "Copier l'image", - "Copy Image URL": "Copier l'URL de l'image", - "Copy Link": "Copier le lien", - "Cut": "Couper", - "Inspect Element": "Inspecter l'élément", - "Look Up {searchText}": "Rechercher \"{searchText}\"", - "Open Link": "Ouvrir le lien", - "Paste": "Coller", - "Reload": "Recharger", - "Search with Google": "Rechercher avec Google" - }, - "DownloadManager": { - "Show in Folder": "Afficher dans le dossier", - "Reveal in Finder": "Révéler dans le Finder", - "Open": "Ouvrir", - "Downloaded": "Téléchargé", - "File not Found": "Fichier non trouvé", - "The file you are trying to open cannot be found in the specified path.": "Le fichier que vous essayez d'ouvrir est introuvable dans le chemin spécifié." - }, - "Copy": "Copier", - "Custom": "Personnalisé", - "Cut": "Couper", - "Delete": "Effacer", - "Disable Hamburger menu": "Désactiver le menu Hamburger", - "Dev Tools disabled": "Outils de développement désactivés", - "Dev Tools has been disabled! Please contact your system administrator to enable it!": "Dev Tools a été désactivé ! Veuillez contacter votre administrateur système pour l’activer !", - "Edit": "Modifier", - "Enable Hamburger menu": "Activer le menu Hamburger", - "Error loading configuration": "Erreur de chargement de la configuration", - "Error loading URL": "Erreur de chargement de l'URL", - "Error loading window": "Erreur de chargement de la fenêtre", - "Error setting AutoLaunch configuration": "Erreur de configuration dans le Lancement Automatique", - "Failed!": "Échoué!", - "Flash Notification in Taskbar": "Notification Flash dans la barre des tâches", - "Help": "Aide", - "Help Url": "https://support.symphony.com", - "Symphony Url": "https://symphony.com/fr-FR", - "Hide Others": "Cacher les autres", - "Hide Symphony": "Cacher Symphony", - "Ignore": "Ignorer", - "Learn More": "Apprendre encore plus", - "Loading Error": "Erreur lors du chargement", - "Minimize": "Minimiser", - "Minimiser on Close": "Minimiser à la fermeture", - "Native": "Originaire", - "Network connectivity has been lost. Check your internet connection.": "La connectivité a été perdue. Vérifiez votre connection à l'internet.", - "NetworkError": { - "Problem connecting to Symphony": "Problème de connexion à Symphony", - "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "On dirait que vous n'êtes pas connecté à Internet. Nous allons essayer de vous reconnecter automatiquement.", - "Cancel Retry": "Annuler nouvelle tentative", - "Quit Symphony": "Quitter Symphony" - }, - "No crashes available to share": "Pas de crash à partager", - "No logs are available to share": "Pas de journal à partager", - "Not Allowed": "Interdit", - "Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.": "Remarque: lorsque le menu Hamburger est désactivé, vous pouvez activer le menu principal en appuyant sur la touche \"Alt\" key.", - "NotificationSettings": { - "Bottom Left": "Gauche inférieure", - "Bottom Right": "Droite inférieure", - "CANCEL": "ANNULER", - "Monitor": "Moniteur", - "Notification Settings": "Paramètres des notifications", - "Notification shown on Monitor: ": "Notification affichée sur le Moniteur: ", - "OK": "OK", - "Position": "Position", - "Symphony - Configure Notification Position": "Symphony - Configurer la position des notifications", - "Top Left": "Gauche supérieure", - "Top Right": "Droite supérieure" - }, - "Oops! Looks like we have had a crash.": "Oops! On dirait que nous avons eu un crash.", - "Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! On dirait que nous avons eu un crash. Veuillez recharger ou fermer cette fenêtre.", - "Paste": "Coller", - "Paste and Match Style": "Coller et appliquer le style", - "Permission Denied": "Permission refusée", - "Please contact your admin for help": "Veuillez contacter votre administrateur pour obtenir de l'aide.", - "please contact your administrator for more details": "veuillez contacter votre administrateur pour plus de détails", - "Quit Symphony": "Quitter Symphony", - "Redo": "Refaire", - "Refresh app when idle": "Actualiser l'application en mode inactive", - "Clear cache and Reload": "Vider le cache et recharger", - "Relaunch Application": "Redémarrer l'application", - "Relaunch": "Redémarrer", - "Reload": "Recharger", - "Renderer Process Crashed": "Processus de rendu a eu un crash", - "ScreenPicker": { - "Applications": "Applications", - "Cancel": "Annuler", - "Choose what you'd like to share": "Choisissez ce que vous souhaitez partager", - "No screens or applications are currently available.": "Aucun écran ou application n'est actuellement disponible.", - "Screen Picker": "Sélecteur d'écran", - "Screens": "Écrans", - "Select Application": "Sélectionnez une application", - "Select Screen": "Sélectionnez l'écran", - "Share": "Partager", - "Entire screen": "Écran entier", - "Screen {number}": "Écran {number}" - }, - "ScreenSharingIndicator": { - "You are sharing your screen on Symphony": "Vous partagez votre écran sur Symphony", - "Stop sharing": "Arrêter le partage", - "Hide": "Masquer" - }, - "ScreenSnippet": { - "Done": "Terminé", - "Erase": "Effacer", - "Highlight": "Surligner", - "Pen": "Stylo", - "Snipping Tool": "Outil Capture" - }, - "AboutSymphony": { - "About Symphony": "À propos de Symphony", - "Version": "Version" - }, - "Select All": "Tout sélectionner", - "Services": "Services", - "Show All": "Tout afficher", - "Show crash dump in Explorer": "Afficher rapport de crash dans Explorateur", - "Show crash dump in Finder": "Afficher rapport de crash dans Finder", - "Toggle Developer Tools": "Basculer, les outils de développement", - "More Information": "Plus d'information", - "Show Logs in Explorer": "Afficher journal d'évenements dans Explorateur", - "Show Logs in Finder": "Afficher journal d'évenements dans Finder", - "SnackBar": { - " to exit full screen": " pour quitter le plein écran", - "esc": "esc", - "Press ": "Appuyer sur " - }, - "Sorry, you are not allowed to access this website": "Désolé, vous n'êtes pas autorisé à accéder à ce site.", - "Speech": "Dictée vocale", - "Start Speaking": "Commencer à dicter", - "Stop Speaking": "Arreter de dicter", - "Symphony Help": "Aide Symphony", - "TitleBar": { - "Close": "Fermer", - "Maximize": "Maximiser", - "Menu": "Menu", - "Minimize": "Minimiser" - }, - "Title Bar Style": "Style de la barre de titre", - "Toggle Full Screen": "Basculer plein écran", - "Troubleshooting": "Dépannage", - "Unable to generate crash reports due to ": "Impossible de générer le rapport de crash en raison de ", - "Unable to generate logs due to ": "Impossible de générer le journal d'évenements en raison de", - "Undo": "Défaire", - "Updating Title bar style requires Symphony to relaunch.": "La mise à jour du style de la barre de titre nécessite le redémarrage de Symphony.", - "View": "Visualiser", - "Window": "Fenêtre", - "Your administrator has disabled": "Votre administrateur a désactivé", - "Zoom": "Zoom", - "Zoom In": "Zoom Avant", - "Zoom Out": "Zoom Arrière" -} diff --git a/locale/ja-JP.json b/locale/ja-JP.json deleted file mode 100644 index 67f5213f..00000000 --- a/locale/ja-JP.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "About Symphony": "Symphonyについて", - "Actual Size": "実際のサイズ", - "Always on Top": "つねに前面に表示", - "Auto Launch On Startup": "スタートアップ時に自動起動", - "BasicAuth": { - "Authentication Request": "認証要求", - "Cancel": "キャンセル", - "hostname": "ホスト名", - "Invalid user name/password": "ユーザー名かパスワード、または両方が無効", - "Log In": "ログイン", - "Password:": "パスワード:", - "Please provide your login credentials for:": "あなたのログイン認証情報を入力してください。", - "User name:": "ユーザー名:" - }, - "MoreInfo": { - "More Information": "詳しくは", - "Version Information": "バージョン情報" - }, - "Bring All to Front": "すべて前面に表示", - "Bring to Front on Notifications": "通知時に前面に表示", - "Certificate Error": "証明書のエラー", - "Close": "閉じる", - "Cancel": "キャンセル", - "ContextMenu": { - "Add to Dictionary": "辞書に追加", - "Copy": "コピー", - "Copy Email Address": "電子メールアドレスをコピー", - "Copy Image": "画像をコピー", - "Copy Image URL": "画像のURLをコピー", - "Copy Link": "リンクをコピー", - "Cut": "切り取り", - "Inspect Element": "要素を調査", - "Look Up {searchText}": "「{searchText}」を検索", - "Open Link": "リンクを開く", - "Paste": "貼り付け", - "Reload": "再読み込み", - "Search with Google": "Googleで検索" - }, - "DownloadManager": { - "Show in Folder": "フォルダーを開く", - "Reveal in Finder": "Finderで確認する", - "Open": "開く", - "Downloaded": "ダウンロード済み", - "File not Found": "ファイルが見つかりません", - "The file you are trying to open cannot be found in the specified path.": "開こうとしているファイルが指定されたパスに見つかりません." - }, - "Copy": "コピー", - "Custom": "カスタム", - "Cut": "切り取り", - "Delete": "削除", - "Disable Hamburger menu": "ハンバーガーメニューを無効にする", - "Dev Tools disabled": "開発ツールが無効", - "Dev Tools has been disabled! Please contact your system administrator to enable it!": "開発ツールが無効になっています。システム管理者に連絡して、有効にしてください。", - "Edit": "編集", - "Enable Hamburger menu": "ハンバーガーメニューを有効にする", - "Error loading configuration": "構成の読み込みエラー", - "Error loading URL": "URLの読み込みエラー", - "Error loading window": "ウィンドウを読み込みエラー", - "Error setting AutoLaunch configuration": "自動起動の構成の設定エラー", - "Failed!": "問題が起きました!", - "Flash Notification in Taskbar": "タスクバーの通知を点滅", - "Help": "ヘルプ", - "Help Url": "https://support.symphony.com/hc/ja", - "Symphony Url": "https://symphony.com/ja", - "Hide Others": "他を隠す", - "Hide Symphony": "Symphonyを隠す", - "Ignore": "無視", - "Learn More": "詳細", - "Loading Error": "読み込みエラー", - "Minimize": "最小化", - "Minimize on Close": "閉じるで最小化", - "Native": "Native", - "Network connectivity has been lost. Check your internet connection.": "ネットワーク接続が失われました。インターネット接続を確認してください。", - "NetworkError": { - "Problem connecting to Symphony": "Symphonyへの接続に関する問題", - "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "インターネットに接続していないようです。 自動的に再接続します。", - "Cancel Retry": "再試行をキャンセル", - "Quit Symphony": "Symphonyを終了" - }, - "No crashes available to share": "共有できるクラッシュはありません", - "No logs are available to share": "共有できるログはありません", - "Not Allowed": "許可されていませ。", - "Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.": "注:ハンバーガーメニューが無効になっている場合、「Alt」キーを押してメインメニューをトリガーすることができます。", - "NotificationSettings": { - "Bottom Left": "左下", - "Bottom Right": "右下", - "CANCEL": "キャンセル", - "Monitor": "モニター", - "Notification Settings": "通知設定", - "Notification shown on Monitor: ": "モニターに表示する通知:", - "OK": "OK", - "Position": "位置", - "Symphony - Configure Notification Position": "Symphony - 通知位置の設定", - "Top Left": "左上", - "Top Right": "右上" - }, - "Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。", - "Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。", - "Paste": "貼り付け", - "Paste and Match Style": "貼り付けでスタイルを合わせる", - "Permission Denied": "アクセス許可が拒否されています", - "Please contact your admin for help": "不明な点がある場合は、管理者にお問い合わせください", - "please contact your administrator for more details": "詳細は、管理者にお問い合わせください", - "Quit Symphony": "Symphonyを終了", - "Redo": "やり直し", - "Refresh app when idle": "アイドル時にアプリを再表示", - "Clear cache and Reload": "キャッシュをクリアしてリロードする", - "Relaunch Application": "アプリケーションの再起動", - "Relaunch": "「リスタート」", - "Reload": "再読み込み", - "Renderer Process Crashed": "レンダラープロセスがクラッシュしました", - "ScreenPicker": { - "Applications": "アプリケーション", - "Cancel": "キャンセル", - "Choose what you'd like to share": "共有するものを選択する", - "No screens or applications are currently available.": "現在利用できる画面およびアプリケーションはありません。", - "Screen Picker": "画面の選択", - "Screens": "画面", - "Select Application": "アプリケーションを選択", - "Select Screen": "画面を選択", - "Share": "共有", - "Entire screen": "画面全体", - "Screen {number}": "画面 {number}" - }, - "ScreenSharingIndicator": { - "You are sharing your screen on Symphony": "あなたはSymphony上であなたの画面を共有しています", - "Stop sharing": "画面共有を停止", - "Hide": "非表示にする" - }, - "ScreenSnippet": { - "Done": "完了", - "Erase": "消去", - "Highlight": "強調", - "Pen": "ペン", - "Snipping Tool": "切り取りツール" - }, - "AboutSymphony": { - "About Symphony": "Symphonyについて", - "Version": "バージョン" - }, - "Select All": "すべてを選択", - "Services": "サービス", - "Show All": "すべてを表示", - "Show crash dump in Explorer": "Explorerにクラッシュダンプを表示", - "Show crash dump in Finder": "ファインダーにクラッシュダンプを表示", - "Toggle Developer Tools": "開発者ツールの切り替え", - "More Information": "詳しくは", - "Show Logs in Explorer": "Explorerにログを表示", - "Show Logs in Finder": "ファインダーにログを表示", - "SnackBar": { - " to exit full screen": " を押します", - "esc": "esc", - "Press ": "全画面表示を終了するには " - }, - "Sorry, you are not allowed to access this website": "申し訳ありませんが、このウェブサイトへのアクセスは許可されていません", - "Speech": "スピーチ", - "Start Speaking": "スピーチを開始", - "Stop Speaking": "スピーチを終了", - "Symphony Help": "Symphonyのヘルプ", - "TitleBar": { - "Close": "閉じる", - "Maximize": "最大化する", - "Menu": "メニュー", - "Minimize": "最小化する" - }, - "Title Bar Style": "タイトルバーのスタイル", - "Toggle Full Screen": "画面表示を切り替え", - "Troubleshooting": "トラブルシューティング", - "Unable to generate crash reports due to ": "クラッシュレポートを生成できません。理由: ", - "Unable to generate logs due to ": "ログを生成できません。理由:", - "Undo": "元に戻す", - "Updating Title bar style requires Symphony to relaunch.": "タイトルバーのスタイルを更新するには、Symphonyが再起動する必要があります。", - "View": "ビュー", - "Window": "ウインドウ", - "Your administrator has disabled": "管理者によて無効にされています", - "Zoom": "ズーム", - "Zoom In": "ズームイン", - "Zoom Out": "ズームアウト" -} diff --git a/locale/ja.json b/locale/ja.json deleted file mode 100644 index 67f5213f..00000000 --- a/locale/ja.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "About Symphony": "Symphonyについて", - "Actual Size": "実際のサイズ", - "Always on Top": "つねに前面に表示", - "Auto Launch On Startup": "スタートアップ時に自動起動", - "BasicAuth": { - "Authentication Request": "認証要求", - "Cancel": "キャンセル", - "hostname": "ホスト名", - "Invalid user name/password": "ユーザー名かパスワード、または両方が無効", - "Log In": "ログイン", - "Password:": "パスワード:", - "Please provide your login credentials for:": "あなたのログイン認証情報を入力してください。", - "User name:": "ユーザー名:" - }, - "MoreInfo": { - "More Information": "詳しくは", - "Version Information": "バージョン情報" - }, - "Bring All to Front": "すべて前面に表示", - "Bring to Front on Notifications": "通知時に前面に表示", - "Certificate Error": "証明書のエラー", - "Close": "閉じる", - "Cancel": "キャンセル", - "ContextMenu": { - "Add to Dictionary": "辞書に追加", - "Copy": "コピー", - "Copy Email Address": "電子メールアドレスをコピー", - "Copy Image": "画像をコピー", - "Copy Image URL": "画像のURLをコピー", - "Copy Link": "リンクをコピー", - "Cut": "切り取り", - "Inspect Element": "要素を調査", - "Look Up {searchText}": "「{searchText}」を検索", - "Open Link": "リンクを開く", - "Paste": "貼り付け", - "Reload": "再読み込み", - "Search with Google": "Googleで検索" - }, - "DownloadManager": { - "Show in Folder": "フォルダーを開く", - "Reveal in Finder": "Finderで確認する", - "Open": "開く", - "Downloaded": "ダウンロード済み", - "File not Found": "ファイルが見つかりません", - "The file you are trying to open cannot be found in the specified path.": "開こうとしているファイルが指定されたパスに見つかりません." - }, - "Copy": "コピー", - "Custom": "カスタム", - "Cut": "切り取り", - "Delete": "削除", - "Disable Hamburger menu": "ハンバーガーメニューを無効にする", - "Dev Tools disabled": "開発ツールが無効", - "Dev Tools has been disabled! Please contact your system administrator to enable it!": "開発ツールが無効になっています。システム管理者に連絡して、有効にしてください。", - "Edit": "編集", - "Enable Hamburger menu": "ハンバーガーメニューを有効にする", - "Error loading configuration": "構成の読み込みエラー", - "Error loading URL": "URLの読み込みエラー", - "Error loading window": "ウィンドウを読み込みエラー", - "Error setting AutoLaunch configuration": "自動起動の構成の設定エラー", - "Failed!": "問題が起きました!", - "Flash Notification in Taskbar": "タスクバーの通知を点滅", - "Help": "ヘルプ", - "Help Url": "https://support.symphony.com/hc/ja", - "Symphony Url": "https://symphony.com/ja", - "Hide Others": "他を隠す", - "Hide Symphony": "Symphonyを隠す", - "Ignore": "無視", - "Learn More": "詳細", - "Loading Error": "読み込みエラー", - "Minimize": "最小化", - "Minimize on Close": "閉じるで最小化", - "Native": "Native", - "Network connectivity has been lost. Check your internet connection.": "ネットワーク接続が失われました。インターネット接続を確認してください。", - "NetworkError": { - "Problem connecting to Symphony": "Symphonyへの接続に関する問題", - "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "インターネットに接続していないようです。 自動的に再接続します。", - "Cancel Retry": "再試行をキャンセル", - "Quit Symphony": "Symphonyを終了" - }, - "No crashes available to share": "共有できるクラッシュはありません", - "No logs are available to share": "共有できるログはありません", - "Not Allowed": "許可されていませ。", - "Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.": "注:ハンバーガーメニューが無効になっている場合、「Alt」キーを押してメインメニューをトリガーすることができます。", - "NotificationSettings": { - "Bottom Left": "左下", - "Bottom Right": "右下", - "CANCEL": "キャンセル", - "Monitor": "モニター", - "Notification Settings": "通知設定", - "Notification shown on Monitor: ": "モニターに表示する通知:", - "OK": "OK", - "Position": "位置", - "Symphony - Configure Notification Position": "Symphony - 通知位置の設定", - "Top Left": "左上", - "Top Right": "右上" - }, - "Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。", - "Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。", - "Paste": "貼り付け", - "Paste and Match Style": "貼り付けでスタイルを合わせる", - "Permission Denied": "アクセス許可が拒否されています", - "Please contact your admin for help": "不明な点がある場合は、管理者にお問い合わせください", - "please contact your administrator for more details": "詳細は、管理者にお問い合わせください", - "Quit Symphony": "Symphonyを終了", - "Redo": "やり直し", - "Refresh app when idle": "アイドル時にアプリを再表示", - "Clear cache and Reload": "キャッシュをクリアしてリロードする", - "Relaunch Application": "アプリケーションの再起動", - "Relaunch": "「リスタート」", - "Reload": "再読み込み", - "Renderer Process Crashed": "レンダラープロセスがクラッシュしました", - "ScreenPicker": { - "Applications": "アプリケーション", - "Cancel": "キャンセル", - "Choose what you'd like to share": "共有するものを選択する", - "No screens or applications are currently available.": "現在利用できる画面およびアプリケーションはありません。", - "Screen Picker": "画面の選択", - "Screens": "画面", - "Select Application": "アプリケーションを選択", - "Select Screen": "画面を選択", - "Share": "共有", - "Entire screen": "画面全体", - "Screen {number}": "画面 {number}" - }, - "ScreenSharingIndicator": { - "You are sharing your screen on Symphony": "あなたはSymphony上であなたの画面を共有しています", - "Stop sharing": "画面共有を停止", - "Hide": "非表示にする" - }, - "ScreenSnippet": { - "Done": "完了", - "Erase": "消去", - "Highlight": "強調", - "Pen": "ペン", - "Snipping Tool": "切り取りツール" - }, - "AboutSymphony": { - "About Symphony": "Symphonyについて", - "Version": "バージョン" - }, - "Select All": "すべてを選択", - "Services": "サービス", - "Show All": "すべてを表示", - "Show crash dump in Explorer": "Explorerにクラッシュダンプを表示", - "Show crash dump in Finder": "ファインダーにクラッシュダンプを表示", - "Toggle Developer Tools": "開発者ツールの切り替え", - "More Information": "詳しくは", - "Show Logs in Explorer": "Explorerにログを表示", - "Show Logs in Finder": "ファインダーにログを表示", - "SnackBar": { - " to exit full screen": " を押します", - "esc": "esc", - "Press ": "全画面表示を終了するには " - }, - "Sorry, you are not allowed to access this website": "申し訳ありませんが、このウェブサイトへのアクセスは許可されていません", - "Speech": "スピーチ", - "Start Speaking": "スピーチを開始", - "Stop Speaking": "スピーチを終了", - "Symphony Help": "Symphonyのヘルプ", - "TitleBar": { - "Close": "閉じる", - "Maximize": "最大化する", - "Menu": "メニュー", - "Minimize": "最小化する" - }, - "Title Bar Style": "タイトルバーのスタイル", - "Toggle Full Screen": "画面表示を切り替え", - "Troubleshooting": "トラブルシューティング", - "Unable to generate crash reports due to ": "クラッシュレポートを生成できません。理由: ", - "Unable to generate logs due to ": "ログを生成できません。理由:", - "Undo": "元に戻す", - "Updating Title bar style requires Symphony to relaunch.": "タイトルバーのスタイルを更新するには、Symphonyが再起動する必要があります。", - "View": "ビュー", - "Window": "ウインドウ", - "Your administrator has disabled": "管理者によて無効にされています", - "Zoom": "ズーム", - "Zoom In": "ズームイン", - "Zoom Out": "ズームアウト" -} diff --git a/package.json b/package.json index 6cd1741d..4ba4fa04 100644 --- a/package.json +++ b/package.json @@ -82,12 +82,12 @@ "devDependencies": { "@types/enzyme": "3.9.0", "@types/ffi-napi": "2.4.1", - "@types/ref-napi": "1.4.0", "@types/jest": "23.3.12", "@types/lodash.omit": "4.5.4", "@types/node": "10.11.4", "@types/react": "16.8.3", "@types/react-dom": "16.0.9", + "@types/ref-napi": "1.4.0", "bluebird": "3.5.3", "browserify": "16.2.3", "chromedriver": "2.45.0", @@ -101,6 +101,7 @@ "electron-rebuild": "1.8.2", "enzyme": "3.9.0", "enzyme-adapter-react-16": "1.10.0", + "enzyme-to-json": "3.3.5", "eslint": "5.6.1", "eslint-config-airbnb": "17.1.0", "eslint-plugin-import": "2.14.0", diff --git a/spec/__mocks__/electron.ts b/spec/__mocks__/electron.ts index dc36bf48..fa17fb33 100644 --- a/spec/__mocks__/electron.ts +++ b/spec/__mocks__/electron.ts @@ -8,6 +8,7 @@ const appName: string = 'Symphony'; const executableName: string = '/Symphony.exe'; const isReady: boolean = true; const version: string = '4.0.0'; +const openAtLogin: boolean = true; interface IApp { commandLine: any; getAppPath(): string; @@ -18,6 +19,11 @@ interface IApp { on(eventName: any, cb: any): void; once(eventName: any, cb: any): void; setPath(value: string, path: string): void; + setLoginItemSettings(settings: { openAtLogin: boolean, path: string }): void; + getLoginItemSettings(options?: { path: string, args: string[] }): LoginItemSettings; +} +interface LoginItemSettings { + openAtLogin: boolean; } interface IIpcMain { on(event: any, cb: any): void; @@ -67,6 +73,8 @@ export const app: IApp = { once: (eventName, cb) => { ipcEmitter.on(eventName, cb); }, + setLoginItemSettings: () => jest.fn(), + getLoginItemSettings: () => { openAtLogin }, }; // simple ipc mocks for render and main process ipc using diff --git a/spec/autoLaunchController.spec.ts b/spec/autoLaunchController.spec.ts index fd6c56d0..20c7560c 100644 --- a/spec/autoLaunchController.spec.ts +++ b/spec/autoLaunchController.spec.ts @@ -1,6 +1,6 @@ import { autoLaunchInstance } from '../src/app/auto-launch-controller'; import { config } from '../src/app/config-handler'; -import * as electron from './__mocks__/electron'; +import { app } from './__mocks__/electron'; jest.mock('electron-log'); @@ -30,42 +30,26 @@ describe('auto launch controller', async () => { }); it('should call `enableAutoLaunch` correctly', async () => { - const spyFn = 'enable'; - const spy = jest.spyOn(autoLaunchInstance, spyFn); + const spyFn = 'setLoginItemSettings'; + const spy = jest.spyOn(app, spyFn); await autoLaunchInstance.enableAutoLaunch(); expect(spy).toBeCalled(); }); it('should call `disableAutoLaunch` correctly', async () => { - const spyFn = 'disable'; - const spy = jest.spyOn(autoLaunchInstance, spyFn); + const spyFn = 'setLoginItemSettings'; + const spy = jest.spyOn(app, spyFn); await autoLaunchInstance.disableAutoLaunch(); expect(spy).toBeCalled(); }); it('should call `isAutoLaunchEnabled` correctly', async () => { - const spyFn = 'isEnabled'; - const spy = jest.spyOn(autoLaunchInstance, spyFn); + const spyFn = 'getLoginItemSettings'; + const spy = jest.spyOn(app, spyFn); await autoLaunchInstance.isAutoLaunchEnabled(); expect(spy).toBeCalled(); }); - it('should fail `enableAutoLaunch` when catch is trigged', async () => { - const spyFn = 'showMessageBox'; - const spy = jest.spyOn(electron.dialog, spyFn); - autoLaunchInstance.enable = jest.fn(() => Promise.reject()); - await autoLaunchInstance.enableAutoLaunch(); - expect(spy).toBeCalled(); - }); - - it('should fail `disableAutoLaunch` when catch is trigged', async () => { - const spyFn = 'showMessageBox'; - const spy = jest.spyOn(electron.dialog, spyFn); - autoLaunchInstance.disable = jest.fn(() => Promise.reject()); - await autoLaunchInstance.disableAutoLaunch(); - expect(spy).toBeCalled(); - }); - it('should enable AutoLaunch when `handleAutoLaunch` is called', async () => { const spyFn = 'enableAutoLaunch'; const spy = jest.spyOn(autoLaunchInstance, spyFn); @@ -84,6 +68,7 @@ describe('auto launch controller', async () => { const spy = jest.spyOn(autoLaunchInstance, spyFn); jest.spyOn(autoLaunchInstance,'isAutoLaunchEnabled').mockImplementation(() => true); await autoLaunchInstance.handleAutoLaunch(); + console.log(autoLaunchInstance.isAutoLaunchEnabled()); expect(spy).toBeCalled(); }); }); diff --git a/src/app/auto-launch-controller.ts b/src/app/auto-launch-controller.ts index 6cef2159..e7600ecf 100644 --- a/src/app/auto-launch-controller.ts +++ b/src/app/auto-launch-controller.ts @@ -57,12 +57,15 @@ class AutoLaunchController { const { launchOnStartup }: IConfig = config.getConfigFields([ 'launchOnStartup' ]); const { openAtLogin: isAutoLaunchEnabled }: LoginItemSettings = this.isAutoLaunchEnabled(); + console.log(this.isAutoLaunchEnabled()); + if (typeof launchOnStartup === 'boolean' && launchOnStartup) { if (!isAutoLaunchEnabled) { this.enableAutoLaunch(); } return; } + console.log(`Auto launch enabled!! ${isAutoLaunchEnabled}`) if (isAutoLaunchEnabled) { this.disableAutoLaunch(); } diff --git a/src/app/window-handler.ts b/src/app/window-handler.ts index b393bed6..84beda2b 100644 --- a/src/app/window-handler.ts +++ b/src/app/window-handler.ts @@ -61,7 +61,7 @@ export class WindowHandler { sandbox: true, nodeIntegration: false, devTools: false, - contextIsolation: true, + contextIsolation: false, }, winKey: getGuid(), }; @@ -83,7 +83,7 @@ export class WindowHandler { webPreferences: { nodeIntegration: false, sandbox: true, - contextIsolation: true, + contextIsolation: false, }, winKey: getGuid(), }; @@ -106,7 +106,7 @@ export class WindowHandler { sandbox: true, nodeIntegration: false, devTools: false, - contextIsolation: true, + contextIsolation: false, }, winKey: getGuid(), }; @@ -131,7 +131,7 @@ export class WindowHandler { sandbox: true, nodeIntegration: false, devTools: false, - contextIsolation: true, + contextIsolation: false, }, winKey: getGuid(), }; @@ -152,7 +152,7 @@ export class WindowHandler { sandbox: true, nodeIntegration: false, devTools: false, - contextIsolation: true, + contextIsolation: false, }, winKey: getGuid(), }; diff --git a/tests/DownloadManager.test.js b/tests/DownloadManager.test.js deleted file mode 100644 index b27d5e8d..00000000 --- a/tests/DownloadManager.test.js +++ /dev/null @@ -1,90 +0,0 @@ -const downloadManager = require('../js/downloadManager'); -const electron = require('./__mocks__/electron'); - -describe('download manager', function() { - describe('Download Manager to create DOM once download is initiated', function() { - beforeEach(function() { - global.document.body.innerHTML = - '
' + - '
'; - }); - - it('should inject download bar element into DOM once download is initiated', function() { - electron.ipcRenderer.send('downloadCompleted', { _id: '12345', fileName: 'test.png', total: 100 }); - expect(document.getElementsByClassName('text-cutoff')[0].innerHTML).toBe('test.png'); - expect(document.getElementById('per').innerHTML).toBe('100 Downloaded'); - }); - - it('should inject multiple download items during multiple downloads', function() { - electron.ipcRenderer.send('downloadCompleted', { _id: '12345', fileName: 'test (1).png', total: 100 }); - electron.ipcRenderer.send('downloadCompleted', { _id: '67890', fileName: 'test (2).png', total: 200 }); - - let fileNames = document.getElementsByClassName('text-cutoff'); - let fNames = []; - - for (var i = 0; i < fileNames.length; i++) { - fNames.push(fileNames[i].innerHTML); - } - - expect(fNames).toEqual(expect.arrayContaining(['test (1).png', 'test (2).png'])); - expect(document.getElementById('per').innerHTML).toBe('100 Downloaded'); - - let downloadElements = document.getElementsByClassName('download-element'); - expect(downloadElements[0].id).toBe('67890'); - expect(downloadElements[1].id).toBe('12345'); - }); - - }); - - describe('Download Manager to initiate footer', function() { - beforeEach(function() { - global.document.body.innerHTML = - ''; - }); - - it('should inject dom element once download is completed', function() { - electron.ipcRenderer.send('downloadProgress'); - expect(document.getElementById('footer').classList).not.toContain('hidden'); - }); - - it('should remove the download bar and clear up the download items', function() { - - electron.ipcRenderer.send('downloadProgress'); - expect(document.getElementById('footer').classList).not.toContain('hidden'); - - document.getElementById('close-download-bar').click(); - expect(document.getElementById('footer').classList).toContain('hidden'); - - }); - - }); - - describe('Download Manager to initiate footer', function() { - - beforeEach(function() { - global.document.body.innerHTML = - ''; - }); - - it('should inject ul element if not found', function() { - - electron.ipcRenderer.send('downloadProgress'); - - expect(document.getElementById('download-main')).not.toBeNull(); - expect(document.getElementById('footer').classList).not.toContain('hidden'); - - }); - - }); - -}); \ No newline at end of file diff --git a/tests/ProtocolHandler.test.js b/tests/ProtocolHandler.test.js deleted file mode 100644 index f152b880..00000000 --- a/tests/ProtocolHandler.test.js +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Created by vishwas on 10/05/17. - */ -const protocolHandler = require('../js/protocolHandler'); -const electron = require('./__mocks__/electron'); - -describe('protocol handler', function () { - - const url = 'symphony://?userId=100001'; - const nonProtocolUrl = 'sy://abc=123'; - - const mainProcess = electron.ipcMain; - const protocolWindow = electron.ipcRenderer; - - beforeAll(function () { - protocolHandler.setProtocolWindow(protocolWindow); - }); - - it('process a protocol action', function (done) { - - const spy = jest.spyOn(protocolHandler, 'processProtocolAction'); - protocolHandler.processProtocolAction(url); - expect(spy).toHaveBeenCalledWith(url); - - done(); - - }); - - it('protocol url should be undefined by default', function (done) { - expect(protocolHandler.getProtocolUrl()).toBeUndefined(); - done(); - }); - - it('protocol handler open url should be called', function (done) { - - const spy = jest.spyOn(mainProcess, 'send'); - mainProcess.send('open-url', url); - - expect(spy).toHaveBeenCalled(); - - done(); - - }); - - it('protocol handler open url should be called', function(done) { - - const spy = jest.spyOn(mainProcess, 'send'); - mainProcess.send('open-url', nonProtocolUrl); - - expect(spy).toHaveBeenCalled(); - - done(); - - }); - - it('check protocol action should be called', function (done) { - - const spy = jest.spyOn(protocolHandler, 'checkProtocolAction'); - const setSpy = jest.spyOn(protocolHandler, 'setProtocolUrl'); - - protocolHandler.setProtocolUrl(url); - expect(setSpy).toHaveBeenCalledWith(url); - - protocolHandler.checkProtocolAction(); - expect(spy).toHaveBeenCalled(); - - expect(protocolHandler.getProtocolUrl()).toBeUndefined(); - - done(); - - }); - - it('check protocol action should be called when we have an incorrect protocol url', function (done) { - - const spy = jest.spyOn(protocolHandler, 'checkProtocolAction'); - const setSpy = jest.spyOn(protocolHandler, 'setProtocolUrl'); - - protocolHandler.setProtocolUrl(nonProtocolUrl); - expect(setSpy).toHaveBeenCalledWith(nonProtocolUrl); - - protocolHandler.checkProtocolAction(); - expect(spy).toHaveBeenCalled(); - - expect(protocolHandler.getProtocolUrl()).toBeUndefined(); - - done(); - - }); - - it('check protocol action should be called when the protocol url is undefined', function(done) { - - const spy = jest.spyOn(protocolHandler, 'checkProtocolAction'); - const setSpy = jest.spyOn(protocolHandler, 'setProtocolUrl'); - - protocolHandler.setProtocolUrl(undefined); - expect(setSpy).toHaveBeenCalledWith(undefined); - - protocolHandler.checkProtocolAction(); - expect(spy).toHaveBeenCalled(); - - expect(protocolHandler.getProtocolUrl()).toBeUndefined(); - - done(); - - }); - - it('should cache the protocol url if the protocol window is not defined yet', (done) => { - protocolHandler.setProtocolWindow(null); - const setSpy = jest.spyOn(protocolHandler, 'setProtocolUrl'); - protocolHandler.setProtocolUrl(url); - - protocolHandler.checkProtocolAction(); - expect(setSpy).toHaveBeenCalled(); - - done(); - }); - -}); \ No newline at end of file diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index dd299ab5..00000000 --- a/tests/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# to override dependencies -- use https://github.com/thlorenz/proxyquire diff --git a/tests/ScreenSnippet.test.js b/tests/ScreenSnippet.test.js deleted file mode 100644 index a9fac356..00000000 --- a/tests/ScreenSnippet.test.js +++ /dev/null @@ -1,134 +0,0 @@ -const { ScreenSnippet, readResult } = require('../js/screenSnippet'); -const path = require('path'); -const fs = require('fs'); -const os = require('os'); - -const { isMac } = require('../js/utils/misc.js'); - -const snippetBase64 = require('./fixtures/snippet/snippet-base64.js'); - -// mock child_process used in ScreenSnippet -jest.mock('child_process', function() { - return { - execFile: mockedExecFile - } -}); - -/** - * mock version of execFile just creates a copy of a test jpeg file. - */ -function mockedExecFile(util, args, doneCallback) { - let outputFileName = args[args.length - 1]; - - copyTestFile(outputFileName, function(copyTestFile) { - doneCallback(); - }); -} - -function copyTestFile(destFile, done) { - const testfile = path.join(__dirname, - 'fixtures/snippet/ScreenSnippet.jpeg'); - - let reader = fs.createReadStream(testfile); - let writer = fs.createWriteStream(destFile); - - writer.on('close', function() { - done(); - }); - - reader.pipe(writer); -} - -function createTestFile(done) { - let tmpDir = os.tmpdir(); - const testFileName = path.join(tmpDir, - 'ScreenSnippet-' + Date.now() + '.jpeg'); - - copyTestFile(testFileName, function() { - done(testFileName) - }); -} - - - -describe('Tests for ScreenSnippet', function() { - describe('when reading a valid jpeg file', function() { - - // skip test for windows - until feature is supported - if (isMac) { - it('should match base64 output', function(done) { - let s = new ScreenSnippet(); - s.capture().then(gotImage); - - function gotImage(rsp) { - expect(rsp.type).toEqual('image/jpg;base64'); - expect(rsp.data).toEqual(snippetBase64); - done(); - } - }); - } - - it('should remove output file after completed', function(done) { - createTestFile(function(testfileName) { - readResult(testfileName, resolve); - - function resolve() { - // should be long enough before file - // gets removed - setTimeout(function() { - let exists = fs.existsSync(testfileName); - expect(exists).toBe(false); - done(); - }, 2000); - } - }); - }); - }); - - it('should fail if output file does not exist', function(done) { - let nonExistentFile = 'bogus.jpeg'; - readResult(nonExistentFile, resolve, reject); - - function resolve() { - // shouldn't get here - expect(true).toBe(false); - } - - function reject(err) { - expect(err).toBeTruthy(); - done(); - } - }); - - // skip test for windows - until feature is supported - if (isMac) { - it('should fail if read file fails', function(done) { - const origFsReadFile = fs.readFile; - - fs.readFile = jest.fn(mockedReadFile); - - function mockedReadFile(filename, callback) { - callback(new Error('failed')); - } - - let s = new ScreenSnippet(); - s.capture().then(resolved).catch(rejected); - - function resolved(err) { - cleanup(); - // shouldn't get here - expect(true).toBe(false); - } - - function rejected(err) { - expect(err).toBeTruthy(); - cleanup(); - done(); - } - - function cleanup() { - fs.readFile = origFsReadFile; - } - }); - } -}); \ No newline at end of file diff --git a/tests/__mocks__/electron.js b/tests/__mocks__/electron.js deleted file mode 100644 index a584a5c5..00000000 --- a/tests/__mocks__/electron.js +++ /dev/null @@ -1,80 +0,0 @@ -const path = require('path'); -const EventEmitter = require('events'); - -let ipcEmitter = new EventEmitter(); - -// use config provided by test framework -function pathToConfigDir() { - return path.join(__dirname, '/../fixtures'); -} - -// electron app mock... -const app = { - getAppPath: pathToConfigDir, - getPath: function(type) { - if (type === 'exe') { - return path.join(pathToConfigDir(), '/Symphony.exe'); - } - return pathToConfigDir(); - }, - on: function() { - // no-op - } -}; - -// simple ipc mocks for render and main process ipc using -// nodes' EventEmitter -const ipcMain = { - on: function(event, cb) { - ipcEmitter.on(event, cb); - }, - send: function (event, args) { - const senderEvent = { - sender: { - send: function (event, arg) { - ipcEmitter.emit(event, arg); - } - } - }; - ipcEmitter.emit(event, senderEvent, args); - }, -}; - -const ipcRenderer = { - sendSync: function(event, args) { - let listeners = ipcEmitter.listeners(event); - if (listeners.length > 0) { - let listener = listeners[0]; - const eventArg = {}; - listener(eventArg, args); - return eventArg.returnValue; - } - return null; - }, - send: function(event, args) { - const senderEvent = { - sender: { - send: function (event, arg) { - ipcEmitter.emit(event, arg); - } - } - }; - ipcEmitter.emit(event, senderEvent, args); - }, - on: function(eventName, cb) { - ipcEmitter.on(eventName, cb); - }, - removeListener: function(eventName, cb) { - ipcEmitter.removeListener(eventName, cb); - } -}; - -module.exports = { - require: jest.fn(), - match: jest.fn(), - app: app, - ipcMain: ipcMain, - ipcRenderer: ipcRenderer, - remote: jest.fn(), - dialog: jest.fn() -}; diff --git a/tests/activityDetection.test.js b/tests/activityDetection.test.js deleted file mode 100644 index 950f24ee..00000000 --- a/tests/activityDetection.test.js +++ /dev/null @@ -1,84 +0,0 @@ -const electron = require('./__mocks__/electron'); -const activityDetection = require('../js/activityDetection'); -describe('Tests for Activity Detection', function() { - - const originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; - jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000; - - beforeAll(function(done) { - electron.app.isReady = jest.fn().mockReturnValue(true); - electron.powerMonitor = { querySystemIdleTime: jest.fn() } - done(); - }); - - beforeEach(function() { - jest.clearAllMocks() - }); - - afterAll(function(done) { - jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; - done(); - }); - - it('should send activity event', function() { - const spy = jest.spyOn(activityDetection, 'send'); - expect(spy).not.toBeCalled(); - - activityDetection.send({ systemIdleTime: 120000 }); - expect(spy).toHaveBeenCalledWith({ systemIdleTime: 120000 }); - - }); - - it('should monitor user activity', function() { - activityDetection.setActivityWindow(500000, electron.ipcRenderer); - const spy = jest.spyOn(activityDetection, 'monitorUserActivity'); - - expect(spy).not.toBeCalled(); - - activityDetection.monitorUserActivity(); - expect(spy).toHaveBeenCalled(); - - }); - - it('should start `activityDetection()`', () => { - const spy = jest.spyOn(activityDetection, 'activityDetection'); - expect(spy).not.toBeCalled(); - - activityDetection.activityDetection(); - expect(spy).toBeCalled(); - }); - - it('should not send activity event as data is undefined', function() { - const spy = jest.spyOn(activityDetection, 'send'); - - expect(spy).not.toBeCalled(); - - activityDetection.send(undefined); - expect(spy).toHaveBeenCalledWith(undefined); - - }); - - it('should call `send()` when period was greater than idleTime', () => { - - const spy = jest.spyOn(activityDetection, 'send'); - - expect(spy).not.toBeCalled(); - - electron.powerMonitor = { querySystemIdleTime: jest.fn().mockImplementationOnce(cb => cb(1)) }; - activityDetection.setActivityWindow(900000, electron.ipcRenderer); - - expect(spy).toBeCalled(); - - }); - - it('should start `activityDetection()` when `setActivityWindow()` was called', () => { - const spy = jest.spyOn(activityDetection, 'activityDetection'); - - expect(spy).not.toBeCalled(); - - activityDetection.setActivityWindow(900000, electron.ipcRenderer); - - expect(spy).toBeCalled(); - - }); -}); diff --git a/tests/config.test.js b/tests/config.test.js deleted file mode 100644 index 6fae46d5..00000000 --- a/tests/config.test.js +++ /dev/null @@ -1,322 +0,0 @@ -const { clearCachedConfigs, getConfigField, updateConfigField, configFileName, saveUserConfig } = require('../js/config'); -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -// mock required so getConfig reads config from correct path -jest.mock('../js/utils/misc.js', function() { - return { - isDevEnv: false, - isMac: false - } -}); - -let globalConfigDir; -let userConfigDir; - -jest.mock('electron', function() { - return { - app: { - getPath: mockedGetPath, - getVersion: mockedGetVersion - } - } -}); - -function mockedGetVersion() { - return '3.1.0'; -} - -function mockedGetPath(type) { - if (type === 'exe') { - return globalConfigDir; - } - - if (type === 'userData') { - return userConfigDir - } - return ''; -} - -describe('read/write config tests', function() { - - beforeEach(function() { - /// reset module vars between running tests. - globalConfigDir = null; - userConfigDir = null; - - // reset module values so each test starts clean. - clearCachedConfigs(); - }); - - afterEach(function() { - // clean up temp files creating during tests - if (globalConfigDir) { - fs.unlinkSync(path.join(globalConfigDir, configFileName)); - fs.rmdirSync(globalConfigDir); - fs.rmdirSync(path.join(globalConfigDir, '..')); - } - if (userConfigDir) { - fs.unlinkSync(path.join(userConfigDir, configFileName)); - fs.rmdirSync(userConfigDir); - } - }); - - function createTempConfigFile(filePath, config) { - fs.writeFileSync(filePath, JSON.stringify(config)); - } - - function createTempUserConfig(config) { - const tmpDir = os.tmpdir(); - userConfigDir = fs.mkdtempSync(path.join(tmpDir, 'config-')); - return createTempConfigFile(path.join(userConfigDir, configFileName), config); - } - - function createTempGlobalConfig(config) { - const tmpDir = os.tmpdir(); - globalConfigDir = path.join(fs.mkdtempSync(path.join(tmpDir, 'config-')), 'config'); - fs.mkdirSync(globalConfigDir); - return createTempConfigFile(path.join(globalConfigDir, configFileName), config); - } - - function removeTempConfigFile(filePath) { - fs.unlinkSync(filePath); - } - - describe('getConfigField tests', function() { - it('should fail when field not present in either user or global config', function() { - const userConfig = { - url: 'something' - }; - - createTempUserConfig(userConfig); - - const globalConfig = { - url: 'something-else' - }; - - createTempGlobalConfig(globalConfig); - - return getConfigField('noturl').catch(function(err) { - expect(err).toBeTruthy(); - }); - }); - - it('should succeed when field only present in user config', function() { - const userConfig = { - url: 'something' - }; - - createTempUserConfig(userConfig); - - return getConfigField('url').then(function(url) { - expect(url).toBe('something'); - }); - }); - - it('should succeed when field only present in global config', function() { - const globalConfig = { - url: 'something-else' - }; - - createTempGlobalConfig(globalConfig); - - return getConfigField('url').then(function(url) { - expect(url).toBe('something-else'); - }); - }); - - it('should succeed and return user config field when value is in both', function() { - const userConfig = { - url: 'something' - }; - - createTempUserConfig(userConfig); - - const globalConfig = { - url: 'something-else' - }; - - createTempGlobalConfig(globalConfig); - - return getConfigField('url').then(function(url) { - expect(url).toBe('something'); - }); - }); - - it('should fail when global config path is invalid', function() { - const globalConfig = { - url: 'something-else' - }; - createTempGlobalConfig(globalConfig); - - let correctConfigDir = globalConfigDir; - globalConfigDir = '//'; - - return getConfigField('url').catch(function(err) { - globalConfigDir = correctConfigDir; - expect(err).toBeTruthy(); - }); - }); - - it('should fail when user config path is invalid', function() { - const userConfig = { - url: 'something' - }; - createTempUserConfig(userConfig); - - let correctConfigDir = userConfigDir; - userConfigDir = '//'; - - return getConfigField('url').catch(function(err) { - userConfigDir = correctConfigDir; - expect(err).toBeTruthy(); - }); - }); - - it('should read cached user config value rather than reading file from disk again', function(done) { - const userConfig = { - url: 'qa4.symphony.com' - }; - createTempUserConfig(userConfig); - - const userConfig2 = { - url: 'qa5.symphony.com' - }; - - return getConfigField('url') - .then(function() { - createTempUserConfig(userConfig2); - }) - .then(function() { - return getConfigField('url') - }) - .then(function(url) { - expect(url).toBe('qa4.symphony.com'); - done(); - }); - }); - - it('should read cache global config value rather than reading file from disk again', function(done) { - const globalConfig = { - url: 'qa8.symphony.com' - }; - createTempGlobalConfig(globalConfig); - - const globalConfig2 = { - url: 'qa9.symphony.com' - }; - - return getConfigField('url') - .then(function() { - createTempGlobalConfig(globalConfig2); - }) - .then(function() { - return getConfigField('url') - }) - .then(function(url) { - expect(url).toBe('qa8.symphony.com'); - done(); - }); - }); - - }); - - describe('updateConfigField tests', function() { - - it('should succeed and overwrite existing field', function() { - const userConfig = { - url: 'something' - }; - - createTempUserConfig(userConfig); - - return updateConfigField('url', 'hello world') - .then(function(newConfig) { - expect(newConfig).toEqual({ - url: 'hello world' - }); - }); - }); - - it('should succeed and add new field', function() { - const userConfig = { - url: 'something' - }; - - createTempUserConfig(userConfig); - - return updateConfigField('url2', 'hello world') - .then(function(newConfig) { - expect(newConfig).toEqual({ - url: 'something', - url2: 'hello world' - }); - }); - }); - - it('should fail to update if invalid field name', function() { - - const userConfig = { - url: 'something' - }; - - createTempUserConfig(userConfig); - - return updateConfigField('', 'hello word').catch(function (err) { - expect(err.message).toBe('can not save config, invalid input'); - }); - - }); - - it('should throw error if path is not defined', function() { - - userConfigDir = null; - - return updateConfigField('url2', 'hello world') - .catch(function (err) { - expect(err.message).toBe('The "path" argument must be of type string. Received type object'); - }); - - }); - - it('should throw error if fieldName is not defined', function() { - - const userConfig = { - url: 'something' - }; - - createTempUserConfig(userConfig); - - return saveUserConfig(undefined, 'something', 'oldConfig') - .catch(function (reject) { - expect(reject.message).toBe('can not save config, invalid input'); - }); - }); - - it('should throw error if config is not defined', function() { - - const userConfig = { - url: 'something' - }; - - createTempUserConfig(userConfig); - - return saveUserConfig('url2', 'something', undefined) - .catch(function (reject) { - expect(reject.message).toBe('can not save config, invalid input'); - }); - }); - - it('should throw error if path is not defined for saveUserConfig()', function() { - - userConfigDir = null; - - return saveUserConfig('url2', 'hello world') - .catch(function (err) { - expect(err.message).toBe('The "path" argument must be of type string. Received type object'); - }); - }); - - }); -}); diff --git a/tests/fixtures/snippet/ScreenSnippet.jpeg b/tests/fixtures/snippet/ScreenSnippet.jpeg deleted file mode 100644 index f399021d84e4e91a0a2e2e5d2575b35b87b9c706..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2813 zcmb`I2{e>nAII-(#+D@}p_o$0QY1`8bC~R9UrG+ym&h_B^fHsgl#;iy{adU_@y9=f zqLgF{uQzKbG9sjGk!5(^JENm>{^x(ryPWqsbMO5;cYgQxJm2U3o@bDE$VVVzYG`5z zP$(2Ih97_o0-8a%uNMGEj{+qC058CzqyP>Q3bsQi>2)?lDFO_49t{A~7hr$Jn8T4H zn2oz;ZA53H*CQyIm~}qL2}QmFlBG`|Es*LH7(~?Atp-T?CP%THj8I(O$wiTltyg*y zK?!#39ADbRf<7H#p)Z(FC@z-P7KSEA44@GJ&@|VeAb-x{fYY=PYa@N)vEz{$)4c#l3dWuFA>fWwO|#6C0NCn8)-tnFBPHzg+TbQozHL z>JH8G!YOwTs(S!rf5UJXh9rqfGi#YFA(y34tuZh6o=iojOJJo53#kT zKyRBNi@2?^B8R=#n8aasA6E|;!@1)sYu5k?06ycxL~XVGs(Rd`|6>>69}0bP#i9Qp--5*QQ_;_K~0BkI9B>q(>p1_XuDJVR87 zCa2wZs}T39sjG3~ID5t!0hqPs?geU7>#w=MECBY)0f4#kYtAhNfbwSmY<}}=Zbv!* z!m!55{t@IF;<}a(_NaO6zdNRSY3$|3qXhKj11=Ly+Zc1X;;}=ZAat z(jR#VM0vqMK*6B41GFd#BZ@*^16g<;9DK4k57$qj&=@R^2hYpLF8~8dMF1Lw!Jx4i z9F9}$s3`b5z>4BFZd2Fe5wmi|Zx0sV8+|Q{S5E(7HNpDr)D8`|kn?=}nE-R?>qn)9o(T(&I2&^zHja7mQvCHB zNy#axX}{gf&bga=FYo??{E|nHOUufiJgsxPQ!YN;1=?hf76bAgFB}=*0|38Gh^rfCui%%e);MH6oBRmgGOU; z7z_r-gM;GX<>82zpO-8CpAzIsh$G>3AutIF=D=dHc=#0&;1dw}uZ4VoFGn2G3j{GJ zSWFmEKnCo}?D)A!W-k(W0*dNq)4f^q0geHKy?HX1b_}#MlB?xD1zvoCjIkLFOo0y=aKB&E@RiNn?_xdId!|ZHQy%q#+Bro z5?+cJj~Y2njZfs-HBFR`haY<{ zmEC41r;Gqq1lT5~FnlKkYpL13{VK8e4DUTXxpEKUoswIZe<^{!Ssn{?zweVca@tQ1qN&GZn`bN?4_U9IZ ztyiCAHT;+UZi^BlRy4FqOPg(QlUcbp^d_e7UK3Ugu5Er$^jc#eo^XuxK z$YjS7{OUxhw@N70C-YfKb+NopO&xb6Zdl2Cc9c+dDKFeyLMH0d3*RK6Y{e1@@}&CE zO1q1AH!Nteac6Z{(3Jc#uL}QI$v!C|f`o2T@KQP~=!QpE7+t{|@0X2@GJ=B08xY{> z`FQGj*NG;!1*0-otHOz&QjF+ z&z%*z0RLPn)2cL`mE4Ami~{Y_?z7{yXN#3jDs3!CPad^w2^LRPk^lN*)w{{dX+zSG z{J5LiSmU=&)0{v?U;oynFvyZafL6%g=wQI`5SwC>vrT5;wVb67gTA6u93 z2>9g7Sg4qte)nc>U{WD;g{IrgWQI&sktdRDPkbnJ>7Qd;q}(rlz!-E2*I124z@n7P z>h8n^2N^*GWHMF_QxR~vNZ6w_d%j`OFMcU?l~5l;9+gGFwOj 1 sec', function() { - const callback = jest.fn(); - const throttledCB = throttle(1000, callback); - - expect(callback).not.toBeCalled(); - - throttledCB(); - expect(callback.mock.calls.length).toBe(1); - - now += 1000; - - throttledCB(); - expect(callback.mock.calls.length).toBe(2); - }); - - it('expect clearTimeout to be invoked', function() { - const callback = jest.fn(); - const throttledCB = throttle(1000, callback); - - expect(callback).not.toBeCalled(); - - throttledCB(); - expect(callback.mock.calls.length).toBe(1); - expect(clearTimeout.mock.calls.length).toBe(0); - - now -= 1000; - throttledCB(); - expect(callback.mock.calls.length).toBe(1); - - now += 1000; - throttledCB(); - expect(callback.mock.calls.length).toBe(1); - expect(clearTimeout.mock.calls.length).toBe(1); - }); - - describe('expect to throw exception', function() { - it('when calling throttle with time equal to zero', function(done) { - try { - throttle(0, function() {}); - } catch(error) { - expect(error.message).toBeDefined(); - done(); - } - }); - - it('when calling throttle with time less than zero', function(done) { - try { - throttle(-1, function() {}); - } catch(error) { - expect(error.message).toBeDefined(); - done(); - } - }); - - it('when calling throttle without a function callback', function(done) { - try { - throttle(1, 'not a func'); - } catch(error) { - expect(error.message).toBeDefined(); - done(); - } - }); - }); - - afterEach(function() { - // restore orig - Date.now = origNow; - }) -}); diff --git a/tests/utils/whitelist.test.js b/tests/utils/whitelist.test.js deleted file mode 100644 index 93195901..00000000 --- a/tests/utils/whitelist.test.js +++ /dev/null @@ -1,88 +0,0 @@ -const { checkWhitelist } = require('../../js/utils/whitelistHandler'); - -describe('validate url with whitelist', function() { - - describe('checkWhitelist truth tests', function() { - - it('should return true when the url is valid', function() { - const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com'; - const url = 'https://my.symphony.com/'; - - return expect(checkWhitelist(url, whitelist)).toBeTruthy(); - }); - - it('should return true when if hostName is defined', function() { - const whitelist = 'www.symphony.com, app.symphony.com, symphony.com'; - const url = 'https://xyz.symphony.com/'; - - return expect(checkWhitelist(url, whitelist)).toBeTruthy(); - }); - - it('should return true when the first occurrence of sub-domain is matched', function() { - const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com'; - const url = 'https://xyz.my.symphony.com/'; - - return expect(checkWhitelist(url, whitelist)).toBeTruthy(); - }); - - it('should return true when for any URL if whitelist has *', function() { - const whitelist = '*'; - const url = 'https://www.example.com/'; - - return expect(checkWhitelist(url, whitelist)).toBeTruthy(); - }); - - it('should return true for non-standard TLDs', function() { - const whitelist = 'symphony.com, symphony.econet'; - const url = 'https://my.symphony.econet/'; - - return expect(checkWhitelist(url, whitelist)).toBeTruthy(); - }); - - }); - - describe('checkWhitelist falsity tests', function () { - - it('should return false when sub-domain does not match', function () { - const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com'; - const url = 'https://xyz.symphony.com/'; - - return expect(checkWhitelist(url, whitelist)).toBeFalsy(); - }); - - it('should return false when hostName does not match', function () { - const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com'; - const url = 'https://my.example.com/'; - - return expect(checkWhitelist(url, whitelist)).toBeFalsy(); - }); - - it('should return false when TLD does not match', function () { - const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com'; - const url = 'https://my.symphony.echonet/'; - - return expect(checkWhitelist(url, whitelist)).toBeFalsy(); - }); - - it('should return false when the URL is invalid', function () { - const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com'; - const url = 'invalidUrl'; - - return expect(checkWhitelist(url, whitelist)).toBeFalsy(); - }); - - it('should return false when the whitelist is invalid', function () { - const whitelist = 'invalidWhitelist'; - const url = 'https://www.symphony.com'; - - return expect(checkWhitelist(url, whitelist)).toBeFalsy(); - }); - - it('should return false if whitelist is empty', function() { - const whitelist = ''; - const url = 'https://www.example.com/'; - - return expect(checkWhitelist(url, whitelist)).toBeFalsy(); - }); - }); -}); \ No newline at end of file