Merge branch 'master' into electron-17

# Conflicts:
#	js/main.js
#	js/windowMgr.js
This commit is contained in:
Vishwas Shashidhar 2017-09-13 16:13:49 +05:30
commit 40f0cdcdf9
60 changed files with 10908 additions and 720 deletions

View File

@ -57,5 +57,11 @@ In order to achieve those goals Symphony is participating and working in close c
- code coverage reports are placed in dir: converage
- tests are located in dir: tests
## Logging
- Local logging is enabled for dev environments using the module [electron-log](https://www.npmjs.com/package/electron-log)
- On macOS, the logs are stored under `~/Library/Logs/<app name>/log.log`
- On Windows, the logs are stored under `%USERPROFILE%\AppData\Roaming\<app name>\log.log`
- Remote logging is enabled for local and production cases and are sent to the backend server via the remote objects
## Misc notes
If desiring to run against server without proper cert use cmd line option: --ignore-certificate-errors

View File

@ -3,7 +3,6 @@
"minimizeOnClose" : false,
"launchOnStartup" : true,
"alwaysOnTop" : false,
"launchOnStartup" : true,
"notificationSettings": {
"position": "upper-right",
"display": ""

View File

@ -2,4 +2,23 @@
<head>
</head>
Test Window has been opened
<p>
<label for='tag'>tag:</label>
<input type='text' id='tag' value=''/>
</p>
<hr>
<p>Badge Count:<p>
<button id='inc-badge'>increment badge count</button>
<br>
<script>
var badgeCount = 0;
var incBadgeEl = document.getElementById('inc-badge');
incBadgeEl.addEventListener('click', function() {
badgeCount++;
ssf.setBadgeCount(badgeCount);
});
</script>
</html>

View File

@ -18,37 +18,10 @@ sed -i "" -E "s#\"minimizeOnClose\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#
sed -i "" -E "s#\"alwaysOnTop\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"alwaysOnTop\":\ $always_on_top#g" $newPath
sed -i "" -E "s#\"launchOnStartup\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"launchOnStartup\":\ $launch_on_startup#g" $newPath
## Add app to login items
if [ $launch_on_startup == true ]; then
mkdir ~/Library/LaunchAgents/
cat > ~/Library/LaunchAgents/com.symphony.symphony-desktop.agent.plist << EOT
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.symphony.symphony-desktop.agent</string>
<key>ProgramArguments</key>
<array>
<string>$installPath/Symphony.app/Contents/MacOS/Symphony</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
EOT
else
launchctl unload ~/Library/LaunchAgents/com.symphony.symphony-desktop.agent.plist
fi
## Remove the temp settings file created ##
rm -f $tempFilePath
## For launching symphony with sandbox enabled, create a shell script that is used as the launch point for the app
EXEC_PATH=$installPath/Symphony.app/Contents/MacOS
mv $EXEC_PATH/Symphony $EXEC_PATH/Symphony-bin
cat > $EXEC_PATH/Symphony << EOT
#!/bin/sh
exec "\${0%/*}/Symphony-bin" --enable-sandbox \$@
EOT
chmod 755 $EXEC_PATH/Symphony
exec $EXEC_PATH/Symphony --install $newPath $launch_on_startup
chmod 755 $EXEC_PATH/Symphony

View File

@ -43,50 +43,155 @@
<ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1"/>
<ROW Directory="DesktopFolder" Directory_Parent="TARGETDIR" DefaultDir="DESKTO~1|DesktopFolder" IsPseudoRoot="1"/>
<ROW Directory="ProgramMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~1|ProgramMenuFolder" IsPseudoRoot="1"/>
<ROW Directory="Release_Dir" Directory_Parent="build_Dir" DefaultDir="Release"/>
<ROW Directory="Symphony_Dir" Directory_Parent="ProgramMenuFolder" DefaultDir="Symphony"/>
<ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
<ROW Directory="app.asar.unpacked_Dir" Directory_Parent="resources_Dir" DefaultDir="APPASA~1.UNP|app.asar.unpacked"/>
<ROW Directory="build_Dir" Directory_Parent="cld_Dir" DefaultDir="build"/>
<ROW Directory="cld_Dir" Directory_Parent="paulcbetts_Dir" DefaultDir="cld"/>
<ROW Directory="config_Dir" Directory_Parent="APPDIR" DefaultDir="config"/>
<ROW Directory="jobber_Dir" Directory_Parent="vendor_Dir" DefaultDir="jobber"/>
<ROW Directory="lib_Dir" Directory_Parent="spawnrx_Dir" DefaultDir="lib"/>
<ROW Directory="locales_Dir" Directory_Parent="APPDIR" DefaultDir="locales"/>
<ROW Directory="node_modules_Dir" Directory_Parent="app.asar.unpacked_Dir" DefaultDir="NODE_M~1|node_modules"/>
<ROW Directory="paulcbetts_Dir" Directory_Parent="node_modules_Dir" DefaultDir="@PAULC~1|@paulcbetts"/>
<ROW Directory="resources_Dir" Directory_Parent="APPDIR" DefaultDir="RESOUR~1|resources"/>
<ROW Directory="spawnrx_Dir" Directory_Parent="node_modules_Dir" DefaultDir="spawn-rx"/>
<ROW Directory="src_1_Dir" Directory_Parent="spawnrx_Dir" DefaultDir="src"/>
<ROW Directory="src_Dir" Directory_Parent="lib_Dir" DefaultDir="src"/>
<ROW Directory="vendor_Dir" Directory_Parent="spawnrx_Dir" DefaultDir="vendor"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="AI_CustomARPName" ComponentId="{2817ACD9-F494-4729-9830-111EF3311CFA}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
<ROW Component="Jobber.exe" ComponentId="{A24482DB-EF3B-4FCA-8482-C7F034F7816C}" Directory_="jobber_Dir" Attributes="0" KeyPath="Jobber.exe"/>
<ROW Component="PodUrl" ComponentId="{EA80D82D-BC65-4075-A9A8-F53E2B2513CE}" Directory_="APPDIR" Attributes="260" KeyPath="PodUrl"/>
<ROW Component="ProductInformation" ComponentId="{8B92B687-8AE0-4A5C-B6AB-5D1854009CEA}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
<ROW Component="ScreenSnippet.exe" ComponentId="{B92951AB-9E69-4970-A3B5-B4E5C32F3477}" Directory_="APPDIR" Attributes="0" KeyPath="ScreenSnippet.exe"/>
<ROW Component="Symphony" ComponentId="{A6B4BA2F-2403-4B8E-9303-BF8400A9B1C4}" Directory_="Symphony_Dir" Attributes="0"/>
<ROW Component="Symphony.config" ComponentId="{644A231D-2C96-4D3D-ADB0-7820DA373499}" Directory_="config_Dir" Attributes="0" KeyPath="Symphony.config_1" Type="0"/>
<ROW Component="Symphony.exe" ComponentId="{853053E4-D96C-42FE-9AF1-52FF1F449FFD}" Directory_="APPDIR" Attributes="256" KeyPath="Symphony.exe"/>
<ROW Component="SymphonyElectron.exe" ComponentId="{050ABDCB-63C1-46C4-8E40-B3C80931C3DA}" Directory_="APPDIR" Attributes="256" KeyPath="SymphonyElectron.exe"/>
<ROW Component="am.pak" ComponentId="{76F935B8-6077-4C7A-AD1B-77E2DBA856CC}" Directory_="locales_Dir" Attributes="0" KeyPath="am.pak" Type="0"/>
<ROW Component="ambient.d.ts" ComponentId="{DF6F3FD1-B8F8-4758-977E-B395FA0E1F23}" Directory_="src_1_Dir" Attributes="0" KeyPath="ambient.d.ts" Type="0"/>
<ROW Component="apimswincoreconsolel110.dll" ComponentId="{24C259E2-26D5-4B36-9029-C373504B0DDD}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoreconsolel110.dll"/>
<ROW Component="apimswincoredatetimel110.dll" ComponentId="{E600038E-64D7-4245-9AFD-7E47970B7D6A}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoredatetimel110.dll"/>
<ROW Component="apimswincoredebugl110.dll" ComponentId="{4C033829-BD86-4529-BC64-6848E1662B75}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoredebugl110.dll"/>
<ROW Component="apimswincoreerrorhandlingl110.dll" ComponentId="{37BA9D4B-C09D-4829-A084-BD563C7E1362}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoreerrorhandlingl110.dll"/>
<ROW Component="apimswincorefilel110.dll" ComponentId="{38F5DF0C-B395-43FF-ABA1-B0D40022C22D}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorefilel110.dll"/>
<ROW Component="apimswincorefilel120.dll" ComponentId="{C344A79D-9ABD-468D-89D5-A7959D8ADF19}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorefilel120.dll"/>
<ROW Component="apimswincorefilel210.dll" ComponentId="{0E5E0F2D-D576-4EAF-AE31-D6FAFD973EE3}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorefilel210.dll"/>
<ROW Component="apimswincorehandlel110.dll" ComponentId="{DC732D85-0DC0-4662-8E25-A70E94FD39A3}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorehandlel110.dll"/>
<ROW Component="apimswincoreheapl110.dll" ComponentId="{999D3928-A009-47F8-AE8C-959946FD7792}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoreheapl110.dll"/>
<ROW Component="apimswincoreinterlockedl110.dll" ComponentId="{7C58CFF7-6DE6-4118-A4EE-312353AD1251}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoreinterlockedl110.dll"/>
<ROW Component="apimswincorelibraryloaderl110.dll" ComponentId="{B47FD549-F531-42AD-9497-DF3A48ED8E44}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorelibraryloaderl110.dll"/>
<ROW Component="apimswincorelocalizationl120.dll" ComponentId="{BF019863-CF65-4E9F-91C1-ECFEF9D92713}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorelocalizationl120.dll"/>
<ROW Component="apimswincorememoryl110.dll" ComponentId="{B39B18CB-35EC-458E-B8FA-8D0F21AF94AA}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorememoryl110.dll"/>
<ROW Component="apimswincorenamedpipel110.dll" ComponentId="{D760EA98-72E8-4462-BC90-28F9E959316B}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorenamedpipel110.dll"/>
<ROW Component="apimswincoreprocessenvironmentl110.dll" ComponentId="{75FBE549-412F-4AFE-9AD6-E54A3E3194A9}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoreprocessenvironmentl110.dll"/>
<ROW Component="apimswincoreprocessthreadsl110.dll" ComponentId="{3A1336A3-BF45-4006-893E-329152CECB3D}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoreprocessthreadsl110.dll"/>
<ROW Component="apimswincoreprocessthreadsl111.dll" ComponentId="{C10C98AD-EC3B-46BC-AE5C-0570783568BA}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoreprocessthreadsl111.dll"/>
<ROW Component="apimswincoreprofilel110.dll" ComponentId="{A3FBBBCE-610A-4D69-84F2-6D3392CAA335}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoreprofilel110.dll"/>
<ROW Component="apimswincorertlsupportl110.dll" ComponentId="{5B0B55DE-EE65-476A-AA5E-8FA02AF79115}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorertlsupportl110.dll"/>
<ROW Component="apimswincorestringl110.dll" ComponentId="{067B5B3C-03ED-4259-A90A-325D13DC7225}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincorestringl110.dll"/>
<ROW Component="apimswincoresynchl110.dll" ComponentId="{5904965D-C9E2-4ED1-850A-67C65B2061C7}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoresynchl110.dll"/>
<ROW Component="apimswincoresynchl120.dll" ComponentId="{EB1E667C-1F9E-4AB7-9A94-6840AB2E1CB6}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoresynchl120.dll"/>
<ROW Component="apimswincoresysinfol110.dll" ComponentId="{96B3CA59-EEDA-4993-B881-4E7C64B1D7D8}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoresysinfol110.dll"/>
<ROW Component="apimswincoretimezonel110.dll" ComponentId="{2320A636-16B4-40AF-A088-F4431131F282}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoretimezonel110.dll"/>
<ROW Component="apimswincoreutill110.dll" ComponentId="{C352A94B-5270-430A-B48C-4BB014005F51}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincoreutill110.dll"/>
<ROW Component="apimswincrtconiol110.dll" ComponentId="{DCB1F7CD-93D6-4BA6-9355-2095C24F1FDD}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtconiol110.dll"/>
<ROW Component="apimswincrtconvertl110.dll" ComponentId="{CAC36607-2746-4DA8-AEE3-56D22F4A5AF1}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtconvertl110.dll"/>
<ROW Component="apimswincrtenvironmentl110.dll" ComponentId="{F8CE9393-7850-49B9-B130-48DFA87C4F13}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtenvironmentl110.dll"/>
<ROW Component="apimswincrtfilesysteml110.dll" ComponentId="{D75C42A0-F297-47EE-9AC9-5E5FE2FE1908}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtfilesysteml110.dll"/>
<ROW Component="apimswincrtheapl110.dll" ComponentId="{2A7C8C46-C2B6-4CD1-96C3-97EAF4A8203C}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtheapl110.dll"/>
<ROW Component="apimswincrtlocalel110.dll" ComponentId="{1B6E355E-3BE9-4B1A-BD09-4DEB5512D4EB}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtlocalel110.dll"/>
<ROW Component="apimswincrtmathl110.dll" ComponentId="{BFFFB222-F773-4725-8B24-59A1806BDC51}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtmathl110.dll"/>
<ROW Component="apimswincrtmultibytel110.dll" ComponentId="{C90985C8-8251-421B-BD27-96D3B913399E}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtmultibytel110.dll"/>
<ROW Component="apimswincrtprivatel110.dll" ComponentId="{D7FCC4D5-4DF0-4394-9B64-35A43CD04A23}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtprivatel110.dll"/>
<ROW Component="apimswincrtprocessl110.dll" ComponentId="{5ACF94EA-8AAB-43F9-B1EF-56F170FF175B}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtprocessl110.dll"/>
<ROW Component="apimswincrtruntimel110.dll" ComponentId="{9900ED9E-06CA-4350-A592-B4670490D057}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtruntimel110.dll"/>
<ROW Component="apimswincrtstdiol110.dll" ComponentId="{80AF06FB-CD45-42A1-9140-7D932B79876C}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtstdiol110.dll"/>
<ROW Component="apimswincrtstringl110.dll" ComponentId="{C8732501-6C9F-4A7F-83A6-70718E908A00}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtstringl110.dll"/>
<ROW Component="apimswincrttimel110.dll" ComponentId="{B86BBEB1-2984-4AC9-896C-973716988AF2}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrttimel110.dll"/>
<ROW Component="apimswincrtutilityl110.dll" ComponentId="{C2BDE659-5902-481C-91F4-EE4B0291076C}" Directory_="APPDIR" Attributes="256" KeyPath="apimswincrtutilityl110.dll"/>
<ROW Component="appupdate.yml" ComponentId="{F7586760-660A-4C38-8937-138DBEC18D34}" Directory_="resources_Dir" Attributes="0" KeyPath="appupdate.yml" Type="0"/>
<ROW Component="blink_image_resources_200_percent.pak" ComponentId="{56AB17A5-B690-4CBE-A39D-512381AAAFE1}" Directory_="APPDIR" Attributes="0" KeyPath="blink_image_resources_200_percent.pak" Type="0"/>
<ROW Component="cld.node" ComponentId="{1F2DDDA0-B69B-4AF4-A641-59D455B30ACB}" Directory_="Release_Dir" Attributes="256" KeyPath="cld.node" Type="0"/>
<ROW Component="d3dcompiler_47.dll" ComponentId="{C7B87C02-3116-43A8-A70B-3592B70E6AC8}" Directory_="APPDIR" Attributes="256" KeyPath="d3dcompiler_47.dll"/>
<ROW Component="ffmpeg.dll" ComponentId="{A1C4A332-3490-44D8-A5C9-9523889B488B}" Directory_="APPDIR" Attributes="256" KeyPath="ffmpeg.dll"/>
<ROW Component="index.d.ts" ComponentId="{FB146550-23F5-45DD-82E4-90609B0C6562}" Directory_="src_Dir" Attributes="0" KeyPath="index.d.ts" Type="0"/>
<ROW Component="libEGL.dll" ComponentId="{8EEC76AB-3601-4D11-B13E-32EC2A38C539}" Directory_="APPDIR" Attributes="256" KeyPath="libEGL.dll"/>
<ROW Component="libGLESv2.dll" ComponentId="{0E8B8B21-B4C0-45C9-95D3-637FD93A4EC0}" Directory_="APPDIR" Attributes="256" KeyPath="libGLESv2.dll"/>
<ROW Component="msvcp140.dll" ComponentId="{93A6289C-CF23-4BB8-A579-7FDDD1D15591}" Directory_="APPDIR" Attributes="256" KeyPath="msvcp140.dll"/>
<ROW Component="node.dll" ComponentId="{C0972355-339E-438C-94A3-74174DE4C6B6}" Directory_="APPDIR" Attributes="256" KeyPath="node.dll"/>
<ROW Component="npmignore_1" ComponentId="{3367B8BE-13CC-4957-B834-60B1574BCFDB}" Directory_="spawnrx_Dir" Attributes="0" KeyPath="npmignore_1" Type="0"/>
<ROW Component="ucrtbase.dll" ComponentId="{16D802A3-DAD4-4BF4-AD64-88D6F63F5D1E}" Directory_="APPDIR" Attributes="256" KeyPath="ucrtbase.dll"/>
<ROW Component="vcruntime140.dll" ComponentId="{2542FC82-8D71-4351-8514-2C0D12772ED5}" Directory_="APPDIR" Attributes="256" KeyPath="vcruntime140.dll"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
<ROW Feature="D564007E3BBE4F85950A09B470A7CA65" Title="Visual C++ Redistributable for Visual Studio 2013 x86" Description="Visual C++ Redistributable for Visual Studio 2013 x86" Display="3" Level="1" Attributes="0" Components="AI_CustomARPName"/>
<ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName PodUrl ProductInformation ScreenSnippet.exe Symphony Symphony.config Symphony.exe SymphonyElectron.exe am.pak appupdate.yml blink_image_resources_200_percent.pak d3dcompiler_47.dll ffmpeg.dll libEGL.dll libGLESv2.dll node.dll"/>
<ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName Jobber.exe PodUrl ProductInformation ScreenSnippet.exe Symphony Symphony.config Symphony.exe am.pak ambient.d.ts apimswincoreconsolel110.dll apimswincoredatetimel110.dll apimswincoredebugl110.dll apimswincoreerrorhandlingl110.dll apimswincorefilel110.dll apimswincorefilel120.dll apimswincorefilel210.dll apimswincorehandlel110.dll apimswincoreheapl110.dll apimswincoreinterlockedl110.dll apimswincorelibraryloaderl110.dll apimswincorelocalizationl120.dll apimswincorememoryl110.dll apimswincorenamedpipel110.dll apimswincoreprocessenvironmentl110.dll apimswincoreprocessthreadsl110.dll apimswincoreprocessthreadsl111.dll apimswincoreprofilel110.dll apimswincorertlsupportl110.dll apimswincorestringl110.dll apimswincoresynchl110.dll apimswincoresynchl120.dll apimswincoresysinfol110.dll apimswincoretimezonel110.dll apimswincoreutill110.dll apimswincrtconiol110.dll apimswincrtconvertl110.dll apimswincrtenvironmentl110.dll apimswincrtfilesysteml110.dll apimswincrtheapl110.dll apimswincrtlocalel110.dll apimswincrtmathl110.dll apimswincrtmultibytel110.dll apimswincrtprivatel110.dll apimswincrtprocessl110.dll apimswincrtruntimel110.dll apimswincrtstdiol110.dll apimswincrtstringl110.dll apimswincrttimel110.dll apimswincrtutilityl110.dll appupdate.yml blink_image_resources_200_percent.pak cld.node d3dcompiler_47.dll ffmpeg.dll index.d.ts libEGL.dll libGLESv2.dll msvcp140.dll node.dll npmignore_1 ucrtbase.dll vcruntime140.dll"/>
<ATTRIBUTE name="CurrentFeature" value="MainFeature"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
<ROW File="CODE_OF_CONDUCT.md" Component_="npmignore_1" FileName="CODE_O~1.MD|CODE_OF_CONDUCT.md" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\CODE_OF_CONDUCT.md" SelfReg="false" NextFile="COPYING"/>
<ROW File="COPYING" Component_="npmignore_1" FileName="COPYING" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\COPYING" SelfReg="false" NextFile="esdoc.json"/>
<ROW File="Jobber.exe" Component_="Jobber.exe" FileName="Jobber.exe" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\vendor\jobber\Jobber.exe" SelfReg="false" DigSign="true"/>
<ROW File="LICENSE.electron.txt" Component_="blink_image_resources_200_percent.pak" FileName="LICENS~1.TXT|LICENSE.electron.txt" Attributes="0" SourcePath="..\..\dist\win-unpacked\LICENSE.electron.txt" SelfReg="false" NextFile="LICENSES.chromium.html"/>
<ROW File="LICENSES.chromium.html" Component_="blink_image_resources_200_percent.pak" FileName="LICENS~1.HTM|LICENSES.chromium.html" Attributes="0" SourcePath="..\..\dist\win-unpacked\LICENSES.chromium.html" SelfReg="false" NextFile="natives_blob.bin"/>
<ROW File="ScreenSnippet.exe" Component_="ScreenSnippet.exe" FileName="SCREEN~1.EXE|ScreenSnippet.exe" Attributes="0" SourcePath="..\..\node_modules\screen-snippet\bin\Release\ScreenSnippet.exe" SelfReg="false" NextFile="SymphonyElectron.exe" DigSign="true"/>
<ROW File="ScreenSnippet.exe" Component_="ScreenSnippet.exe" FileName="SCREEN~1.EXE|ScreenSnippet.exe" Attributes="0" SourcePath="..\..\node_modules\screen-snippet\bin\Release\ScreenSnippet.exe" SelfReg="false" NextFile="apimswincoreconsolel110.dll" DigSign="true"/>
<ROW File="Symphony.config_1" Component_="Symphony.config" FileName="SYMPHO~1.CON|Symphony.config" Attributes="0" SourcePath="..\..\dist\win-unpacked\config\Symphony.config" SelfReg="false" NextFile="ScreenSnippet.exe"/>
<ROW File="Symphony.exe" Component_="Symphony.exe" FileName="Symphony.exe" Attributes="0" SourcePath="Symphony.exe" SelfReg="false" NextFile="ui_resources_200_percent.pak" DigSign="true"/>
<ROW File="SymphonyElectron.exe" Component_="SymphonyElectron.exe" FileName="SYMPHO~1.EXE|Symphony-Electron.exe" Attributes="0" SourcePath="..\..\dist\win-unpacked\Symphony-Electron.exe" SelfReg="false" DigSign="true"/>
<ROW File="Symphony.exe" Component_="Symphony.exe" FileName="Symphony.exe" Attributes="0" SourcePath="..\..\dist\win-unpacked\Symphony.exe" SelfReg="false" NextFile="ui_resources_200_percent.pak" DigSign="true"/>
<ROW File="am.pak" Component_="am.pak" FileName="am.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\am.pak" SelfReg="false" NextFile="ar.pak"/>
<ROW File="ambient.d.ts" Component_="ambient.d.ts" FileName="AMBIEN~1.TS|ambient.d.ts" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\src\ambient.d.ts" SelfReg="false" NextFile="index.ts"/>
<ROW File="apimswincoreconsolel110.dll" Component_="apimswincoreconsolel110.dll" FileName="API-MS~1.DLL|api-ms-win-core-console-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-console-l1-1-0.dll" SelfReg="false" NextFile="apimswincoredatetimel110.dll"/>
<ROW File="apimswincoredatetimel110.dll" Component_="apimswincoredatetimel110.dll" FileName="API-MS~2.DLL|api-ms-win-core-datetime-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-datetime-l1-1-0.dll" SelfReg="false" NextFile="apimswincoredebugl110.dll"/>
<ROW File="apimswincoredebugl110.dll" Component_="apimswincoredebugl110.dll" FileName="API-MS~3.DLL|api-ms-win-core-debug-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-debug-l1-1-0.dll" SelfReg="false" NextFile="apimswincoreerrorhandlingl110.dll"/>
<ROW File="apimswincoreerrorhandlingl110.dll" Component_="apimswincoreerrorhandlingl110.dll" FileName="API-MS~4.DLL|api-ms-win-core-errorhandling-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-errorhandling-l1-1-0.dll" SelfReg="false" NextFile="apimswincorefilel110.dll"/>
<ROW File="apimswincorefilel110.dll" Component_="apimswincorefilel110.dll" FileName="API-MS~5.DLL|api-ms-win-core-file-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-file-l1-1-0.dll" SelfReg="false" NextFile="apimswincorefilel120.dll"/>
<ROW File="apimswincorefilel120.dll" Component_="apimswincorefilel120.dll" FileName="API-MS~6.DLL|api-ms-win-core-file-l1-2-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-file-l1-2-0.dll" SelfReg="false" NextFile="apimswincorefilel210.dll"/>
<ROW File="apimswincorefilel210.dll" Component_="apimswincorefilel210.dll" FileName="API-MS~7.DLL|api-ms-win-core-file-l2-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-file-l2-1-0.dll" SelfReg="false" NextFile="apimswincorehandlel110.dll"/>
<ROW File="apimswincorehandlel110.dll" Component_="apimswincorehandlel110.dll" FileName="API-MS~8.DLL|api-ms-win-core-handle-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-handle-l1-1-0.dll" SelfReg="false" NextFile="apimswincoreheapl110.dll"/>
<ROW File="apimswincoreheapl110.dll" Component_="apimswincoreheapl110.dll" FileName="API-MS~9.DLL|api-ms-win-core-heap-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-heap-l1-1-0.dll" SelfReg="false" NextFile="apimswincoreinterlockedl110.dll"/>
<ROW File="apimswincoreinterlockedl110.dll" Component_="apimswincoreinterlockedl110.dll" FileName="API-M~10.DLL|api-ms-win-core-interlocked-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-interlocked-l1-1-0.dll" SelfReg="false" NextFile="apimswincorelibraryloaderl110.dll"/>
<ROW File="apimswincorelibraryloaderl110.dll" Component_="apimswincorelibraryloaderl110.dll" FileName="API-M~11.DLL|api-ms-win-core-libraryloader-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-libraryloader-l1-1-0.dll" SelfReg="false" NextFile="apimswincorelocalizationl120.dll"/>
<ROW File="apimswincorelocalizationl120.dll" Component_="apimswincorelocalizationl120.dll" FileName="API-M~12.DLL|api-ms-win-core-localization-l1-2-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-localization-l1-2-0.dll" SelfReg="false" NextFile="apimswincorememoryl110.dll"/>
<ROW File="apimswincorememoryl110.dll" Component_="apimswincorememoryl110.dll" FileName="API-M~13.DLL|api-ms-win-core-memory-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-memory-l1-1-0.dll" SelfReg="false" NextFile="apimswincorenamedpipel110.dll"/>
<ROW File="apimswincorenamedpipel110.dll" Component_="apimswincorenamedpipel110.dll" FileName="API-M~14.DLL|api-ms-win-core-namedpipe-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-namedpipe-l1-1-0.dll" SelfReg="false" NextFile="apimswincoreprocessenvironmentl110.dll"/>
<ROW File="apimswincoreprocessenvironmentl110.dll" Component_="apimswincoreprocessenvironmentl110.dll" FileName="API-M~15.DLL|api-ms-win-core-processenvironment-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-processenvironment-l1-1-0.dll" SelfReg="false" NextFile="apimswincoreprocessthreadsl110.dll"/>
<ROW File="apimswincoreprocessthreadsl110.dll" Component_="apimswincoreprocessthreadsl110.dll" FileName="API-M~16.DLL|api-ms-win-core-processthreads-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-processthreads-l1-1-0.dll" SelfReg="false" NextFile="apimswincoreprocessthreadsl111.dll"/>
<ROW File="apimswincoreprocessthreadsl111.dll" Component_="apimswincoreprocessthreadsl111.dll" FileName="API-M~17.DLL|api-ms-win-core-processthreads-l1-1-1.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-processthreads-l1-1-1.dll" SelfReg="false" NextFile="apimswincoreprofilel110.dll"/>
<ROW File="apimswincoreprofilel110.dll" Component_="apimswincoreprofilel110.dll" FileName="API-M~18.DLL|api-ms-win-core-profile-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-profile-l1-1-0.dll" SelfReg="false" NextFile="apimswincorertlsupportl110.dll"/>
<ROW File="apimswincorertlsupportl110.dll" Component_="apimswincorertlsupportl110.dll" FileName="API-M~19.DLL|api-ms-win-core-rtlsupport-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-rtlsupport-l1-1-0.dll" SelfReg="false" NextFile="apimswincorestringl110.dll"/>
<ROW File="apimswincorestringl110.dll" Component_="apimswincorestringl110.dll" FileName="API-M~20.DLL|api-ms-win-core-string-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-string-l1-1-0.dll" SelfReg="false" NextFile="apimswincoresynchl110.dll"/>
<ROW File="apimswincoresynchl110.dll" Component_="apimswincoresynchl110.dll" FileName="API-M~21.DLL|api-ms-win-core-synch-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-synch-l1-1-0.dll" SelfReg="false" NextFile="apimswincoresynchl120.dll"/>
<ROW File="apimswincoresynchl120.dll" Component_="apimswincoresynchl120.dll" FileName="API-M~22.DLL|api-ms-win-core-synch-l1-2-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-synch-l1-2-0.dll" SelfReg="false" NextFile="apimswincoresysinfol110.dll"/>
<ROW File="apimswincoresysinfol110.dll" Component_="apimswincoresysinfol110.dll" FileName="API-M~23.DLL|api-ms-win-core-sysinfo-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-sysinfo-l1-1-0.dll" SelfReg="false" NextFile="apimswincoretimezonel110.dll"/>
<ROW File="apimswincoretimezonel110.dll" Component_="apimswincoretimezonel110.dll" FileName="API-M~24.DLL|api-ms-win-core-timezone-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-timezone-l1-1-0.dll" SelfReg="false" NextFile="apimswincoreutill110.dll"/>
<ROW File="apimswincoreutill110.dll" Component_="apimswincoreutill110.dll" FileName="API-M~25.DLL|api-ms-win-core-util-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-core-util-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtconiol110.dll"/>
<ROW File="apimswincrtconiol110.dll" Component_="apimswincrtconiol110.dll" FileName="API-M~26.DLL|api-ms-win-crt-conio-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-conio-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtconvertl110.dll"/>
<ROW File="apimswincrtconvertl110.dll" Component_="apimswincrtconvertl110.dll" FileName="API-M~27.DLL|api-ms-win-crt-convert-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-convert-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtenvironmentl110.dll"/>
<ROW File="apimswincrtenvironmentl110.dll" Component_="apimswincrtenvironmentl110.dll" FileName="API-M~28.DLL|api-ms-win-crt-environment-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-environment-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtfilesysteml110.dll"/>
<ROW File="apimswincrtfilesysteml110.dll" Component_="apimswincrtfilesysteml110.dll" FileName="API-M~29.DLL|api-ms-win-crt-filesystem-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-filesystem-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtheapl110.dll"/>
<ROW File="apimswincrtheapl110.dll" Component_="apimswincrtheapl110.dll" FileName="API-M~30.DLL|api-ms-win-crt-heap-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-heap-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtlocalel110.dll"/>
<ROW File="apimswincrtlocalel110.dll" Component_="apimswincrtlocalel110.dll" FileName="API-M~31.DLL|api-ms-win-crt-locale-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-locale-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtmathl110.dll"/>
<ROW File="apimswincrtmathl110.dll" Component_="apimswincrtmathl110.dll" FileName="API-M~32.DLL|api-ms-win-crt-math-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-math-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtmultibytel110.dll"/>
<ROW File="apimswincrtmultibytel110.dll" Component_="apimswincrtmultibytel110.dll" FileName="API-M~33.DLL|api-ms-win-crt-multibyte-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-multibyte-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtprivatel110.dll"/>
<ROW File="apimswincrtprivatel110.dll" Component_="apimswincrtprivatel110.dll" FileName="API-M~34.DLL|api-ms-win-crt-private-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-private-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtprocessl110.dll"/>
<ROW File="apimswincrtprocessl110.dll" Component_="apimswincrtprocessl110.dll" FileName="API-M~35.DLL|api-ms-win-crt-process-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-process-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtruntimel110.dll"/>
<ROW File="apimswincrtruntimel110.dll" Component_="apimswincrtruntimel110.dll" FileName="API-M~36.DLL|api-ms-win-crt-runtime-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-runtime-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtstdiol110.dll"/>
<ROW File="apimswincrtstdiol110.dll" Component_="apimswincrtstdiol110.dll" FileName="API-M~37.DLL|api-ms-win-crt-stdio-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-stdio-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtstringl110.dll"/>
<ROW File="apimswincrtstringl110.dll" Component_="apimswincrtstringl110.dll" FileName="API-M~38.DLL|api-ms-win-crt-string-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-string-l1-1-0.dll" SelfReg="false" NextFile="apimswincrttimel110.dll"/>
<ROW File="apimswincrttimel110.dll" Component_="apimswincrttimel110.dll" FileName="API-M~39.DLL|api-ms-win-crt-time-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-time-l1-1-0.dll" SelfReg="false" NextFile="apimswincrtutilityl110.dll"/>
<ROW File="apimswincrtutilityl110.dll" Component_="apimswincrtutilityl110.dll" FileName="API-M~40.DLL|api-ms-win-crt-utility-l1-1-0.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\api-ms-win-crt-utility-l1-1-0.dll" SelfReg="false" NextFile="msvcp140.dll"/>
<ROW File="app.asar" Component_="appupdate.yml" FileName="APP~1.ASA|app.asar" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar" SelfReg="false" NextFile="electron.asar"/>
<ROW File="appupdate.yml" Component_="appupdate.yml" FileName="APP-UP~1.YML|app-update.yml" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app-update.yml" SelfReg="false" NextFile="app.asar"/>
<ROW File="ar.pak" Component_="am.pak" FileName="ar.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\ar.pak" SelfReg="false" NextFile="bg.pak"/>
<ROW File="bg.pak" Component_="am.pak" FileName="bg.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\bg.pak" SelfReg="false" NextFile="bn.pak"/>
<ROW File="blink_image_resources_200_percent.pak" Component_="blink_image_resources_200_percent.pak" FileName="BLINK_~1.PAK|blink_image_resources_200_percent.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\blink_image_resources_200_percent.pak" SelfReg="false" NextFile="content_resources_200_percent.pak"/>
<ROW File="bn.pak" Component_="am.pak" FileName="bn.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\bn.pak" SelfReg="false" NextFile="ca.pak"/>
<ROW File="build.cmd" Component_="npmignore_1" FileName="build.cmd" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\build.cmd" SelfReg="false" NextFile="build.sh"/>
<ROW File="build.sh" Component_="npmignore_1" FileName="build.sh" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\build.sh" SelfReg="false" NextFile="CODE_OF_CONDUCT.md"/>
<ROW File="ca.pak" Component_="am.pak" FileName="ca.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\ca.pak" SelfReg="false" NextFile="cs.pak"/>
<ROW File="cld.node" Component_="cld.node" FileName="CLD~1.NOD|cld.node" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\@paulcbetts\cld\build\Release\cld.node" SelfReg="false" NextFile="npmignore_1"/>
<ROW File="content_resources_200_percent.pak" Component_="blink_image_resources_200_percent.pak" FileName="CONTEN~1.PAK|content_resources_200_percent.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\content_resources_200_percent.pak" SelfReg="false" NextFile="content_shell.pak"/>
<ROW File="content_shell.pak" Component_="blink_image_resources_200_percent.pak" FileName="CONTEN~2.PAK|content_shell.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\content_shell.pak" SelfReg="false" NextFile="d3dcompiler_47.dll"/>
<ROW File="cs.pak" Component_="am.pak" FileName="cs.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\cs.pak" SelfReg="false" NextFile="da.pak"/>
@ -99,6 +204,7 @@
<ROW File="enUS.pak" Component_="am.pak" FileName="en-US.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\en-US.pak" SelfReg="false" NextFile="es.pak"/>
<ROW File="es.pak" Component_="am.pak" FileName="es.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\es.pak" SelfReg="false" NextFile="es419.pak"/>
<ROW File="es419.pak" Component_="am.pak" FileName="es-419.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\es-419.pak" SelfReg="false" NextFile="et.pak"/>
<ROW File="esdoc.json" Component_="npmignore_1" FileName="ESDOC~1.JSO|esdoc.json" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\esdoc.json" SelfReg="false" NextFile="index.d.ts"/>
<ROW File="et.pak" Component_="am.pak" FileName="et.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\et.pak" SelfReg="false" NextFile="fa.pak"/>
<ROW File="fa.pak" Component_="am.pak" FileName="fa.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\fa.pak" SelfReg="false" NextFile="fakebidi.pak"/>
<ROW File="fakebidi.pak" Component_="am.pak" FileName="FAKE-B~1.PAK|fake-bidi.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\fake-bidi.pak" SelfReg="false" NextFile="fi.pak"/>
@ -113,6 +219,10 @@
<ROW File="hu.pak" Component_="am.pak" FileName="hu.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\hu.pak" SelfReg="false" NextFile="id.pak"/>
<ROW File="icudtl.dat" Component_="blink_image_resources_200_percent.pak" FileName="icudtl.dat" Attributes="0" SourcePath="..\..\dist\win-unpacked\icudtl.dat" SelfReg="false" NextFile="libEGL.dll"/>
<ROW File="id.pak" Component_="am.pak" FileName="id.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\id.pak" SelfReg="false" NextFile="it.pak"/>
<ROW File="index.d.ts" Component_="index.d.ts" FileName="INDEXD~1.TS|index.d.ts" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\lib\src\index.d.ts" SelfReg="false" NextFile="index.js"/>
<ROW File="index.js" Component_="index.d.ts" FileName="index.js" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\lib\src\index.js" SelfReg="false" NextFile="index.js.map"/>
<ROW File="index.js.map" Component_="index.d.ts" FileName="INDEXJ~1.MAP|index.js.map" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\lib\src\index.js.map" SelfReg="false" NextFile="package.json_1"/>
<ROW File="index.ts" Component_="ambient.d.ts" FileName="index.ts" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\src\index.ts" SelfReg="false" NextFile="tsconfig.json"/>
<ROW File="it.pak" Component_="am.pak" FileName="it.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\it.pak" SelfReg="false" NextFile="ja.pak"/>
<ROW File="ja.pak" Component_="am.pak" FileName="ja.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\ja.pak" SelfReg="false" NextFile="kn.pak"/>
<ROW File="kn.pak" Component_="am.pak" FileName="kn.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\kn.pak" SelfReg="false" NextFile="ko.pak"/>
@ -124,10 +234,13 @@
<ROW File="ml.pak" Component_="am.pak" FileName="ml.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\ml.pak" SelfReg="false" NextFile="mr.pak"/>
<ROW File="mr.pak" Component_="am.pak" FileName="mr.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\mr.pak" SelfReg="false" NextFile="ms.pak"/>
<ROW File="ms.pak" Component_="am.pak" FileName="ms.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\ms.pak" SelfReg="false" NextFile="nb.pak"/>
<ROW File="msvcp140.dll" Component_="msvcp140.dll" FileName="msvcp140.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\msvcp140.dll" SelfReg="false" NextFile="ucrtbase.dll"/>
<ROW File="natives_blob.bin" Component_="blink_image_resources_200_percent.pak" FileName="NATIVE~1.BIN|natives_blob.bin" Attributes="0" SourcePath="..\..\dist\win-unpacked\natives_blob.bin" SelfReg="false" NextFile="node.dll"/>
<ROW File="nb.pak" Component_="am.pak" FileName="nb.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\nb.pak" SelfReg="false" NextFile="nl.pak"/>
<ROW File="nl.pak" Component_="am.pak" FileName="nl.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\nl.pak" SelfReg="false" NextFile="pl.pak"/>
<ROW File="node.dll" Component_="node.dll" FileName="node.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\node.dll" SelfReg="false" NextFile="snapshot_blob.bin"/>
<ROW File="npmignore_1" Component_="npmignore_1" FileName="NPMIGN~1|.npmignore" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\.npmignore" SelfReg="false" NextFile="build.cmd"/>
<ROW File="package.json_1" Component_="npmignore_1" FileName="PACKAG~1.JSO|package.json" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\package.json" SelfReg="false" NextFile="ambient.d.ts"/>
<ROW File="pl.pak" Component_="am.pak" FileName="pl.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\pl.pak" SelfReg="false" NextFile="ptBR.pak"/>
<ROW File="ptBR.pak" Component_="am.pak" FileName="pt-BR.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\pt-BR.pak" SelfReg="false" NextFile="ptPT.pak"/>
<ROW File="ptPT.pak" Component_="am.pak" FileName="pt-PT.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\pt-PT.pak" SelfReg="false" NextFile="ro.pak"/>
@ -143,8 +256,12 @@
<ROW File="te.pak" Component_="am.pak" FileName="te.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\te.pak" SelfReg="false" NextFile="th.pak"/>
<ROW File="th.pak" Component_="am.pak" FileName="th.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\th.pak" SelfReg="false" NextFile="tr.pak"/>
<ROW File="tr.pak" Component_="am.pak" FileName="tr.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\tr.pak" SelfReg="false" NextFile="uk.pak"/>
<ROW File="tsconfig.json" Component_="npmignore_1" FileName="TSCONF~1.JSO|tsconfig.json" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\tsconfig.json" SelfReg="false" NextFile="tslint.json"/>
<ROW File="tslint.json" Component_="npmignore_1" FileName="TSLINT~1.JSO|tslint.json" Attributes="0" SourcePath="..\..\dist\win-unpacked\resources\app.asar.unpacked\node_modules\spawn-rx\tslint.json" SelfReg="false" NextFile="Jobber.exe"/>
<ROW File="ucrtbase.dll" Component_="ucrtbase.dll" FileName="ucrtbase.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\ucrtbase.dll" SelfReg="false" NextFile="vcruntime140.dll"/>
<ROW File="ui_resources_200_percent.pak" Component_="blink_image_resources_200_percent.pak" FileName="UI_RES~1.PAK|ui_resources_200_percent.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\ui_resources_200_percent.pak" SelfReg="false" NextFile="views_resources_200_percent.pak"/>
<ROW File="uk.pak" Component_="am.pak" FileName="uk.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\uk.pak" SelfReg="false" NextFile="vi.pak"/>
<ROW File="vcruntime140.dll" Component_="vcruntime140.dll" FileName="VCRUNT~1.DLL|vcruntime140.dll" Attributes="0" SourcePath="..\..\dist\win-unpacked\vcruntime140.dll" SelfReg="false" NextFile="cld.node"/>
<ROW File="vi.pak" Component_="am.pak" FileName="vi.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\vi.pak" SelfReg="false" NextFile="zhCN.pak"/>
<ROW File="views_resources_200_percent.pak" Component_="blink_image_resources_200_percent.pak" FileName="VIEWS_~1.PAK|views_resources_200_percent.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\views_resources_200_percent.pak" SelfReg="false" NextFile="am.pak"/>
<ROW File="zhCN.pak" Component_="am.pak" FileName="zh-CN.pak" Attributes="0" SourcePath="..\..\dist\win-unpacked\locales\zh-CN.pak" SelfReg="false" NextFile="zhTW.pak"/>
@ -362,7 +479,8 @@
<ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[AI_UserProgramFiles][Manufacturer]\[ProductName]"/>
<ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]"/>
<ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
<ROW Action="Symphony.exe" Type="1042" Source="SymphonyElectron.exe" Target="--install"/>
<ROW Action="Symphony.exe" Type="1042" Source="Symphony.exe" Target="--install --peruser"/>
<ROW Action="Symphony.exe_All_User" Type="1042" Source="Symphony.exe" Target="--install"/>
<ROW Action="UninstallPreviousVersions" Type="1" Source="aicustact.dll" Target="UninstallPreviousVersions" Options="1"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
@ -380,7 +498,8 @@
<ROW Action="AI_DATA_SETTER" Sequence="54"/>
<ROW Action="AI_SETMIXINSTLOCATION" Sequence="749"/>
<ROW Action="AI_TxtUpdaterInstall" Sequence="5101"/>
<ROW Action="Symphony.exe" Condition="( NOT Installed )" Sequence="5935"/>
<ROW Action="Symphony.exe" Condition="( NOT Installed ) AND ( MSIINSTALLPERUSER )" Sequence="5935"/>
<ROW Action="Symphony.exe_All_User" Condition="( NOT Installed ) AND ( ALLUSERS )" Sequence="5936"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
<ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
@ -443,4 +562,4 @@
<COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateSetComponent">
<ROW Key="Symphony.config" Component="am.pak" FileName="Symphony.config" Directory="config_Dir" Options="17"/>
</COMPONENT>
</DOCUMENT>
</DOCUMENT>

View File

@ -16,7 +16,7 @@ let throttleActivity;
function activityDetection() {
// Get system idle status and idle time from PaulCBetts package
if (systemIdleTime.getIdleTime() < maxIdleTime) {
return {isUserIdle: false, systemIdleTime: systemIdleTime.getIdleTime()};
return { isUserIdle: false, systemIdleTime: systemIdleTime.getIdleTime() };
}
// If idle for more than 4 mins, monitor system idle status every second
@ -65,7 +65,7 @@ function monitorUserActivity() {
function sendActivity() {
let systemActivity = activityDetection();
if (systemActivity && !systemActivity.isUserIdle && systemActivity.systemIdleTime) {
send({systemIdleTime: systemActivity.systemIdleTime});
send({ systemIdleTime: systemActivity.systemIdleTime });
}
}
@ -84,6 +84,11 @@ function send(data) {
}
}
/**
* Set the activity's window
* @param period
* @param win
*/
function setActivityWindow(period, win) {
maxIdleTime = period;
activityWindow = win;
@ -95,6 +100,5 @@ module.exports = {
send: send,
setActivityWindow: setActivityWindow,
activityDetection: activityDetection,
monitorUserActivity: monitorUserActivity, // Exporting this for unit test
initiateActivityDetection: initiateActivityDetection
};
monitorUserActivity: monitorUserActivity, // Exporting this for unit tests
};

View File

@ -10,6 +10,10 @@ 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);
@ -37,6 +41,11 @@ function show(count) {
}
}
/**
* Sets the data url
* @param dataUrl
* @param count
*/
function setDataUrl(dataUrl, count) {
const mainWindow = windowMgr.getMainWindow();
if (mainWindow && dataUrl && count) {
@ -50,4 +59,4 @@ function setDataUrl(dataUrl, count) {
module.exports = {
show: show,
setDataUrl: setDataUrl
}
};

View File

@ -4,16 +4,25 @@ const electron = require('electron');
const app = electron.app;
const path = require('path');
const fs = require('fs');
const AppDirectory = require('appdirectory');
const omit = require('lodash.omit');
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 configFileName = 'Symphony.config';
const dirs = new AppDirectory('Symphony');
// 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'];
/**
* 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:
@ -29,17 +38,22 @@ let globalConfig;
*/
function getConfigField(fieldName) {
return getUserConfigField(fieldName)
.then(function(value) {
// got value from user config
return value;
}, function () {
// failed to get value from user config, so try global config
return getGlobalConfigField(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(function(config) {
return readUserConfig().then((config) => {
if (typeof fieldName === 'string' && fieldName in config) {
return config[fieldName];
}
@ -48,16 +62,25 @@ function getUserConfigField(fieldName) {
});
}
function readUserConfig() {
return new Promise(function(resolve, reject) {
/**
* 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 = path.join(app.getPath('userData'), configFileName);
let configPath = customConfigPath;
fs.readFile(configPath, 'utf8', function(err, data) {
if (!configPath) {
configPath = path.join(app.getPath('userData'), configFileName);
}
fs.readFile(configPath, 'utf8', (err, data) => {
if (err) {
reject('cannot open user config file: ' + configPath + ', error: ' + err);
} else {
@ -74,8 +97,13 @@ function readUserConfig() {
});
}
/**
* Gets a specific user config value for a field
* @param fieldName
* @returns {Promise}
*/
function getGlobalConfigField(fieldName) {
return readGlobalConfig().then(function(config) {
return readGlobalConfig().then((config) => {
if (typeof fieldName === 'string' && fieldName in config) {
return config[fieldName];
}
@ -93,7 +121,7 @@ function getGlobalConfigField(fieldName) {
* installed app). for dev env, the file is read directly from packed asar file.
*/
function readGlobalConfig() {
return new Promise(function(resolve, reject) {
return new Promise((resolve, reject) => {
if (globalConfig) {
resolve(globalConfig);
return;
@ -113,7 +141,7 @@ function readGlobalConfig() {
configPath = path.join(execPath, isMac ? '..' : '', globalConfigFileName);
}
fs.readFile(configPath, 'utf8', function(err, data) {
fs.readFile(configPath, 'utf8', (err, data) => {
if (err) {
reject('cannot open global config file: ' + configPath + ', error: ' + err);
} else {
@ -124,12 +152,12 @@ function readGlobalConfig() {
reject('can not parse config file data: ' + data + ', error: ' + err);
}
getRegistry('PodUrl')
.then(function(url) {
globalConfig.url = url;
resolve(globalConfig);
}).catch(function () {
resolve(globalConfig);
});
.then((url) => {
globalConfig.url = url;
resolve(globalConfig);
}).catch(() => {
resolve(globalConfig);
});
}
});
});
@ -143,21 +171,27 @@ function readGlobalConfig() {
*/
function updateConfigField(fieldName, newValue) {
return readUserConfig()
.then(function(config) {
return saveUserConfig(fieldName, newValue, config);
},
function() {
// in case config doesn't exist, can't read or is corrupted.
// add configVersion - just in case in future we need to provide
// upgrade capabilities.
return saveUserConfig(fieldName, newValue, {
configVersion: '1.0.0'
.then((config) => {
return saveUserConfig(fieldName, newValue, config);
}, () => {
// in case config doesn't exist, can't read or is corrupted.
// add configVersion - just in case in future we need to provide
// upgrade capabilities.
return saveUserConfig(fieldName, newValue, {
configVersion: '1.0.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(function(resolve, reject) {
return new Promise((resolve, reject) => {
let configPath = path.join(app.getPath('userData'), configFileName);
if (!oldConfig || !fieldName) {
@ -182,17 +216,124 @@ function saveUserConfig(fieldName, newValue, oldConfig) {
});
}
/**
* 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
let newUserConfig = omit(oldUserConfig, ignoreSettings);
let newUserConfigString = JSON.stringify(newUserConfig, null, 2);
// get the user config path
let userConfigFile;
if (isMac) {
userConfigFile = path.join(dirs.userConfig(), configFileName);
} else {
userConfigFile = path.join(app.getPath('userData'), configFileName);
}
if (!userConfigFile) {
return reject('user config file doesn\'t exist');
}
// write the new user config changes to the user config file
fs.writeFileSync(userConfigFile, newUserConfigString, 'utf-8');
return resolve();
});
}
/**
* Manipulates user config on windows
* @param {String} perUserInstall - Is a flag to determine if we are installing for an individual user
* @returns {Promise}
*/
function updateUserConfigWin(perUserInstall) {
return new Promise((resolve, reject) => {
// we get the user config path using electron
const userConfigFile = path.join(app.getPath('userData'), configFileName);
// if it's not a per user installation or if the
// user config file doesn't exist, we simple move on
if (!perUserInstall || !fs.existsSync(userConfigFile)) {
log.send(logLevels.WARN, 'config: Could not find the user config file!');
reject();
return;
}
// 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
readUserConfig(userConfigFile).then((data) => {
resolve(updateUserConfig(data));
}).catch((err) => {
reject(err);
});
});
}
/**
* Manipulates user config on macOS
* @param {String} globalConfigPath - The global config path from installer
* @returns {Promise}
*/
function updateUserConfigMac() {
return new Promise((resolve, reject) => {
const userConfigFile = path.join(dirs.userConfig(), configFileName);
// if user config file does't exist, just use the global config settings
// i.e. until an user makes changes manually using the menu items
if (!fs.existsSync(userConfigFile)) {
log.send(logLevels.WARN, 'config: Could not find the user config file!');
reject();
return;
}
// 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
readUserConfig(userConfigFile).then((data) => {
resolve(updateUserConfig(data));
}).catch((err) => {
reject(err);
});
});
}
/**
* Clears the cached config
*/
function clearCachedConfigs() {
userConfig = null;
globalConfig = null;
}
module.exports = {
getConfigField,
updateConfigField,
configFileName,
getConfigField,
updateConfigField,
updateUserConfigWin,
updateUserConfigMac,
// items below here are only exported for testing, do NOT use!
saveUserConfig,
clearCachedConfigs
};

View File

@ -12,23 +12,32 @@
// renderer process, this will have to do. See github issue posted here to
// electron: https://github.com/electron/electron/issues/9312
var { ipcRenderer } = require('electron');
const { ipcRenderer } = require('electron');
var nextId = 0;
var includes = [].includes;
let nextId = 0;
let includes = [].includes;
function getNextId() {
return ++nextId;
}
// |options.type| can not be empty and has to include 'window' or 'screen'.
/**
* 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);
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) {
var captureScreen, captureWindow, id;
let captureScreen, captureWindow, id;
if (!isValid(options)) {
return callback(new Error('Invalid options'));
}
@ -36,33 +45,34 @@ function getSources(options, callback) {
captureScreen = includes.call(options.types, 'screen');
let updatedOptions = options;
if (updatedOptions.thumbnailSize == null) {
if (!updatedOptions.thumbnailSize) {
updatedOptions.thumbnailSize = {
width: 150,
height: 150
}
};
}
id = getNextId();
ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, updatedOptions.thumbnailSize, id);
return ipcRenderer.once('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + id, function (event, sources) {
var source;
callback(null, (function () {
var i, len, results
return ipcRenderer.once('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + id, function(event, sources) {
let source;
callback(null, (function() {
let i, len, results;
results = [];
for (i = 0, len = sources.length; i < len; i++) {
source = sources[i]
source = sources[i];
results.push({
id: source.id,
name: source.name,
thumbnail: source.thumbnail
})
});
}
return results
return results;
}()));
});
}
module.exports = getSources;
module.exports = getSources;

View File

@ -11,14 +11,14 @@ const logLevels = require('../enums/logLevels.js');
* @param {String} url Url that failed
* @param {String} errorDesc Description of error
* @param {Number} errorCode Error code
* @param {callback} retryCallback Callback when user clicks reload
* @param {function} retryCallback Callback when user clicks reload
*/
function showLoadFailure(win, url, errorDesc, errorCode, retryCallback) {
let msg;
if (url) {
msg = 'Error loading URL:\n' + url;
} else {
msg = 'Error loading window'
msg = 'Error loading window';
}
if (errorDesc) {
msg += '\n\n' + errorDesc;
@ -29,7 +29,7 @@ function showLoadFailure(win, url, errorDesc, errorCode, retryCallback) {
electron.dialog.showMessageBox(win, {
type: 'error',
buttons: [ 'Reload', 'Ignore' ],
buttons: ['Reload', 'Ignore'],
defaultId: 0,
cancelId: 1,
noLink: true,
@ -38,7 +38,7 @@ function showLoadFailure(win, url, errorDesc, errorCode, retryCallback) {
}, response);
log.send(logLevels.WARNING, 'Load failure msg: ' + errorDesc +
' errorCode: ' + errorCode + ' for url:' + url);
' errorCode: ' + errorCode + ' for url:' + url);
// async handle of user input
function response(buttonId) {
@ -53,11 +53,11 @@ function showLoadFailure(win, url, errorDesc, errorCode, retryCallback) {
* Show message indicating network connectivity has been lost.
* @param {BrowserWindow} win Window to host dialog
* @param {String} url Url that failed
* @param {callback} retryCallback Callback when user clicks reload
* @param {function} retryCallback Callback when user clicks reload
*/
function showNetworkConnectivityError(win, url, retryCallback) {
var errorDesc = 'Network connectivity has been lost, check your internet connection.';
let errorDesc = 'Network connectivity has been lost, check your internet connection.';
showLoadFailure(win, url, errorDesc, 0, retryCallback);
}
module.exports = { showLoadFailure, showNetworkConnectivityError };
module.exports = { showLoadFailure, showNetworkConnectivityError };

View File

@ -19,12 +19,13 @@ local.ipcRenderer.on('downloadProgress', () => {
/**
* Open file in default app.
* @param id
*/
function openFile(id) {
let fileIndex = local.downloadItems.findIndex((item) => {
return item._id === id
return item._id === id;
});
if (fileIndex !== -1){
if (fileIndex !== -1) {
let openResponse = remote.shell.openExternal(`file:///${local.downloadItems[fileIndex].savedPath}`);
if (!openResponse) {
remote.dialog.showErrorBox("File not found", 'The file you are trying to open cannot be found in the specified path.');
@ -34,10 +35,11 @@ function openFile(id) {
/**
* Show downloaded file in explorer or finder.
* @param id
*/
function showInFinder(id) {
let showFileIndex = local.downloadItems.findIndex((item) => {
return item._id === id
return item._id === id;
});
if (showFileIndex !== -1) {
let showResponse = remote.shell.showItemInFolder(local.downloadItems[showFileIndex].savedPath);
@ -47,6 +49,10 @@ function showInFinder(id) {
}
}
/**
* Create the document object model
* @param arg
*/
function createDOM(arg) {
if (arg && arg._id) {
@ -149,6 +155,9 @@ function createDOM(arg) {
}
}
/**
* Initiate the download manager
*/
function initiate() {
let mainFooter = document.getElementById('footer');
let mainDownloadDiv = document.getElementById('download-manager-footer');
@ -159,7 +168,7 @@ function initiate() {
let ulFind = document.getElementById('download-main');
if (!ulFind){
if (!ulFind) {
let uList = document.createElement('ul');
uList.id = 'download-main';
mainDownloadDiv.appendChild(uList);
@ -167,7 +176,7 @@ function initiate() {
let closeSpanFind = document.getElementById('close-download-bar');
if (!closeSpanFind){
if (!closeSpanFind) {
let closeSpan = document.createElement('span');
closeSpan.id = 'close-download-bar';
closeSpan.classList.add('close-download-bar');

View File

@ -1,7 +1,11 @@
'use strict';
var keyMirror = require('keymirror');
let keyMirror = require('keymirror');
/**
* Set of APIs exposed to the remote object
* @type {Object}
*/
const cmds = keyMirror({
isOnline: null,
registerLogger: null,

View File

@ -1,7 +1,11 @@
'use strict';
var keyMirror = require('keymirror');
let keyMirror = require('keymirror');
/**
* The different log levels
* @type {Object}
*/
module.exports = keyMirror({
ERROR: null,
CONFLICT: null,

View File

@ -1,16 +1,26 @@
'use strict';
const getCmdLineArg = require('./utils/getCmdLineArg.js')
const getCmdLineArg = require('./utils/getCmdLineArg.js');
const { isDevEnv } = require('./utils/misc');
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 (isDevEnv) {
initializeLocalLogger();
}
}
/**
@ -25,6 +35,10 @@ class Logger {
return;
}
if (isDevEnv) {
logLocally(level, details);
}
let logMsg = {
level: level,
details: details,
@ -45,18 +59,22 @@ class Logger {
}
}
/**
* Sets a window instance for the remote object
* @param win
*/
setLogWindow(win) {
this.logWindow = win;
if (this.logWindow) {
var logMsg = {};
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=');
let logLevel = getCmdLineArg(process.argv, '--logLevel=', false);
if (logLevel) {
let level = logLevel.split('=')[1];
if (level) {
@ -64,7 +82,7 @@ class Logger {
}
}
if (getCmdLineArg(process.argv, '--enableConsoleLogging')) {
if (getCmdLineArg(process.argv, '--enableConsoleLogging', false)) {
logMsg.showInConsole = true;
}
@ -77,11 +95,40 @@ class Logger {
}
}
var loggerInstance = new Logger();
let loggerInstance = new Logger();
/**
* Initializes the electron logger for local logging
*/
function initializeLocalLogger() {
// eslint-disable-next-line global-require
electronLog = require('electron-log');
electronLog.transports.file.level = 'debug';
electronLog.transports.file.format = '{h}:{i}:{s}:{ms} {text}';
electronLog.transports.file.maxSize = 10 * 1024 * 1024;
electronLog.transports.file.appName = 'Symphony';
}
/**
* 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)
}
};

View File

@ -1,12 +1,15 @@
'use strict';
// Third Party Dependencies
const electron = require('electron');
const app = electron.app;
const nodeURL = require('url');
const squirrelStartup = require('electron-squirrel-startup');
const AutoLaunch = require('auto-launch');
const urlParser = require('url');
const { getConfigField } = require('./config.js');
// Local Dependencies
const {getConfigField, updateUserConfigWin, updateUserConfigMac} = require('./config.js');
const { isMac, isDevEnv } = require('./utils/misc.js');
const protocolHandler = require('./protocolHandler');
const getCmdLineArg = require('./utils/getCmdLineArg.js');
@ -48,10 +51,22 @@ if (!isDevEnv && shouldQuit) {
app.quit();
}
var symphonyAutoLauncher = new AutoLaunch({
name: 'Symphony',
path: process.execPath,
});
let symphonyAutoLauncher;
if (isMac) {
symphonyAutoLauncher = new AutoLaunch({
name: 'Symphony',
mac: {
useLaunchAgent: true,
},
path: process.execPath,
});
} else {
symphonyAutoLauncher = new AutoLaunch({
name: 'Symphony',
path: process.execPath,
});
}
/**
* This method will be called when Electron has finished
@ -60,11 +75,18 @@ var symphonyAutoLauncher = new AutoLaunch({
*/
app.on('ready', setupThenOpenMainWindow);
app.on('window-all-closed', function () {
/**
* Is triggered when all the windows are closed
* In which case we quit the app
*/
app.on('window-all-closed', function() {
app.quit();
});
app.on('activate', function () {
/**
* Is triggered when the app is up & running
*/
app.on('activate', function() {
if (windowMgr.isMainWindow(null)) {
setupThenOpenMainWindow();
} else {
@ -77,52 +99,88 @@ app.on('activate', function () {
// and registry keys in windows
app.setAsDefaultProtocolClient('symphony');
// 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) {
/**
* 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) {
handleProtocolAction(url);
});
/**
* 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;
// allows installer to launch app and set auto startup mode then
// immediately quit.
// allows installer to launch app and set appropriate global / user config params.
let hasInstallFlag = getCmdLineArg(process.argv, '--install', true);
let perUserInstall = getCmdLineArg(process.argv, '--peruser', true);
if (!isMac && hasInstallFlag) {
getConfigField('launchOnStartup')
.then(setStartup)
.then(app.quit)
.catch(app.quit);
.then(setStartup)
.then(() => updateUserConfigWin(perUserInstall))
.then(app.quit)
.catch(app.quit);
return;
}
// allows mac installer to overwrite user config
if (isMac && hasInstallFlag) {
// This value is being sent from post install script
// as the app is launched as a root user we don't get
// access to the config file
let launchOnStartup = process.argv[3];
// We wire this in via the post install script
// to get the config file path where the app is installed
let appGlobalConfigPath = process.argv[2];
setStartup(launchOnStartup)
.then(() => updateUserConfigMac(appGlobalConfigPath))
.then(app.quit)
.catch(app.quit);
return;
}
getUrlAndCreateMainWindow();
// Event that fixes the remote desktop issue in Windows
// by repositioning the browser window
electron.screen.on('display-removed', windowMgr.verifyDisplays);
}
function setStartup(lStartup){
/**
* Sets Symphony on startup
* @param lStartup
* @returns {Promise}
*/
function setStartup(lStartup) {
return symphonyAutoLauncher.isEnabled()
.then(function(isEnabled){
if (!isEnabled && lStartup) {
return symphonyAutoLauncher.enable();
}
.then(function(isEnabled) {
if (!isEnabled && lStartup) {
return symphonyAutoLauncher.enable();
}
if (isEnabled && !lStartup) {
return symphonyAutoLauncher.disable();
}
if (isEnabled && !lStartup) {
return symphonyAutoLauncher.disable();
}
return true;
});
return true;
});
}
/**
* Checks for the url argument, processes it
* and creates the main window
*/
function getUrlAndCreateMainWindow() {
// for dev env allow passing url argument
if (isDevEnv) {
let url = getCmdLineArg(process.argv, '--url=')
let url = getCmdLineArg(process.argv, '--url=', false);
if (url) {
windowMgr.createMainWindow(url.substr(6));
return;
@ -130,12 +188,16 @@ function getUrlAndCreateMainWindow() {
}
getConfigField('url')
.then(createWin).catch(function (err) {
.then(createWin).catch(function(err) {
let title = 'Error loading configuration';
electron.dialog.showErrorBox(title, title + ': ' + err);
});
}
/**
* Creates a window
* @param urlFromConfig
*/
function createWin(urlFromConfig) {
let protocol = '';
// add https protocol if none found.
@ -143,7 +205,7 @@ function createWin(urlFromConfig) {
if (!parsedUrl.protocol) {
protocol = 'https';
}
var url = nodeURL.format({
let url = nodeURL.format({
protocol: protocol,
slahes: true,
pathname: parsedUrl.href
@ -179,7 +241,7 @@ function processProtocolAction(argv) {
return;
}
let protocolUri = getCmdLineArg(argv, 'symphony://');
let protocolUri = getCmdLineArg(argv, 'symphony://', false);
if (protocolUri) {
@ -194,6 +256,10 @@ function processProtocolAction(argv) {
}
}
/**
* Handles a protocol action based on the current state of the app
* @param uri
*/
function handleProtocolAction(uri) {
if (!isAppAlreadyOpen) {
// app is opened by the protocol url, cache the protocol url to be used later

View File

@ -9,7 +9,7 @@ const electron = require('electron');
const windowMgr = require('./windowMgr.js');
const log = require('./log.js');
const logLevels = require('./enums/logLevels');
const activityDetection = require('./activityDetection/activityDetection');
const activityDetection = require('./activityDetection');
const badgeCount = require('./badgeCount.js');
const protocolHandler = require('./protocolHandler');
const configureNotification = require('./notify/settings/configure-notification-position');
@ -30,7 +30,7 @@ function isValidWindow(event) {
if (!checkValidWindow) {
return true;
}
var result = false;
let result = false;
if (event && event.sender) {
// validate that event sender is from window we created
const browserWin = electron.BrowserWindow.fromWebContents(event.sender);
@ -107,7 +107,7 @@ electron.ipcMain.on(apiName, (event, arg) => {
// expose these methods primarily for testing...
module.exports = {
shouldCheckValidWindow: function (shouldCheck) {
shouldCheckValidWindow: function(shouldCheck) {
checkValidWindow = shouldCheck;
}
};
};

View File

@ -1,14 +1,17 @@
'use strict';
const log = require('./log.js');
const logLevels = require('./enums/logLevels.js')
const logLevels = require('./enums/logLevels.js');
// once a minute
setInterval(gatherMemory, 1000 * 60);
/**
* Gathers system memory and logs it to the remote system
*/
function gatherMemory() {
var memory = process.getProcessMemoryInfo();
var details =
let memory = process.getProcessMemoryInfo();
let details =
'workingSetSize: ' + memory.workingSetSize +
' peakWorkingSetSize: ' + memory.peakWorkingSetSize +
' privatesBytes: ' + memory.privatesBytes +

View File

@ -1,51 +0,0 @@
'use strict';
var cm = require('electron-context-menu');
/**
* Creates & applies Right Click Context Menu based on
* electron-context-menu library o all windows.
* Unless activated on edittable field, Reload option is shown.
* Enabled Cut/Copy/Paste/Delete/Select all on text.
* Enabled Save Image on images
* Enabled Copy Link on href Link
* Inspect Element is not enabled.
*/
function contextMenu(browserWindow){
cm({
browserWindow,
prepend: (params) => [
{
role: 'reload',
enabled: params.isEditable === false,
visible: params.isEditable === false
},
{
role: 'undo',
enabled: params.isEditable && params.editFlags.canUndu,
visible: params.isEditable
},
{
role: 'redo',
enabled: params.isEditable && params.editFlags.canRedo,
visible: params.isEditable
}
],
append: (params) => [
{
role: 'delete',
enabled: params.isEditable && params.editFlags.canDelete,
visible: params.isEditable
},
{
role: 'selectall',
enabled: params.isEditable && params.editFlags.canSelectAll,
visible: params.isEditable
}
],
showInspectElement: false
});
}
module.exports = contextMenu;

View File

@ -4,27 +4,36 @@ const electron = require('electron');
const { getConfigField, updateConfigField } = require('../config.js');
const AutoLaunch = require('auto-launch');
const isMac = require('../utils/misc.js').isMac;
const childProcess = require('child_process');
const log = require('../log.js');
const logLevels = require('../enums/logLevels.js');
const eventEmitter = require('../eventEmitter');
var minimizeOnClose = false;
var launchOnStartup = false;
var isAlwaysOnTop = false;
let minimizeOnClose = false;
let launchOnStartup = false;
let isAlwaysOnTop = false;
setCheckboxValues();
var symphonyAutoLauncher = new AutoLaunch({
name: 'Symphony',
path: process.execPath,
});
let launchAgentPath = '~/Library/LaunchAgents/com.symphony.symphony-desktop.agent.plist';
let symphonyAutoLauncher;
const template = [
{
label: 'Edit',
submenu: [
if (isMac) {
symphonyAutoLauncher = new AutoLaunch({
name: 'Symphony',
mac: {
useLaunchAgent: true,
},
path: process.execPath,
});
} else {
symphonyAutoLauncher = new AutoLaunch({
name: 'Symphony',
path: process.execPath,
});
}
const template = [{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
@ -34,252 +43,214 @@ const template = [
{ role: 'pasteandmatchstyle' },
{ role: 'delete' },
{ role: 'selectall' }
]
]
},
{
label: 'View',
submenu: [{
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.reload();
}
}
},
{
label: 'View',
submenu: [
{
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
click (item, focusedWindow) {
if (focusedWindow) {
focusedWindow.reload();
}
}
},
{
label: 'Toggle Developer Tools',
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
click (item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.toggleDevTools();
}
}
},
{
type: 'separator'
},
{
role: 'resetzoom'
},
{
role: 'zoomin'
},
{
role: 'zoomout'
},
{
type: 'separator'
},
{
role: 'togglefullscreen'
label: 'Toggle Developer Tools',
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.toggleDevTools();
}
]
}
},
{
role: 'window',
submenu: [
{
role: 'minimize'
},
{
role: 'close'
}
]
type: 'separator'
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click () { electron.shell.openExternal('https://www.symphony.com') }
}
]
role: 'resetzoom'
},
{
role: 'zoomin'
},
{
role: 'zoomout'
},
{
type: 'separator'
},
{
role: 'togglefullscreen'
}
]
},
{
role: 'window',
submenu: [{
role: 'minimize'
},
{
role: 'close'
}
]
},
{
role: 'help',
submenu: [{
label: 'Learn More',
click() { electron.shell.openExternal('https://www.symphony.com'); }
}]
}
];
function getTemplate(app) {
if (isMac && template[0].label !== app.getName()) {
template.unshift({
label: app.getName(),
submenu: [
{
role: 'about'
},
{
type: 'separator'
},
{
role: 'services',
submenu: []
},
{
type: 'separator'
},
{
role: 'hide'
},
{
role: 'hideothers'
},
{
role: 'unhide'
},
{
type: 'separator'
},
{
role: 'quit'
}
submenu: [{
role: 'about'
},
{
type: 'separator'
},
{
role: 'services',
submenu: []
},
{
type: 'separator'
},
{
role: 'hide'
},
{
role: 'hideothers'
},
{
role: 'unhide'
},
{
type: 'separator'
},
{
role: 'quit'
}
]
});
// Edit menu.
template[1].submenu.push(
{
type: 'separator'
// Edit menu.
template[1].submenu.push({
type: 'separator'
}, {
label: 'Speech',
submenu: [{
role: 'startspeaking'
},
{
label: 'Speech',
submenu: [
{
role: 'startspeaking'
},
{
role: 'stopspeaking'
}
]
role: 'stopspeaking'
}
)
// Window menu.
template[3].submenu = [
{
label: 'Close',
accelerator: 'CmdOrCtrl+W',
role: 'close'
},
{
label: 'Minimize',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
},
{
label: 'Zoom',
role: 'zoom'
},
{
type: 'separator'
},
{
label: 'Bring All to Front',
role: 'front'
}
]
]
});
// Window menu.
template[3].submenu = [{
label: 'Close',
accelerator: 'CmdOrCtrl+W',
role: 'close'
},
{
label: 'Minimize',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
},
{
label: 'Zoom',
role: 'zoom'
},
{
type: 'separator'
},
{
label: 'Bring All to Front',
role: 'front'
}
];
}
var index = 2;
if (isMac && template[0].label !== app.getName()){
let index = 2;
if (isMac && template[0].label !== app.getName()) {
index = 3;
}
// Window menu -> launchOnStartup.
template[index].submenu.push(
{
label: 'Auto Launch On Startup',
type: 'checkbox',
checked: launchOnStartup,
click: function (item) {
if (item.checked){
if (isMac){
// TODO: Need to change this implementation to AutoLaunch once they fix this issue ->
// https://github.com/Teamwork/node-auto-launch/issues/28
childProcess.exec(`launchctl load ${launchAgentPath}`, (err) => {
if (err){
let title = 'Error setting AutoLaunch configuration';
log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': process error ' + err);
electron.dialog.showErrorBox(title, 'Please try reinstalling the application');
}
});
} else {
symphonyAutoLauncher.enable()
.catch(function (err) {
let title = 'Error setting AutoLaunch configuration';
log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err);
electron.dialog.showErrorBox(title, title + ': ' + err);
});
}
} else {
if (isMac){
// TODO: Need to change this implementation to AutoLaunch once they fix this issue ->
// https://github.com/Teamwork/node-auto-launch/issues/28
childProcess.exec(`launchctl unload ${launchAgentPath}`, (err) => {
if (err){
let title = 'Error disabling AutoLaunch configuration';
log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': process error ' + err);
electron.dialog.showErrorBox(title, 'Please try reinstalling the application');
}
});
} else {
symphonyAutoLauncher.disable()
.catch(function (err) {
let title = 'Error setting AutoLaunch configuration';
log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err);
electron.dialog.showErrorBox(title, title + ': ' + err);
});
}
}
launchOnStartup = item.checked;
updateConfigField('launchOnStartup', launchOnStartup);
template[index].submenu.push({
label: 'Auto Launch On Startup',
type: 'checkbox',
checked: launchOnStartup,
click: function(item) {
if (item.checked) {
symphonyAutoLauncher.enable()
.catch(function(err) {
let title = 'Error setting AutoLaunch configuration';
log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err);
electron.dialog.showErrorBox(title, title + ': ' + err);
});
} else {
symphonyAutoLauncher.disable()
.catch(function(err) {
let title = 'Error setting AutoLaunch configuration';
log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err);
electron.dialog.showErrorBox(title, title + ': ' + err);
});
}
launchOnStartup = item.checked;
updateConfigField('launchOnStartup', launchOnStartup);
}
)
});
// Window menu -> alwaysOnTop.
template[index].submenu.push(
{
label: 'Always on top',
type: 'checkbox',
checked: isAlwaysOnTop,
click: (item) => {
isAlwaysOnTop = item.checked;
eventEmitter.emit('isAlwaysOnTop', isAlwaysOnTop);
updateConfigField('alwaysOnTop', isAlwaysOnTop);
}
template[index].submenu.push({
label: 'Always on top',
type: 'checkbox',
checked: isAlwaysOnTop,
click: (item) => {
isAlwaysOnTop = item.checked;
eventEmitter.emit('isAlwaysOnTop', isAlwaysOnTop);
updateConfigField('alwaysOnTop', isAlwaysOnTop);
}
)
});
// Window menu -> minimizeOnClose.
// Window menu -> minimizeOnClose.
// ToDo: Add behavior on Close.
template[index].submenu.push(
{
label: 'Minimize on Close',
type: 'checkbox',
checked: minimizeOnClose,
click: function (item) {
minimizeOnClose = item.checked;
updateConfigField('minimizeOnClose', minimizeOnClose);
}
template[index].submenu.push({
label: 'Minimize on Close',
type: 'checkbox',
checked: minimizeOnClose,
click: function(item) {
minimizeOnClose = item.checked;
updateConfigField('minimizeOnClose', minimizeOnClose);
}
)
});
if (!isMac){
template[index].submenu.push(
{
label: 'Quit Symphony',
click: function () {
app.quit();
}
if (!isMac) {
template[index].submenu.push({
label: 'Quit Symphony',
click: function() {
app.quit();
}
)
});
}
return template;
}
function setCheckboxValues(){
/**
* Sets the checkbox values for different menu items
* based on configuration
*/
function setCheckboxValues() {
getConfigField('minimizeOnClose').then(function(mClose) {
minimizeOnClose = mClose;
}).catch(function (err){
}).catch(function(err) {
let title = 'Error loading configuration';
log.send(logLevels.ERROR, 'MenuTemplate: error getting config field minimizeOnClose, error: ' + err);
electron.dialog.showErrorBox(title, title + ': ' + err);
@ -287,7 +258,7 @@ function setCheckboxValues(){
getConfigField('launchOnStartup').then(function(lStartup) {
launchOnStartup = lStartup;
}).catch(function (err){
}).catch(function(err) {
let title = 'Error loading configuration';
log.send(logLevels.ERROR, 'MenuTemplate: error getting config field launchOnStartup, error: ' + err);
electron.dialog.showErrorBox(title, title + ': ' + err);
@ -296,7 +267,7 @@ function setCheckboxValues(){
getConfigField('alwaysOnTop').then(function(mAlwaysOnTop) {
isAlwaysOnTop = mAlwaysOnTop;
eventEmitter.emit('isAlwaysOnTop', isAlwaysOnTop);
}).catch(function (err){
}).catch(function(err) {
let title = 'Error loading configuration';
log.send(logLevels.ERROR, 'MenuTemplate: error getting config field alwaysOnTop, error: ' + err);
electron.dialog.showErrorBox(title, title + ': ' + err);
@ -304,7 +275,7 @@ function setCheckboxValues(){
getConfigField('notificationSettings').then(function(notfObject) {
eventEmitter.emit('notificationSettings', notfObject);
}).catch(function (err){
}).catch(function(err) {
let title = 'Error loading configuration';
log.send(logLevels.ERROR, 'MenuTemplate: error getting config field notificationSettings, error: ' + err);
electron.dialog.showErrorBox(title, title + ': ' + err);
@ -312,11 +283,11 @@ function setCheckboxValues(){
}
function getMinimizeOnClose(){
function getMinimizeOnClose() {
return minimizeOnClose;
}
module.exports = {
getTemplate : getTemplate,
getMinimizeOnClose : getMinimizeOnClose
getTemplate: getTemplate,
getMinimizeOnClose: getMinimizeOnClose
};

View File

@ -3,13 +3,21 @@
const log = require('../log.js');
const logLevels = require('../enums/logLevels.js');
// One animation at a time
/**
* 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);
@ -17,8 +25,12 @@ AnimationQueue.prototype.push = function(object) {
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() {
@ -37,10 +49,13 @@ AnimationQueue.prototype.animate = function(object) {
' with stack trace:' + err.stack);
/* eslint-enable no-console */
})
}
};
/**
* Clears the queue
*/
AnimationQueue.prototype.clear = function() {
this.queue = [];
}
};
module.exports = AnimationQueue;

View File

@ -1,4 +1,4 @@
'use strict'
'use strict';
//
// BrowserWindow preload script use to create notifications window for
@ -9,6 +9,10 @@
const electron = require('electron');
const ipc = electron.ipcRenderer;
/**
* Sets style for a notification
* @param config
*/
function setStyle(config) {
// Style it
let notiDoc = window.document;
@ -20,14 +24,14 @@ function setStyle(config) {
let close = notiDoc.getElementById('close');
// Default style
setStyleOnDomElement(config.defaultStyleContainer, container)
setStyleOnDomElement(config.defaultStyleContainer, container);
let style = {
height: config.height,
width: config.width,
borderRadius: config.borderRadius + 'px'
}
setStyleOnDomElement(style, container)
};
setStyleOnDomElement(style, container);
setStyleOnDomElement(config.defaultStyleHeader, header);
@ -40,6 +44,11 @@ function setStyle(config) {
setStyleOnDomElement(config.defaultStyleClose, close);
}
/**
* Sets contents for a notification
* @param event
* @param notificationObj
*/
function setContents(event, notificationObj) {
// sound
if (notificationObj.sound) {
@ -49,7 +58,7 @@ function setContents(event, notificationObj) {
// Won't check remote files e.g. http://
if (notificationObj.sound.match(/^file:/) !== null
|| notificationObj.sound.match(/^\//) !== null) {
let audio = new window.Audio(notificationObj.sound)
let audio = new window.Audio(notificationObj.sound);
audio.play()
}
} catch (e) {
@ -102,15 +111,20 @@ function setContents(event, notificationObj) {
// note: use onclick because we only want one handler, for case
// when content gets overwritten by notf with same tag
closeButton.onclick = function(clickEvent) {
clickEvent.stopPropagation()
clickEvent.stopPropagation();
ipc.send('electron-notify-close', winId, notificationObj)
}
};
container.onclick = function() {
ipc.send('electron-notify-click', winId, notificationObj);
}
}
/**
* Sets style on a notification for a DOM element
* @param styleObj
* @param domElement
*/
function setStyleOnDomElement(styleObj, domElement) {
try {
let styleAttr = Object.keys(styleObj);
@ -124,22 +138,30 @@ function setStyleOnDomElement(styleObj, domElement) {
}
}
/**
* Loads the config
* @param event
* @param conf
*/
function loadConfig(event, conf) {
setStyle(conf || {})
}
/**
* Resets the notification window
*/
function reset() {
let notiDoc = window.document
let container = notiDoc.getElementById('container')
let closeButton = notiDoc.getElementById('close')
let notiDoc = window.document;
let container = notiDoc.getElementById('container');
let closeButton = notiDoc.getElementById('close');
// Remove event listener
let newContainer = container.cloneNode(true)
container.parentNode.replaceChild(newContainer, container)
let newCloseButton = closeButton.cloneNode(true)
let newContainer = container.cloneNode(true);
container.parentNode.replaceChild(newContainer, container);
let newCloseButton = closeButton.cloneNode(true);
closeButton.parentNode.replaceChild(newCloseButton, closeButton)
}
ipc.on('electron-notify-set-contents', setContents)
ipc.on('electron-notify-load-config', loadConfig)
ipc.on('electron-notify-reset', reset)
ipc.on('electron-notify-set-contents', setContents);
ipc.on('electron-notify-load-config', loadConfig);
ipc.on('electron-notify-reset', reset);

View File

@ -9,8 +9,8 @@
<p id="message"></p>
<div id="close">
<svg fill="#000000" height="16" viewBox="0 0 24 24" width="16" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
</div>
</div>

View File

@ -1,4 +1,4 @@
'use strict'
'use strict';
//
// code here adapted from https://www.npmjs.com/package/electron-notify
// made following changes:
@ -10,12 +10,13 @@
//
const path = require('path');
const fs = require('fs');
const async = require('async');
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 } = require('../utils/misc');
const { isMac, isNodeEnv } = require('../utils/misc');
const log = require('../log.js');
const logLevels = require('../enums/logLevels.js');
@ -48,6 +49,8 @@ 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
@ -131,17 +134,11 @@ let config = {
acceptFirstMouse: true,
webPreferences: {
preload: path.join(__dirname, 'electron-notify-preload.js'),
sandbox: true,
nodeIntegration: false
sandbox: sandboxed,
nodeIntegration: isNodeEnv
}
}
}
// function setConfig(customConfig) {
// Object.assign(customConfig, config);
//
// calcDimensions();
// }
};
if (app.isReady()) {
setup();
@ -149,7 +146,10 @@ if (app.isReady()) {
app.on('ready', setup);
}
// Method to update notification config
/**
* Method to update notification config
* @param customConfig
*/
function updateConfig(customConfig) {
// Fetching user preferred notification position from config
if (customConfig.position) {
@ -164,6 +164,9 @@ function updateConfig(customConfig) {
}
}
/**
* Method to setup the notification configuration
*/
function setup() {
setupConfig();
@ -174,6 +177,10 @@ function setup() {
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 {
@ -185,12 +192,15 @@ function getTemplatePath() {
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
config.totalWidth = config.width;
let firstPosX, firstPosY;
switch (config.startCorner) {
@ -217,13 +227,16 @@ function calcDimensions() {
config.firstPos = {
x: firstPosX,
y: firstPosY
}
};
// Set nextInsertPos
nextInsertPos.x = config.firstPos.x
nextInsertPos.x = config.firstPos.x;
nextInsertPos.y = config.firstPos.y
}
/**
* Setup the notification config
*/
function setupConfig() {
closeAll();
@ -270,6 +283,11 @@ function setupConfig() {
config.maxVisibleNotifications = config.maxVisibleNotifications > 5 ? 5 : config.maxVisibleNotifications;
}
/**
* Notifies the user
* @param notification
* @returns {*}
*/
function notify(notification) {
// Is it an object and only one argument?
if (arguments.length === 1 && typeof notification === 'object') {
@ -280,17 +298,25 @@ function notify(notification) {
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) {
@ -346,7 +372,7 @@ function showNotification(notificationObj) {
});
delete notificationWindow.electronNotifyOnCloseFunc;
}
setNotificationContents(notificationWindow, notificationObj)
setNotificationContents(notificationWindow, notificationObj);
resolve();
return;
}
@ -358,8 +384,8 @@ function showNotification(notificationObj) {
// Get inactiveWindow or create new:
getWindow().then(function(notificationWindow) {
// Move window to position
calcInsertPos()
setWindowPosition(notificationWindow, nextInsertPos.x, nextInsertPos.y)
calcInsertPos();
setWindowPosition(notificationWindow, nextInsertPos.x, nextInsertPos.y);
let updatedNotfWindow = setNotificationContents(notificationWindow, notificationObj);
@ -375,6 +401,12 @@ function showNotification(notificationObj) {
})
}
/**
* Sets the HTML notification contents along with other options
* @param notfWindow
* @param notfObj
* @returns {*}
*/
function setNotificationContents(notfWindow, notfObj) {
// Display time per notification basis.
@ -384,7 +416,7 @@ function setNotificationContents(notfWindow, notfObj) {
clearTimeout(notfWindow.displayTimer);
}
var updatedNotificationWindow = notfWindow;
const updatedNotificationWindow = notfWindow;
updatedNotificationWindow.notfyObj = notfObj;
@ -434,7 +466,13 @@ function setNotificationContents(notfWindow, notfObj) {
return updatedNotificationWindow;
}
// Close notification function
/**
* Closes the notification
* @param notificationWindow
* @param notificationObj
* @param getTimeoutId
* @returns {Function}
*/
function buildCloseNotification(notificationWindow, notificationObj, getTimeoutId) {
return function(event) {
if (closedNotifications[notificationObj.id]) {
@ -456,7 +494,7 @@ function buildCloseNotification(notificationWindow, notificationObj, getTimeoutI
}
// reset content
notificationWindow.webContents.send('electron-notify-reset')
notificationWindow.webContents.send('electron-notify-reset');
if (getTimeoutId && typeof getTimeoutId === 'function') {
let timeoutId = getTimeoutId();
clearTimeout(timeoutId);
@ -477,8 +515,13 @@ function buildCloseNotification(notificationWindow, notificationObj, getTimeoutI
}
}
// Always add to animationQueue to prevent erros (e.g. notification
// got closed while it was moving will produce an error)
/**
* 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({
@ -506,10 +549,10 @@ ipc.on('electron-notify-click', function (event, winId, notificationObj) {
}
});
/*
* Checks for queued notifications and add them
* to AnimationQueue if possible
*/
/**
* Checks for queued notifications and add them
* to AnimationQueue if possible
*/
function checkForQueuedNotifications() {
if (notificationQueue.length > 0 &&
activeNotifications.length < config.maxVisibleNotifications) {
@ -521,27 +564,27 @@ function checkForQueuedNotifications() {
}
}
/*
* Moves the notifications one position down,
* starting with notification at startPos
*
* @param {int} startPos
*/
/**
* 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()
resolve();
return
}
// Build array with index of affected notifications
let notificationPosArray = []
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 = async.map // Best performance
let asyncFunc = asyncMap; // Best performance
if (config.animateInParallel === false) {
asyncFunc = async.mapSeries // Sluggish
asyncFunc = asyncMapSeries // Sluggish
}
asyncFunc(notificationPosArray, moveNotificationAnimation, function() {
resolve()
@ -549,6 +592,11 @@ function moveOneDown(startPos) {
})
}
/**
* Moves the notification animation
* @param i
* @param done
*/
function moveNotificationAnimation(i, done) {
// Get notification to move
let notificationWindow = activeNotifications[i];
@ -568,32 +616,38 @@ function moveNotificationAnimation(i, done) {
}
// Get startPos, calc step size and start animationInterval
let startY = notificationWindow.getPosition()[1]
let step = (newY - startY) / config.animationSteps
let curStep = 1
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)
clearInterval(animationInterval);
done(null, 'done');
return;
}
// Move one step down
setWindowPosition(notificationWindow, config.firstPos.x, startY + curStep * step)
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.isDestroyed()) {
browserWin.setPosition(parseInt(posX, 10), parseInt(posY, 10))
}
}
/*
* Find next possible insert position (on top)
*/
/**
* Find next possible insert position (on top)
*/
function calcInsertPos() {
if (activeNotifications.length < config.maxVisibleNotifications) {
switch(config.startCorner) {
@ -611,35 +665,38 @@ function calcInsertPos() {
}
}
/*
* Get a window to display a notification. Use inactiveWindows or
* create a new window
* @return {Window}
*/
/**
* 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
let notificationWindow;
// Are there still inactiveWindows?
if (inactiveWindows.length > 0) {
notificationWindow = inactiveWindows.pop()
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.setVisibleOnAllWorkspaces(true)
notificationWindow.loadURL(getTemplatePath())
let windowProperties = config.defaultWindow;
windowProperties.width = config.width;
windowProperties.height = config.height;
notificationWindow = new BrowserWindow(windowProperties);
notificationWindow.setVisibleOnAllWorkspaces(true);
notificationWindow.loadURL(getTemplatePath());
notificationWindow.webContents.on('did-finish-load', function() {
// Done
notificationWindow.webContents.send('electron-notify-load-config', config)
notificationWindow.webContents.send('electron-notify-load-config', config);
resolve(notificationWindow)
})
}
})
}
/**
* Closes all the notifications and windows
*/
function closeAll() {
// Clear out animation Queue and close windows
animationQueue.clear();
@ -665,10 +722,13 @@ function closeAll() {
}
/**
/* once a minute, remove inactive windows to free up memory used.
* Once a minute, remove inactive windows to free up memory used.
*/
setInterval(cleanUpInactiveWindow, 60000);
/**
* Cleans up inactive windows
*/
function cleanUpInactiveWindow() {
inactiveWindows.forEach(function(window) {
window.close();
@ -676,6 +736,6 @@ function cleanUpInactiveWindow() {
inactiveWindows = [];
}
module.exports.notify = notify
module.exports.updateConfig = updateConfig
module.exports.reset = setupConfig
module.exports.notify = notify;
module.exports.updateConfig = updateConfig;
module.exports.reset = setupConfig;

View File

@ -51,6 +51,10 @@ class Notify {
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);
@ -61,6 +65,10 @@ class Notify {
}
}
/**
* Handles on click event
* @param arg
*/
function onClick(arg) {
if (arg.id === this._id) {
log.send(logLevels.INFO, 'clicking notification, id=' + this._id);
@ -70,6 +78,10 @@ class Notify {
}
}
/**
* 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);
@ -80,6 +92,10 @@ class Notify {
}
}
/**
* 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
@ -95,7 +111,7 @@ class Notify {
}
/**
* close notification
* Closes notification
*/
close() {
if (typeof this._closeNotification === 'function') {
@ -105,7 +121,7 @@ class Notify {
}
/**
* always allow showing notifications.
* Always allow showing notifications.
* @return {string} 'granted'
*/
static get permission() {
@ -113,14 +129,14 @@ class Notify {
}
/**
* returns data object passed in via constructor options
* Returns data object passed in via constructor options
*/
get data() {
return this._data;
}
/**
* add event listeners for 'click', 'close', 'show', 'error' events
* Adds event listeners for 'click', 'close', 'show', 'error' events
*
* @param {String} event event to listen for
* @param {func} cb callback invoked when event occurs
@ -132,7 +148,7 @@ class Notify {
}
/**
* remove event listeners for 'click', 'close', 'show', 'error' events
* 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
@ -144,7 +160,7 @@ class Notify {
}
/**
* removes all event listeners
* Removes all event listeners
*/
removeAllEvents() {
this.destroy();
@ -168,10 +184,10 @@ class Notify {
*/
function Queue(emitter) {
/**
* Cache emitter on.
* @api private
*/
var cache = emitter.on;
* Cache emitter on.
* @api private
*/
const cache = emitter.on;
let modifiedEmitter = emitter;
/**
* Emit event and store it if no
@ -180,7 +196,7 @@ function Queue(emitter) {
*
* .queue('message', 'hi');
*
* @param {String} event
* @param {String} topic
*/
modifiedEmitter.queue = function(topic) {
this._queue = this._queue || {};
@ -191,18 +207,18 @@ function Queue(emitter) {
(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 {Emitter}
* @return {Event}
*/
modifiedEmitter.on = modifiedEmitter.addEventListener = function(topic, fn) {
this._queue = this._queue || {};
var topics = this._queue[topic];
const topics = this._queue[topic];
cache.apply(this, arguments);
if (!this._callbacks) {
@ -211,7 +227,9 @@ function Queue(emitter) {
this._callbacks[topic] = true;
if (topics) {
for(var i = 0, l = topics.length; i < l; i++) {
let i = 0;
const l = topics.length;
for(; i < l; i++) {
fn.apply(this, topics[i]);
}
delete this._queue[topic];

View File

@ -9,7 +9,9 @@ let selectedDisplay;
renderSettings();
// Method that renders the data from user config
/**
* Method that renders the data from user config
*/
function renderSettings() {
document.addEventListener('DOMContentLoaded', function () {
@ -33,6 +35,9 @@ function renderSettings() {
}
/**
* Updates the configuration and closes the alert
*/
function updateAndClose() {
ipc.send('update-config', {position: selectedPosition, display: selectedDisplay});
ipc.send('close-alert');

View File

@ -15,7 +15,7 @@
<label class="label">Monitor</label>
<div id="screens" class="main">
<label>Notification shown on Monitor: </label>
<select class="selector" id="screen-selector">
<select class="selector" id="screen-selector" title="position">
</select>
</div>
<label class="label">Position</label>

View File

@ -17,6 +17,7 @@ let configurationWindow;
let screens;
let position;
let display;
let sandboxed = false;
let windowConfig = {
width: 460,
@ -27,7 +28,7 @@ let windowConfig = {
resizable: false,
webPreferences: {
preload: path.join(__dirname, 'configure-notification-position-preload.js'),
sandbox: true,
sandbox: sandboxed,
nodeIntegration: false
}
};
@ -40,6 +41,9 @@ app.on('ready', () => {
electron.screen.on('display-removed', updateScreens);
});
/**
* Update all the screens
*/
function updateScreens() {
screens = electron.screen.getAllDisplays();
@ -49,6 +53,10 @@ function updateScreens() {
}
}
/**
* Gets the template path
* @returns {string}
*/
function getTemplatePath() {
let templatePath = path.join(__dirname, 'configure-notification-position.html');
try {
@ -59,6 +67,10 @@ function getTemplatePath() {
return 'file://' + templatePath;
}
/**
* Opens the configuration window for a specific window
* @param windowName
*/
function openConfigurationWindow(windowName) {
let allWindows = BrowserWindow.getAllWindows();
allWindows = allWindows.find((window) => { return window.winName === windowName });
@ -93,6 +105,9 @@ function openConfigurationWindow(windowName) {
});
}
/**
* Destroys a window
*/
function destroyWindow() {
configurationWindow = null;
}

View File

@ -20,9 +20,26 @@ const apiName = apiEnums.apiName;
const getMediaSources = require('../desktopCapturer/getSources');
const crashReporter = require('../crashReporter');
require('../downloadManager/downloadManager');
require('../downloadManager');
const nodeURL = require('url');
// bug in electron preventing us from using spellchecker in pop outs
// https://github.com/electron/electron/issues/4025
// so loading the spellchecker in try catch so that we don't
// block other method from loading
document.addEventListener('DOMContentLoaded', () => {
try {
/* eslint-disable global-require */
const SpellCheckerHelper = require('../spellChecker').SpellCheckHelper;
/* eslint-enable global-require */
// Method to initialize spell checker
const spellChecker = new SpellCheckerHelper();
spellChecker.initializeSpellChecker();
} catch (err) {
/* eslint-disable no-console */
console.error('unable to load the spell checker module, hence, skipping the spell check feature ' + err);
/* eslint-enable no-console */
}
});
// hold ref so doesn't get GC'ed
const local = {
@ -56,25 +73,6 @@ function createAPI() {
return;
}
// bug in electron is preventing using event 'will-navigate' from working
// in sandboxed environment. https://github.com/electron/electron/issues/8841
// so in the mean time using this code below to block clicking on A tags.
// A tags are allowed if they include href='_blank', this cause 'new-window'
// event to be received which is handled properly in windowMgr.js
window.addEventListener('beforeunload', function(event) {
var newUrl = document.activeElement && document.activeElement.href;
if (newUrl) {
var currHostName = window.location.hostname;
var parsedNewUrl = nodeURL.parse(newUrl);
var parsedNewUrlHostName = parsedNewUrl && parsedNewUrl.hostname;
if (currHostName !== parsedNewUrlHostName) {
/* eslint-disable no-param-reassign */
event.returnValue = 'false';
/* eslint-enable no-param-reassign */
}
}
});
// 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)
@ -85,14 +83,14 @@ function createAPI() {
window.ssf = {
getVersionInfo: function() {
return new Promise(function(resolve) {
var appName = remote.app.getName();
var appVer = remote.app.getVersion();
let appName = remote.app.getName();
let appVer = remote.app.getVersion();
const verInfo = {
containerIdentifier: appName,
containerVer: appVer,
apiVer: '1.0.0'
}
};
resolve(verInfo);
});
},
@ -116,9 +114,9 @@ function createAPI() {
/**
* provides api to allow user to capture portion of screen, see api
* details in screenSnipper/ScreenSnippet.js
* details in screenSnipper/index.js
*/
ScreenSnippet: remote.require('./screenSnippet/ScreenSnippet.js').ScreenSnippet,
ScreenSnippet: remote.require('./screenSnippet/index.js').ScreenSnippet,
/**
* Provides API to crash the renderer process that calls this function
@ -188,7 +186,7 @@ function createAPI() {
* this registration func is invoked then the protocolHandler callback
* will be immediately called.
*/
registerProtocolHandler: function (protocolHandler) {
registerProtocolHandler: function(protocolHandler) {
if (typeof protocolHandler === 'function') {
local.processProtocolAction = protocolHandler;
@ -344,4 +342,4 @@ function createAPI() {
window.addEventListener('online', updateOnlineStatus, false);
updateOnlineStatus();
}
}

View File

@ -43,6 +43,10 @@ function setProtocolUrl(uri) {
protocolUrl = uri;
}
/**
* gets the protocol url set against an instance
* @returns {*}
*/
function getProtocolUrl() {
return protocolUrl;
}

View File

@ -10,10 +10,12 @@ const path = require('path');
const { isMac, isDevEnv } = require('../utils/misc.js');
const log = require('../log.js');
const logLevels = require('../enums/logLevels.js');
const eventEmitter = require('.././eventEmitter');
// 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
@ -47,21 +49,32 @@ class ScreenSnippet {
// 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 ];
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');
'../../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');
}
captureUtilArgs = [ outputFileName ];
// 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', false);
}
}
captureUtilArgs = [outputFileName];
}
log.send(logLevels.INFO, 'ScreenSnippet: starting screen capture util: ' + captureUtil + ' with args=' + captureUtilArgs);
@ -72,6 +85,10 @@ class ScreenSnippet {
}
child = childProcess.execFile(captureUtil, captureUtilArgs, (error) => {
// Method to reset always on top feature
if (isAlwaysOnTop) {
eventEmitter.emit('isAlwaysOnTop', true);
}
// will be called when child process exits.
if (error && error.killed) {
// processs was killed, just resolve with no data.
@ -84,9 +101,15 @@ class ScreenSnippet {
}
}
// 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!
/**
* 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) {
@ -120,8 +143,7 @@ function readResult(outputFileName, resolve, reject, childProcessErr) {
});
} catch (error) {
reject(createError(error));
}
finally {
} finally {
// remove tmp file (async)
fs.unlink(outputFileName, function(removeErr) {
// note: node complains if calling async
@ -136,14 +158,24 @@ function readResult(outputFileName, resolve, reject, childProcessErr) {
}
/* eslint-disable class-methods-use-this */
/**
* Create an error object with the ERROR level
* @param msg
* @returns {Error}
*/
function createError(msg) {
var err = new Error(msg);
let err = new Error(msg);
err.type = 'ERROR';
return err;
}
/**
* Create an error object with the WARN level
* @param msg
* @returns {Error}
*/
function createWarn(msg) {
var err = new Error(msg);
let err = new Error(msg);
err.type = 'WARN';
return err;
}
@ -153,4 +185,4 @@ module.exports = {
ScreenSnippet: ScreenSnippet,
// note: readResult only exposed for testing purposes
readResult: readResult
}
};

71
js/spellChecker/index.js Normal file
View File

@ -0,0 +1,71 @@
const { remote } = require('electron');
const { MenuItem } = remote;
const { isMac } = require('./../utils/misc');
const { SpellCheckHandler, ContextMenuListener, ContextMenuBuilder } = require('electron-spellchecker');
class SpellCheckHelper {
/**
* A constructor to create an instance of the spell checker
*/
constructor() {
this.spellCheckHandler = new SpellCheckHandler();
}
/**
* Method to initialize spell checker
*/
initializeSpellChecker() {
this.spellCheckHandler.attachToInput();
// 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) {
this.spellCheckHandler.switchLanguage('en-US');
}
const contextMenuBuilder = new ContextMenuBuilder(this.spellCheckHandler, null, false, SpellCheckHelper.processMenu);
this.contextMenuListener = new ContextMenuListener((info) => {
contextMenuBuilder.showPopupMenu(info);
});
}
/**
* 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
*/
static 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: 'Reload'
}));
}
return menu;
}
}
module.exports = {
SpellCheckHelper: SpellCheckHelper
};

View File

@ -7,7 +7,7 @@ const logLevels = require('../enums/logLevels.js');
* Search given argv for argName using exact match or starts with.
* @param {Array} argv Array of strings
* @param {String} argName Arg name to search for.
* @param {bool} exactMatch If true then look for exact match otherwise
* @param {Boolean} exactMatch If true then look for exact match otherwise
* try finding arg that starts with argName.
* @return {String} If found, returns the arg, otherwise null.
*/
@ -26,4 +26,5 @@ function getCmdLineArg(argv, argName, exactMatch) {
return null;
}
module.exports = getCmdLineArg
module.exports = getCmdLineArg;

View File

@ -7,12 +7,11 @@
* @return {String} guid value in string
*/
function getGuid() {
const guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,
function(c) {
var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
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);
});
return guid;
}
module.exports = getGuid;

View File

@ -5,17 +5,17 @@ const { isMac } = require('./misc.js');
const log = require('../log.js');
const logLevels = require('../enums/logLevels.js');
var Registry = require('winreg');
var symphonyRegistryHKCU = new Registry({
let Registry = require('winreg');
let symphonyRegistryHKCU = new Registry({
hive: Registry.HKCU,
key: symphonyRegistry
});
var symphonyRegistryHKLM = new Registry({
let symphonyRegistryHKLM = new Registry({
key: symphonyRegistry
});
var symphonyRegistryHKLM6432 = new Registry({
let symphonyRegistryHKLM6432 = new Registry({
key: symphonyRegistry.replace('\\Software','\\Software\\WOW6432Node')
});
@ -24,24 +24,24 @@ var symphonyRegistryHKLM6432 = new Registry({
* that are intended to be used as global (or default) value for all users
* running this app.
*/
var getRegistry = function (name) {
var promise = new Promise(function(resolve, reject) {
let getRegistry = function (name) {
return new Promise(function (resolve, reject) {
if (isMac) {
reject('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) {
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) {
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;
@ -49,18 +49,16 @@ var getRegistry = function (name) {
// 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) {
symphonyRegistryHKLM6432.get(name, function (err3, reg3) {
if (!err3 && reg3 !== null && reg3.value) {
resolve(reg3.value);
} else{
} else {
reject('Cannot find PodUrl Registry. Using default url.');
}
});
});
});
});
};
return promise;
}
module.exports = getRegistry
module.exports = getRegistry;

View File

@ -1,4 +1,4 @@
'use strict'
'use strict';
const electron = require('electron');
@ -6,8 +6,8 @@ const electron = require('electron');
/**
* Returns true if given rectangle is contained within the workArea of at
* least one of the screens.
* @param {x: Number, y: Number, width: Number, height: Number} rect
* @return {Boolean} true if condition in desc is met.
* @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) {

View File

@ -5,7 +5,10 @@ const isDevEnv = process.env.ELECTRON_DEV ?
const isMac = (process.platform === 'darwin');
const isNodeEnv = !!process.env.NODE_ENV;
module.exports = {
isDevEnv: isDevEnv,
isMac: isMac
isMac: isMac,
isNodeEnv: isNodeEnv
};

View File

@ -7,7 +7,6 @@ const path = require('path');
const nodeURL = require('url');
const querystring = require('querystring');
const filesize = require('filesize');
const {dialog} = require('electron');
const { getTemplate, getMinimizeOnClose } = require('./menus/menuTemplate.js');
const loadErrors = require('./dialogs/showLoadError.js');
@ -20,6 +19,7 @@ const eventEmitter = require('./eventEmitter');
const throttle = require('./utils/throttle.js');
const { getConfigField, updateConfigField } = require('./config.js');
const { isMac, isNodeEnv } = require('./utils/misc');
const crashReporter = require('./crashReporter');
@ -39,6 +39,7 @@ let boundsChangeWindow;
let alwaysOnTop = false;
let position = 'lower-right';
let display;
let sandboxed = false;
// note: this file is built using browserify in prebuild step.
const preloadMainScript = path.join(__dirname, 'preload/_preloadMain.js');
@ -46,18 +47,36 @@ const preloadMainScript = path.join(__dirname, 'preload/_preloadMain.js');
const MIN_WIDTH = 300;
const MIN_HEIGHT = 600;
/**
* 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
* @param url
* @returns {Url}
*/
function getParsedUrl(url) {
return nodeURL.parse(url);
}
/**
* Creates the main window
* @param initialUrl
*/
function createMainWindow(initialUrl) {
getConfigField('mainWinPos').then(
function (bounds) {
@ -70,6 +89,11 @@ function createMainWindow(initialUrl) {
)
}
/**
* Creates the main window with bounds
* @param initialUrl
* @param initialBounds
*/
function doCreateMainWindow(initialUrl, initialBounds) {
let url = initialUrl;
let key = getGuid();
@ -95,9 +119,10 @@ function doCreateMainWindow(initialUrl, initialBounds) {
minHeight: MIN_HEIGHT,
alwaysOnTop: false,
webPreferences: {
sandbox: true,
nodeIntegration: false,
sandbox: sandboxed,
nodeIntegration: isNodeEnv,
preload: preloadMainScript,
nativeWindowOpen: true
}
};
@ -212,7 +237,7 @@ function doCreateMainWindow(initialUrl, initialBounds) {
function destroyAllWindows() {
let keys = Object.keys(windows);
for (var i = 0, len = keys.length; i < len; i++) {
for (let i = 0, len = keys.length; i < len; i++) {
let winKey = keys[i];
removeWindowKey(winKey);
}
@ -244,6 +269,9 @@ function doCreateMainWindow(initialUrl, initialBounds) {
// bug in electron is preventing this from working in sandboxed evt...
// https://github.com/electron/electron/issues/8841
mainWindow.webContents.on('will-navigate', function(event, willNavUrl) {
if (!sandboxed) {
return;
}
event.preventDefault();
openUrlInDefaultBrower(willNavUrl);
});
@ -277,7 +305,7 @@ function doCreateMainWindow(initialUrl, initialBounds) {
let height = newWinOptions.height || MIN_HEIGHT;
// try getting x and y position from query parameters
var query = newWinParsedUrl && querystring.parse(newWinParsedUrl.query);
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);
@ -346,13 +374,18 @@ function doCreateMainWindow(initialUrl, initialBounds) {
}
});
contextMenu(mainWindow);
}
/**
* 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);
@ -361,10 +394,19 @@ function saveMainWinBounds() {
}
}
/**
* Gets the main window
* @returns {*}
*/
function getMainWindow() {
return mainWindow;
}
/**
* Gets a window's size and position
* @param window
* @returns {*}
*/
function getWindowSizeAndPosition(window) {
if (window) {
let newPos = window.getPosition();
@ -384,14 +426,28 @@ function getWindowSizeAndPosition(window) {
return null;
}
/**
* Shows the main window
*/
function showMainWindow() {
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];
@ -401,6 +457,10 @@ function hasWindow(win, winKey) {
return false;
}
/**
* Sets if a user is online
* @param status
*/
function setIsOnline(status) {
isOnline = status;
}
@ -448,6 +508,10 @@ function sendChildWinBoundsChange(window) {
}
}
/**
* Opens an external url in the system's default browser
* @param urlToOpen
*/
function openUrlInDefaultBrower(urlToOpen) {
if (urlToOpen) {
electron.shell.openExternal(urlToOpen);
@ -479,6 +543,119 @@ eventEmitter.on('notificationSettings', (notificationSettings) => {
display = notificationSettings.display;
});
/**
* Method that gets invoked when an external display
* is removed using electron 'display-removed' event.
*/
function verifyDisplays() {
// 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;
const screen = electron.screen;
// Loops through all the available displays and
// verifies if the wrapper exists within the display bounds
// returns false if not exists otherwise true
return !!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 screen = electron.screen;
const {workArea} = 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);
}
}
module.exports = {
createMainWindow: createMainWindow,
getMainWindow: getMainWindow,
@ -487,5 +664,6 @@ module.exports = {
hasWindow: hasWindow,
setIsOnline: setIsOnline,
activate: activate,
setBoundsChangeWindow: setBoundsChangeWindow
setBoundsChangeWindow: setBoundsChangeWindow,
verifyDisplays: verifyDisplays
};

8092
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,12 +10,14 @@
"demo-win": "npm run prebuild && cross-env ELECTRON_DEV=true electron . --url=file:///demo/index.html",
"demo-mac": "npm run prebuild && cross-env ELECTRON_DEV=true electron . --url=file://$(pwd)/demo/index.html",
"unpacked-mac": "npm run prebuild && npm run test && build --mac --dir",
"unpacked-win": "npm run prebuild && npm run test && build --win --x64 --dir && npm run rename-exe",
"unpacked-win-x86": "npm run prebuild && npm run test && build --win --ia32 --dir && npm run rename-exe",
"packed-mac": "npm run unpacked-mac && packagesbuild -v installer/mac/symphony-mac-packager.pkgproj",
"unpacked-win": "npm run prebuild && npm run test && build --win --x64 --dir",
"unpacked-win-x86": "npm run prebuild && npm run test && build --win --ia32",
"prebuild": "npm run rebuild && npm run browserify-preload",
"browserify-preload": "browserify -o js/preload/_preloadMain.js -x electron --insert-global-vars=__filename,__dirname js/preload/preloadMain.js",
"browserify-preload": "browserify -o js/preload/_preloadMain.js -x electron --insert-global-vars=__filename,__dirname js/preload/preloadMain.js --exclude electron-spellchecker",
"rebuild": "electron-rebuild -f",
"test": "npm run lint && jest --verbose --testPathPattern test",
"test": "npm run lint && jest --verbose --testPathPattern test && npm run rebuild",
"spectron-test": "jest --config tests/spectron/jest_spectron.json --runInBand && npm run rebuild",
"lint": "eslint --ext .js js/",
"rename-exe": "cd dist/win-unpacked && ren Symphony.exe Symphony-Electron.exe"
},
@ -24,10 +26,16 @@
"transformIgnorePatterns": []
},
"build": {
"asarUnpack": [
"node_modules/@paulcbetts/cld/build/Release/cld.node"
],
"files": [
"!coverage/*",
"!installer/*",
"!tests/*"
"!tests/*",
"!node_modules/@paulcbetts/cld/deps/cld${/*}",
"!node_modules/@paulcbetts/cld/build/deps${/*}",
"!node_modules/@paulcbetts/spellchecker/vendor${/*}"
],
"extraFiles": "config/Symphony.config",
"appId": "symphony-electron-desktop",
@ -68,7 +76,7 @@
"devDependencies": {
"browserify": "^14.1.0",
"cross-env": "^3.2.4",
"electron": "1.6.11",
"electron": "1.7.5",
"electron-builder": "^13.9.0",
"electron-builder-squirrel-windows": "^12.3.0",
"electron-packager": "^8.5.2",
@ -78,18 +86,25 @@
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.10.0",
"jest": "^19.0.2"
"jest": "^19.0.2",
"ncp": "^2.0.0",
"robotjs": "^0.4.7",
"spectron": "^3.7.2"
},
"dependencies": {
"@paulcbetts/system-idle-time": "^1.0.4",
"async": "^2.1.5",
"appdirectory": "^0.1.0",
"async.map": "^0.5.2",
"async.mapseries": "^0.5.2",
"auto-launch": "^5.0.1",
"electron-context-menu": "^0.8.0",
"electron-squirrel-startup": "^1.0.0",
"keymirror": "0.1.1",
"winreg": "^1.2.3",
"electron-dl": "^1.9.0",
"filesize": "^3.5.10"
"electron-log": "^2.2.7",
"electron-spellchecker": "^1.2.0",
"electron-squirrel-startup": "^1.0.0",
"filesize": "^3.5.10",
"keymirror": "0.1.1",
"lodash.omit": "^4.5.0",
"winreg": "^1.2.3"
},
"optionalDependencies": {
"screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet.git#v1.0.1"

View File

@ -1,21 +1,21 @@
const downloadManager = require('../js/downloadManager/downloadManager');
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 () {
describe('download manager', function() {
describe('Download Manager to create DOM once download is initiated', function() {
beforeEach(function() {
global.document.body.innerHTML =
'<div id="download-main">' +
'</div>';
});
it('should inject download bar element into DOM once download is initiated', function () {
it('should inject download bar element into DOM once download is initiated', function() {
electron.ipcRenderer.send('downloadCompleted', { _id: '12345', fileName: 'test', total: 100 });
expect(document.getElementsByClassName('text-cutoff')[0].innerHTML).toBe('test');
expect(document.getElementById('per').innerHTML).toBe('100 Downloaded');
});
it('should inject multiple download items during multiple downloads', function () {
it('should inject multiple download items during multiple downloads', function() {
electron.ipcRenderer.send('downloadCompleted', { _id: '12345', fileName: 'test', total: 100 });
electron.ipcRenderer.send('downloadCompleted', { _id: '67890', fileName: 'test1', total: 200 });
@ -31,23 +31,23 @@ describe('download manager', function () {
});
describe('Download Manager to initiate footer', function () {
beforeEach(function () {
describe('Download Manager to initiate footer', function() {
beforeEach(function() {
global.document.body.innerHTML =
'<div id="footer" class="hidden">' +
'<div id="download-manager-footer">' +
'<div id="download-main">' +
'</div>' +
'</div>' +
'<div id="download-manager-footer">' +
'<div id="download-main">' +
'</div>' +
'</div>' +
'</div>';
});
it('should inject dom element once download is completed', function () {
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 () {
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');
@ -59,19 +59,19 @@ describe('download manager', function () {
});
describe('Download Manager to initiate footer', function () {
describe('Download Manager to initiate footer', function() {
beforeEach(function () {
beforeEach(function() {
global.document.body.innerHTML =
'<div id="footer" class="hidden">' +
'<div id="download-manager-footer">' +
'<div id="download-main">' +
'</div>' +
'</div>' +
'<div id="download-manager-footer">' +
'<div id="download-main">' +
'</div>' +
'</div>' +
'</div>';
});
it('should inject ul element if not found', function () {
it('should inject ul element if not found', function() {
electron.ipcRenderer.send('downloadProgress');

View File

@ -1,10 +1,9 @@
const { ScreenSnippet, readResult } = require('../js/screenSnippet/ScreenSnippet.js');
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 { isMac } = require('../js/utils/misc.js');
const snippetBase64 = require('./fixtures/snippet/snippet-base64.js');
@ -27,7 +26,7 @@ function mockedExecFile(util, args, doneCallback) {
}
function copyTestFile(destFile, done) {
const testfile = path.join(__dirname ,
const testfile = path.join(__dirname,
'fixtures/snippet/ScreenSnippet.jpeg');
let reader = fs.createReadStream(testfile);
@ -43,7 +42,7 @@ function copyTestFile(destFile, done) {
function createTestFile(done) {
let tmpDir = os.tmpdir();
const testFileName = path.join(tmpDir,
'ScreenSnippet-' + Date.now() + '.jpeg');
'ScreenSnippet-' + Date.now() + '.jpeg');
copyTestFile(testFileName, function() {
done(testFileName)
@ -65,7 +64,7 @@ describe('Tests for ScreenSnippet', function() {
expect(rsp.type).toEqual('image/jpg;base64');
expect(rsp.data).toEqual(snippetBase64);
done();
};
}
});
}
@ -104,7 +103,7 @@ describe('Tests for ScreenSnippet', function() {
// skip test for windows - until feature is supported
if (isMac) {
it('should fail if read file fails', function(done) {
var origFsReadFile = fs.readFile;
const origFsReadFile = fs.readFile;
fs.readFile = jest.fn(mockedReadFile);
@ -132,4 +131,4 @@ describe('Tests for ScreenSnippet', function() {
}
});
}
});
});

View File

@ -29,7 +29,7 @@ const ipcMain = {
ipcEmitter.on(event, cb);
},
send: function (event, args) {
var senderEvent = {
const senderEvent = {
sender: {
send: function (event, arg) {
ipcEmitter.emit(event, arg);
@ -45,16 +45,16 @@ const ipcRenderer = {
let listeners = ipcEmitter.listeners(event);
if (listeners.length > 0) {
let listener = listeners[0];
var eventArg = {};
const eventArg = {};
listener(eventArg, args);
return eventArg.returnValue;
}
return null;
},
send: function(event, args) {
var senderEvent = {
const senderEvent = {
sender: {
send: function(event, arg) {
send: function (event, arg) {
ipcEmitter.emit(event, arg);
}
}

View File

@ -5,26 +5,24 @@ let activityDetection;
describe('Tests for Activity Detection', function() {
var originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;
const originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
beforeAll(function (done) {
childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function (err) {
activityDetection = require('../js/activityDetection/activityDetection.js');
beforeAll(function(done) {
childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) {
activityDetection = require('../js/activityDetection');
activityDetection.setActivityWindow(900000, electron.ipcRenderer);
done();
});
});
beforeEach(function () {
beforeEach(function() {
jest.clearAllMocks()
});
afterAll(function (done) {
childProcess.exec('npm run rebuild', function (err, stdout) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
done();
});
afterAll(function(done) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
done();
});
it('should return null', function() {
@ -35,17 +33,17 @@ describe('Tests for Activity Detection', function() {
});
it('should send activity event', function () {
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});
activityDetection.send({ systemIdleTime: 120000 });
expect(spy).toHaveBeenCalledWith({ systemIdleTime: 120000 });
});
it('should monitor user activity', function () {
it('should monitor user activity', function() {
activityDetection.setActivityWindow(500000, electron.ipcRenderer);
const spy = jest.spyOn(activityDetection, 'monitorUserActivity');
@ -56,7 +54,7 @@ describe('Tests for Activity Detection', function() {
});
it('should not send activity event as data is undefined', function () {
it('should not send activity event as data is undefined', function() {
const spy = jest.spyOn(activityDetection, 'send');
expect(spy).not.toBeCalled();
@ -66,4 +64,4 @@ describe('Tests for Activity Detection', function() {
});
});
});

View File

@ -62,13 +62,13 @@ describe('read/write config tests', function() {
}
function createTempUserConfig(config) {
var tmpDir = os.tmpdir();
const tmpDir = os.tmpdir();
userConfigDir = fs.mkdtempSync(path.join(tmpDir, 'config-'));
return createTempConfigFile(path.join(userConfigDir, configFileName), config);
}
function createTempGlobalConfig(config) {
var tmpDir = os.tmpdir();
const tmpDir = os.tmpdir();
globalConfigDir = path.join(fs.mkdtempSync(path.join(tmpDir, 'config-')), 'config');
fs.mkdirSync(globalConfigDir);
return createTempConfigFile(path.join(globalConfigDir, configFileName), config);
@ -80,15 +80,15 @@ describe('read/write config tests', function() {
describe('getConfigField tests', function() {
it('should fail when field not present in either user or global config', function() {
var userConfig = {
const userConfig = {
url: 'something'
}
};
createTempUserConfig(userConfig);
var globalConfig = {
const globalConfig = {
url: 'something-else'
}
};
createTempGlobalConfig(globalConfig);
@ -98,9 +98,9 @@ describe('read/write config tests', function() {
});
it('should succeed when field only present in user config', function() {
var userConfig = {
const userConfig = {
url: 'something'
}
};
createTempUserConfig(userConfig);
@ -110,9 +110,9 @@ describe('read/write config tests', function() {
});
it('should succeed when field only present in global config', function() {
var globalConfig = {
const globalConfig = {
url: 'something-else'
}
};
createTempGlobalConfig(globalConfig);
@ -122,15 +122,15 @@ describe('read/write config tests', function() {
});
it('should succeed and return user config field when value is in both', function() {
var userConfig = {
const userConfig = {
url: 'something'
}
};
createTempUserConfig(userConfig);
var globalConfig = {
const globalConfig = {
url: 'something-else'
}
};
createTempGlobalConfig(globalConfig);
@ -140,7 +140,7 @@ describe('read/write config tests', function() {
});
it('should fail when global config path is invalid', function() {
var globalConfig = {
const globalConfig = {
url: 'something-else'
};
createTempGlobalConfig(globalConfig);
@ -155,7 +155,7 @@ describe('read/write config tests', function() {
});
it('should fail when user config path is invalid', function() {
var userConfig = {
const userConfig = {
url: 'something'
};
createTempUserConfig(userConfig);
@ -170,12 +170,12 @@ describe('read/write config tests', function() {
});
it('should read cached user config value rather than reading file from disk again', function(done) {
var userConfig = {
const userConfig = {
url: 'qa4.symphony.com'
};
createTempUserConfig(userConfig);
var userConfig2 = {
const userConfig2 = {
url: 'qa5.symphony.com'
};
@ -193,12 +193,12 @@ describe('read/write config tests', function() {
});
it('should read cache global config value rather than reading file from disk again', function(done) {
var globalConfig = {
const globalConfig = {
url: 'qa8.symphony.com'
};
createTempGlobalConfig(globalConfig);
var globalConfig2 = {
const globalConfig2 = {
url: 'qa9.symphony.com'
};
@ -220,7 +220,7 @@ describe('read/write config tests', function() {
describe('updateConfigField tests', function() {
it('should succeed and overwrite existing field', function() {
var userConfig = {
const userConfig = {
url: 'something'
};
@ -235,7 +235,7 @@ describe('read/write config tests', function() {
});
it('should succeed and add new field', function() {
var userConfig = {
const userConfig = {
url: 'something'
};
@ -252,7 +252,7 @@ describe('read/write config tests', function() {
it('should fail to update if invalid field name', function() {
var userConfig = {
const userConfig = {
url: 'something'
};
@ -277,7 +277,7 @@ describe('read/write config tests', function() {
it('should throw error if fieldName is not defined', function() {
var userConfig = {
const userConfig = {
url: 'something'
};
@ -291,7 +291,7 @@ describe('read/write config tests', function() {
it('should throw error if config is not defined', function() {
var userConfig = {
const userConfig = {
url: 'something'
};

View File

@ -1,5 +1,5 @@
// base64 conversion of file ScreenSnippet.jpeg
const base64ScreenSnippet =
"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";
module.exports = base64ScreenSnippet
module.exports = base64ScreenSnippet;

View File

@ -0,0 +1,182 @@
const Application = require('./spectronSetup');
const {isMac} = require('../../js/utils/misc.js');
const childProcess = require('child_process');
let app = new Application({});
let robot;
let configPath;
let mIsAlwaysOnTop;
describe('Tests for Always on top', () => {
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
beforeAll((done) => {
childProcess.exec(`npm rebuild robotjs --target=${process.version} --build-from-source`, function () {
robot = require('robotjs');
return app.startApplication().then((startedApp) => {
app = startedApp;
getConfigPath().then((config) => {
configPath = config;
done();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
});
function getConfigPath() {
return new Promise(function (resolve, reject) {
app.client.addCommand('getUserDataPath', function () {
return this.execute(function () {
return require('electron').remote.app.getPath('userData');
})
});
app.client.getUserDataPath().then((path) => {
resolve(path.value + '/Symphony.config')
}).catch((err) => {
reject(err);
});
});
}
afterAll((done) => {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
app.stop().then(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
done();
}).catch((err) => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
expect(err).toBeNull();
done();
});
}
});
it('should launch the app', (done) => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
done();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check window count', () => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check browser window visibility', () => {
return app.browserWindow.isVisible().then((isVisible) => {
expect(isVisible).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should bring the app to front in windows', (done) => {
if (!isMac) {
app.browserWindow.focus();
app.browserWindow.restore();
app.browserWindow.setAlwaysOnTop(true).then(() => {
app.browserWindow.isAlwaysOnTop().then((isOnTop) => {
app.browserWindow.getBounds().then((bounds) => {
robot.setMouseDelay(200);
app.browserWindow.restore().then(() => {
let x = bounds.x + 95;
let y = bounds.y + 35;
robot.moveMouseSmooth(x, y);
robot.mouseClick();
robot.setKeyboardDelay(200);
for (let i = 0; i < 4
; i++) {
robot.keyTap('down');
}
robot.keyTap('enter');
expect(isOnTop).toBeTruthy();
done();
})
});
});
}).catch((err) => {
expect(err).toBeNull();
});
} else {
done();
}
});
it('should check is always on top', () => {
return Application.readConfig(configPath).then((userData) => {
return app.browserWindow.isAlwaysOnTop().then((isAlwaysOnTop) => {
mIsAlwaysOnTop = isAlwaysOnTop;
if (userData.alwaysOnTop) {
expect(isAlwaysOnTop).toBeTruthy();
} else {
expect(isAlwaysOnTop).toBeFalsy();
}
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should toggle the always on top property to true', (done) => {
if (isMac) {
robot.setMouseDelay(200);
robot.moveMouse(190, 0);
robot.mouseClick();
for (let i = 0; i < 8; i++) {
robot.keyTap('down');
}
robot.keyTap('enter');
done();
} else {
app.browserWindow.getBounds().then((bounds) => {
app.browserWindow.focus();
robot.setMouseDelay(200);
app.browserWindow.restore().then(() => {
let x = bounds.x + 95;
let y = bounds.y + 35;
robot.moveMouseSmooth(x, y);
robot.mouseClick();
for (let i = 0; i < 4; i++) {
robot.keyTap('down');
}
robot.keyTap('enter');
done();
});
}).catch((err) => {
expect(err).toBeNull();
});
}
});
it('should check is always on top to be true', () => {
if (!mIsAlwaysOnTop) {
return app.browserWindow.isAlwaysOnTop().then((isAlwaysOnTop) => {
expect(isAlwaysOnTop).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
} else {
return app.browserWindow.isAlwaysOnTop().then((isAlwaysOnTop) => {
expect(isAlwaysOnTop).toBeFalsy();
}).catch((err) => {
expect(err).toBeNull();
});
}
});
});

View File

@ -0,0 +1,82 @@
const Application = require('./spectronSetup');
let app = new Application({});
describe('Tests for Bring to front', () => {
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
beforeAll((done) => {
return app.startApplication().then((startedApp) => {
app = startedApp;
done();
}).catch((err) => {
expect(err).toBeNull();
});
});
afterAll((done) => {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
app.stop().then(() => {
done();
}).catch((err) => {
done();
});
}
});
it('should launch the app', (done) => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should minimize the app', () => {
return app.browserWindow.minimize().then(() => {
return app.browserWindow.isMinimized().then((isMinimized) => {
expect(isMinimized).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should not be focused', () => {
return app.browserWindow.isFocused().then((isFocused) => {
expect(isFocused).toBeFalsy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should maximize browser window', () => {
return app.browserWindow.restore().then(() => {
return app.browserWindow.isMinimized().then((isMinimized) => {
expect(isMinimized).toBeFalsy();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should be focused', () => {
return app.browserWindow.isFocused().then((isFocused) => {
expect(isFocused).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
});

View File

@ -0,0 +1,72 @@
const Application = require('./spectronSetup');
const path = require('path');
let app = new Application({});
describe('Tests for clipboard', () => {
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
beforeAll((done) => {
return app.startApplication().then((startedApp) => {
app = startedApp;
done();
});
});
afterAll((done) => {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
app.stop().then(() => {
done();
}).catch((err) => {
done();
});
}
});
it('should launch the app', (done) => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeFalsy();
});
}).catch((err) => {
expect(err).toBeFalsy();
});
});
it('should check window count', () => {
return app.client.url('file:///' + path.join(__dirname, '..', '..', 'demo/index.html'));
});
it('should set the username field', () => {
return app.client
.windowByIndex(0)
.setValue('#tag', 'Test')
.getValue('#tag').then((value) => {
expect(value === 'Test').toBeTruthy();
});
});
it('should verify electron clipboard', () => {
return app.client
.getValue('#tag').then((value) => {
return app.electron.clipboard.writeText(value)
.electron.clipboard.readText().then((clipboardText) => {
expect(clipboardText === 'Test').toBeTruthy();
});
});
});
it('should verify electron clipboard copy', () => {
return app.electron.clipboard.writeText('Testing copy')
.electron.clipboard.readText().then((clipboardText) => {
return app.client.setValue('#tag', clipboardText).getValue('#tag').then((value) => {
expect(value === 'Testing copy').toBeTruthy();
});
});
});
});

View File

@ -0,0 +1,69 @@
describe('Tests for Close', () => {
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
let app;
beforeAll((done) => {
const Application = require('./spectronSetup');
app = new Application({});
return app.startApplication().then((startedApp) => {
app = startedApp;
done();
}).catch((err) => {
expect(err).toBeNull();
});
});
afterAll((done) => {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
app.stop().then(() => {
done();
}).catch((err) => {
done();
});
} else {
done();
}
});
it('should launch the app', (done) => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check window count', () => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check browser window visibility', () => {
return app.browserWindow.isVisible().then((isVisible) => {
expect(isVisible).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should close the app', () => {
return app.stop();
});
it('should check whether the app is running', () => {
expect(app.isRunning()).toBe(false);
});
});

View File

@ -0,0 +1,139 @@
const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const Application = require('./spectronSetup');
const {isMac} = require('../../js/utils/misc');
let robot;
let configPath;
let app = new Application({});
describe('Tests for Full screen', () => {
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
beforeAll((done) => {
childProcess.exec(`npm rebuild robotjs --target=${process.version} --build-from-source`, function () {
robot = require('robotjs');
return app.startApplication().then((startedApp) => {
app = startedApp;
getConfigPath().then((config) => {
configPath = config;
done();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
});
function getConfigPath() {
return new Promise(function (resolve, reject) {
app.client.addCommand('getUserDataPath', function () {
return this.execute(function () {
return require('electron').remote.app.getPath('userData');
})
});
app.client.getUserDataPath().then((path) => {
resolve(path.value + '/Symphony.config')
}).catch((err) => {
reject(err);
});
});
}
afterAll((done) => {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
app.client.getWindowCount().then((count) => {
if (count > 0) {
app.stop().then(() => {
done();
}).catch((err) => {
done();
});
} else {
done();
}
})
} else {
done();
}
});
it('should launch the app', (done) => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check window count', () => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check browser window visibility', () => {
return app.browserWindow.isVisible().then((isVisible) => {
expect(isVisible).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should bring the app to top', () => {
app.browserWindow.focus();
return app.browserWindow.setAlwaysOnTop(true).then(() => {
return app.browserWindow.isAlwaysOnTop().then((isOnTop) => {
expect(isOnTop).toBeTruthy();
});
});
});
it('should set the app full screen and check whether it is in full screen', () => {
if (isMac) {
robot.setMouseDelay(100);
robot.moveMouseSmooth(205, 10);
robot.mouseClick();
robot.setKeyboardDelay(100);
for (let i = 0; i < 6; i++) {
robot.keyTap('down');
}
robot.keyTap('enter');
return app.browserWindow.isFullScreen().then((fullscreen) => {
expect(fullscreen).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
})
} else {
return app.browserWindow.getBounds().then((bounds) => {
robot.setMouseDelay(100);
let x = bounds.x + 200;
let y = bounds.y + 200;
robot.moveMouseSmooth(x, y);
robot.mouseClick();
robot.keyTap('f11');
return app.browserWindow.isFullScreen().then((fullscreen) => {
expect(fullscreen).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
})
});
}
});
});

View File

@ -0,0 +1,4 @@
{
"testMatch": ["**/*.spectron.js"],
"verbose": true
}

View File

@ -0,0 +1,193 @@
const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const Application = require('./spectronSetup');
const {isMac} = require('../../js/utils/misc');
let robot;
let configPath;
let app = new Application({});
describe('Tests for Minimize on Close', () => {
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
beforeAll((done) => {
childProcess.exec(`npm rebuild robotjs --target=${process.version} --build-from-source`, function () {
robot = require('robotjs');
return app.startApplication().then((startedApp) => {
app = startedApp;
getConfigPath().then((config) => {
configPath = config;
done();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
});
function getConfigPath() {
return new Promise(function (resolve, reject) {
app.client.addCommand('getUserDataPath', function () {
return this.execute(function () {
return require('electron').remote.app.getPath('userData');
})
});
app.client.getUserDataPath().then((path) => {
resolve(path.value + '/Symphony.config')
}).catch((err) => {
reject(err);
});
});
}
afterAll((done) => {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
app.client.getWindowCount().then((count) => {
if (count > 0) {
app.stop().then(() => {
done();
}).catch((err) => {
done();
});
} else {
done();
}
})
} else {
done();
}
});
it('should launch the app', (done) => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check window count', () => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check browser window visibility', () => {
return app.browserWindow.isVisible().then((isVisible) => {
expect(isVisible).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should bring the app to top', () => {
app.browserWindow.focus();
return app.browserWindow.setAlwaysOnTop(true).then(() => {
return app.browserWindow.isAlwaysOnTop().then((isOnTop) => {
expect(isOnTop).toBeTruthy();
});
});
});
it('should check whether the app is minimized', (done) => {
Application.readConfig(configPath).then((userConfig) => {
if (isMac) {
if (userConfig.minimizeOnClose) {
robot.setKeyboardDelay(100);
robot.keyToggle('w', 'down', ['command']);
robot.keyToggle('w', 'up');
robot.keyToggle('command', 'up');
app.browserWindow.isMinimized().then(function (minimized) {
expect(minimized).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
done();
});
} else {
robot.setMouseDelay(100);
robot.moveMouseSmooth(200, 10);
robot.mouseClick();
robot.setKeyboardDelay(100);
for (let i = 0; i < 9; i++) {
robot.keyTap('down');
}
robot.keyTap('enter');
robot.keyToggle('w', 'down', ['command']);
robot.keyToggle('w', 'up');
robot.keyToggle('command', 'up');
app.browserWindow.isMinimized().then(function (minimized) {
expect(minimized).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
done();
});
}
} else {
if (!userConfig.minimizeOnClose) {
app.browserWindow.getBounds().then((bounds) => {
robot.setMouseDelay(100);
let x = bounds.x + 95;
let y = bounds.y + 35;
robot.moveMouse(x, y);
robot.mouseClick();
for (let i = 0; i < 5; i++) {
robot.keyTap('down');
}
robot.keyTap('enter');
robot.keyToggle('w', 'down', ['control']);
robot.keyToggle('w', 'up');
robot.keyToggle('control', 'up');
app.browserWindow.isMinimized().then(function (minimized) {
expect(minimized).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
done();
});
});
} else {
app.browserWindow.getBounds().then((bounds) => {
robot.setMouseDelay(100);
let x = bounds.x + 200;
let y = bounds.y + 200;
robot.moveMouseSmooth(x, y);
robot.mouseClick();
robot.keyToggle('w', 'down', ['control']);
robot.keyToggle('w', 'up');
robot.keyToggle('control', 'up');
app.browserWindow.isMinimized().then(function (minimized) {
expect(minimized).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
done();
});
});
}
}
}).catch((err) => {
expect(err).toBeNull();
done();
})
});
});

View File

@ -0,0 +1,200 @@
const Application = require('./spectronSetup');
const path = require('path');
const {isMac} = require('../../js/utils/misc');
let app = new Application({});
describe('Tests for Notification position', () => {
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
beforeAll((done) => {
return app.startApplication().then((startedApp) => {
app = startedApp;
done();
}).catch((err) => {
expect(err).toBeNull();
});
});
afterAll((done) => {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
app.stop().then(() => {
done();
}).catch((err) => {
done();
});
}
});
it('should launch the app', (done) => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should load demo html page', () => {
return app.client.url('file:///' + path.join(__dirname, '..', '..', 'demo/index.html'));
});
it('should load demo html', () => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getTitle().then((title) => {
expect(title === '').toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should open notification configure window', () => {
return app.client
.click('#open-config-win')
.windowByIndex(1)
.click('#upper-left')
.click('#ok-button')
.windowByIndex(0)
.click('#notf')
.windowByIndex(1)
});
it('should check notification position', () => {
return app.browserWindow.getBounds().then((bounds) => {
expect(bounds.x === 0).toBeTruthy();
if (isMac) {
expect(bounds.y > 0).toBeTruthy();
} else {
expect(bounds.y === 0).toBeTruthy();
}
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should change the window', () => {
return app.client.windowByIndex(0).then(() => {
return app.browserWindow.getTitle().then((title) => {
expect(title === 'Symphony | Secure Seamless Communication').toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should change notification position to lower-right', () => {
return app.client
.click('#open-config-win')
.windowByIndex(2)
.click('#lower-right')
.click('#ok-button')
.windowByIndex(0)
.click('#notf')
.windowByIndex(1).then(() => {
return app.browserWindow.getTitle().then((title) => {
expect(title === 'Electron').toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check notification position and equal to lower-right', () => {
return app.browserWindow.getBounds().then((bounds) => {
expect(bounds.x > 0).toBeTruthy();
expect(bounds.y > 0).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should change the window', () => {
return app.client.windowByIndex(0).then(() => {
return app.browserWindow.getTitle().then((title) => {
expect(title === 'Symphony | Secure Seamless Communication').toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should change notification position to upper-right', () => {
return app.client
.click('#open-config-win')
.windowByIndex(2)
.click('#upper-right')
.click('#ok-button')
.windowByIndex(0)
.click('#notf')
.windowByIndex(1).then(() => {
return app.browserWindow.getTitle().then((title) => {
expect(title === 'Electron').toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check notification position and equal to upper-right', () => {
return app.browserWindow.getBounds().then((bounds) => {
expect(bounds.x > 0).toBeTruthy();
if (isMac) {
expect(bounds.y > 0).toBeTruthy();
} else {
expect(bounds.y === 0).toBeTruthy();
}
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should change the window to main', () => {
return app.client.windowByIndex(0).then(() => {
return app.browserWindow.getTitle().then((title) => {
expect(title === 'Symphony | Secure Seamless Communication').toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should open notification and close', () => {
return app.client
.windowByIndex(0)
.click('#notf')
.getWindowCount().then((count) => {
expect(count === 3).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
})
.windowByIndex(1).then(() => {
return app.browserWindow.getTitle().then((title) => {
expect(title === 'Electron').toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
});

View File

@ -0,0 +1,71 @@
const Application = require('spectron').Application;
const path = require('path');
const fs = require('fs');
const {isMac} = require('../../js/utils/misc');
const ncp = require('ncp').ncp;
class App {
constructor(options) {
this.options = options;
if (!this.options.path) {
this.options.path = App.getAppPath();
this.options.args = [path.join(__dirname, '..', '..', 'js/main.js')];
}
App.copyConfigPath();
this.app = new Application(this.options);
}
startApplication() {
return this.app.start().then((app) => {
return app;
}).catch((err) => {
});
}
static getAppPath() {
let electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', 'electron');
if (process.platform === 'win32') {
electronPath += '.cmd';
}
return electronPath
}
static getTimeOut() {
return 90000
}
static readConfig(configPath) {
return new Promise(function (resolve, reject) {
fs.readFile(configPath, function (err, data) {
if (err) {
reject(err);
}
resolve(JSON.parse(data));
});
});
}
static copyConfigPath() {
if (isMac) {
ncp('config', 'node_modules/electron/dist/Electron.app/Contents/config', function (err) {
if (err) {
throw(err);
}
});
} else {
ncp('config', 'node_modules/electron/dist/config', function (err) {
if (err) {
throw(err);
}
});
}
}
}
module.exports = App;

View File

@ -0,0 +1,121 @@
const Application = require('./spectronSetup');
const path = require('path');
const {isMac} = require('../../js/utils/misc.js');
const childProcess = require('child_process');
let app = new Application({});
let robot;
describe('Tests for spellChecker', () => {
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = Application.getTimeOut();
beforeAll((done) => {
childProcess.exec(`npm rebuild robotjs --target=${process.version} --build-from-source`, function () {
robot = require('robotjs');
app.startApplication().then((startedApp) => {
app = startedApp;
done();
});
});
});
afterAll((done) => {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
app.stop().then(() => {
done();
}).catch((err) => {
done();
});
}
});
it('should launch the app', (done) => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeFalsy();
done();
});
}).catch((err) => {
expect(err).toBeFalsy();
done();
});
});
it('should check window count', () => {
return app.client.url('file:///' + path.join(__dirname, '..', '..', 'demo/index.html'));
});
it('should set the misspelled word', () => {
return app.client
.windowByIndex(0)
.setValue('#tag', 'comming ')
.getValue('#tag').then((value) => {
expect(value === 'comming ').toBeTruthy();
});
});
it('should bring the app to front in windows', (done) => {
if (!isMac) {
app.browserWindow.focus();
app.browserWindow.restore();
app.browserWindow.getBounds().then((bounds) => {
robot.setMouseDelay(100);
let x = bounds.x + 200;
let y = bounds.y + 200;
robot.moveMouseSmooth(x, y);
robot.mouseClick();
done();
});
} else {
done();
}
});
it('should invoke context menu ', (done) => {
if (isMac) {
app.browserWindow.getBounds().then((bounds) => {
let x = bounds.x + 45;
let y = bounds.y + 398;
robot.moveMouseSmooth(x, y);
robot.setMouseDelay(200);
robot.mouseClick('left', true);
robot.mouseClick('right');
robot.setKeyboardDelay(500);
robot.keyTap('down');
robot.keyTap('down');
robot.keyTap('enter');
done();
});
} else {
app.browserWindow.getBounds().then((bounds) => {
let x = bounds.x + 55;
let y = bounds.y + 430;
robot.moveMouseSmooth(x, y);
robot.setMouseDelay(200);
robot.mouseClick('left', true);
robot.mouseClick('right');
robot.setKeyboardDelay(500);
robot.keyTap('down');
robot.keyTap('down');
robot.keyTap('enter');
done();
});
}
});
it('should verify the text field', () => {
return app.client
.windowByIndex(0)
.getValue('#tag').then((value) => {
expect(value).toBe('coming ');
});
});
});

View File

@ -0,0 +1,199 @@
const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const Application = require('./spectronSetup');
const {isMac} = require('../../js/utils/misc');
let robot;
let configPath;
let app = new Application({});
describe('Tests for Zoom in and Zoom out', () => {
let originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
beforeAll((done) => {
childProcess.exec(`npm rebuild robotjs --target=${process.version} --build-from-source`, function () {
robot = require('robotjs');
return app.startApplication().then((startedApp) => {
app = startedApp;
getConfigPath().then((config) => {
configPath = config;
done();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
});
function getConfigPath() {
return new Promise(function (resolve, reject) {
app.client.addCommand('getUserDataPath', function () {
return this.execute(function () {
return require('electron').remote.app.getPath('userData');
})
});
app.client.getUserDataPath().then((path) => {
resolve(path.value + '/Symphony.config')
}).catch((err) => {
reject(err);
});
});
}
afterAll((done) => {
if (app && app.isRunning()) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
app.client.getWindowCount().then((count) => {
if (count > 0) {
app.stop().then(() => {
done();
}).catch((err) => {
done();
});
} else {
done();
}
})
} else {
done();
}
});
it('should launch the app', (done) => {
return app.client.waitUntilWindowLoaded().then(() => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
done();
}).catch((err) => {
expect(err).toBeNull();
});
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check window count', () => {
return app.client.getWindowCount().then((count) => {
expect(count === 1).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should check browser window visibility', () => {
return app.browserWindow.isVisible().then((isVisible) => {
expect(isVisible).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
});
});
it('should bring the app to top', () => {
app.browserWindow.focus();
return app.browserWindow.setAlwaysOnTop(true).then(() => {
return app.browserWindow.isAlwaysOnTop().then((isOnTop) => {
expect(isOnTop).toBeTruthy();
});
});
});
it('should zoom in the app and check whether it is zoomed in', () => {
robot.setKeyboardDelay(500);
if (isMac) {
robot.keyToggle('0', 'down', ['command']);
robot.keyToggle('0', 'up');
robot.keyToggle('command', 'up');
for (let i = 0; i < 4; i++) {
robot.keyToggle('+', 'down', ['command']);
}
robot.keyToggle('+', 'up');
robot.keyToggle('command', 'up');
return app.electron.webFrame.getZoomFactor().then((zoomFactor) => {
expect(zoomFactor > 1).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
})
} else {
return app.browserWindow.getBounds().then((bounds) => {
robot.setMouseDelay(100);
let x = bounds.x + 200;
let y = bounds.y + 200;
robot.moveMouseSmooth(x, y);
robot.mouseClick();
robot.keyToggle('0', 'down', ['control']);
robot.keyToggle('0', 'up');
robot.keyToggle('control', 'up');
for (let i = 0; i < 4; i++) {
robot.keyToggle('+', 'down', ['control', 'shift']);
}
robot.keyToggle('+', 'up');
robot.keyToggle('control', 'up');
robot.keyToggle('shift', 'up');
return app.electron.webFrame.getZoomFactor().then((zoomFactor) => {
expect(zoomFactor > 1).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
})
});
}
});
it('should zoom out the app and check whether it is zoomed out', () => {
robot.setKeyboardDelay(500);
if (isMac) {
robot.keyToggle('0', 'down', ['command']);
robot.keyToggle('0', 'up');
robot.keyToggle('command', 'up');
for (let i = 0; i < 4; i++) {
robot.keyToggle('-', 'down', ['command']);
}
robot.keyToggle('-', 'up');
robot.keyToggle('command', 'up');
return app.electron.webFrame.getZoomFactor().then((zoomFactor) => {
expect(zoomFactor < 1).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
})
} else {
return app.browserWindow.getBounds().then((bounds) => {
robot.setMouseDelay(100);
let x = bounds.x + 200;
let y = bounds.y + 200;
robot.moveMouseSmooth(x, y);
robot.mouseClick();
robot.keyToggle('0', 'down', ['control']);
robot.keyToggle('0', 'up');
robot.keyToggle('control', 'up');
for (let i = 0; i < 4; i++) {
robot.keyToggle('-', 'down', ['control']);
}
robot.keyToggle('-', 'up');
robot.keyToggle('control', 'up');
return app.electron.webFrame.getZoomFactor().then((zoomFactor) => {
expect(zoomFactor < 1).toBeTruthy();
}).catch((err) => {
expect(err).toBeNull();
})
});
}
});
});

View File

@ -2,27 +2,27 @@ const getCmdLineArg = require('../../js/utils/getCmdLineArg.js');
describe('getCmdLineArg tests', function() {
it('should return no exact match', function() {
var result = getCmdLineArg([ 'hello.exe', '--arg1', '--arg2'], '--arg', true);
const result = getCmdLineArg(['hello.exe', '--arg1', '--arg2'], '--arg', true);
expect(result).toBe(null);
});
it('should return exact match only', function() {
var result = getCmdLineArg([ 'hello.exe', '--arg1', '--arg2'], '--arg2', true);
const result = getCmdLineArg(['hello.exe', '--arg1', '--arg2'], '--arg2', true);
expect(result).toBe('--arg2');
});
it('should return starts with match', function() {
var result = getCmdLineArg([ 'hello.exe', '--hello=test', '--arg2'], '--hello=');
const result = getCmdLineArg(['hello.exe', '--hello=test', '--arg2'], '--hello=', false);
expect(result).toBe('--hello=test');
});
it('should return no match for starts with', function() {
var result = getCmdLineArg([ 'hello.exe', '--hello=test', '--arg2'], '--help=');
const result = getCmdLineArg(['hello.exe', '--hello=test', '--arg2'], '--help=', false);
expect(result).toBe(null);
});
it('should return no match invalid argv given', function() {
var result = getCmdLineArg('invalid argv', '--help=');
const result = getCmdLineArg('invalid argv', '--help=', false);
expect(result).toBe(null);
});
});

View File

@ -2,9 +2,9 @@ const getGuid = require('../../js/utils/getGuid.js');
describe('guid tests', function() {
it('should have valid length', function() {
var guid = getGuid();
const guid = getGuid();
expect(guid.length).toBe(36);
var parts = guid.split('-');
const parts = guid.split('-');
expect(parts.length).toBe(5);
expect(parts[0].length).toBe(8);
expect(parts[1].length).toBe(4);
@ -14,9 +14,9 @@ describe('guid tests', function() {
});
it('should only contains hex chars', function() {
for(var i = 0; i < 100; i++) {
var guid = getGuid();
var parts = guid.split('-');
for(let i = 0; i < 100; i++) {
const guid = getGuid();
const parts = guid.split('-');
parts.forEach(function(part) {
expect(/^([A-Fa-f0-9]{2})+$/.test(part)).toBe(true);
});

View File

@ -33,12 +33,12 @@ describe('isInDisplayBounds should', function() {
}
}]);
var rect = {
const rect = {
x: 1,
y: 1,
width: 90,
height: 90
}
};
expect(isInDisplayBounds(rect)).toBe(true);
});
@ -53,12 +53,12 @@ describe('isInDisplayBounds should', function() {
}
}]);
var rect = {
const rect = {
x: 0,
y: 0,
width: 100,
height: 100
}
};
expect(isInDisplayBounds(rect)).toBe(true);
});
@ -73,12 +73,12 @@ describe('isInDisplayBounds should', function() {
}
}]);
var rect = {
const rect = {
x: 0,
y: 0,
width: 100,
height: 100
}
};
expect(isInDisplayBounds(rect)).toBe(true);
});
@ -100,15 +100,15 @@ describe('isInDisplayBounds should', function() {
x: 100,
y: 0
}
}
};
createMockDisplay([ display1, display2 ]);
var rect = {
const rect = {
x: 110,
y: 0,
width: 50,
height: 50
}
};
expect(isInDisplayBounds(rect)).toBe(true);
});
@ -123,12 +123,12 @@ describe('isInDisplayBounds should', function() {
}
}]);
var rect = {
const rect = {
x: 0,
y: 0,
width: 100,
height: 101
}
};
expect(isInDisplayBounds(rect)).toBe(false);
});
@ -149,15 +149,15 @@ describe('isInDisplayBounds should', function() {
x: 100,
y: 0
}
}
};
createMockDisplay([ display1, display2 ]);
var rect = {
const rect = {
x: 50,
y: 50,
width: 75,
height: 25
}
};
expect(isInDisplayBounds(rect)).toBe(false);
});

View File

@ -1,7 +1,7 @@
const throttle = require('../../js/utils/throttle.js');
describe('throttle tests', function() {
var now, origNow;
let now, origNow;
beforeEach(function() {
origNow = Date.now;
// mock date func