1
0
Fork 0
mirror of https://github.com/iNavFlight/inav-configurator.git synced 2025-07-13 11:29:53 +03:00

Merge pull request #1961 from Scavanger/Electron

[WIP] [For discussion] Migrate from nw.js to Electron
This commit is contained in:
Paweł Spychalski 2024-04-23 21:42:20 +02:00 committed by GitHub
commit 82ede9c7fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
170 changed files with 18075 additions and 13033 deletions

10
.gitignore vendored
View file

@ -5,14 +5,6 @@ npm-debug.log
.idea/
npm-debug.log
inav-configurator.iml
# Generated scripts and styles
/build
# Used by nw-builder to download runtimes
/cache
# Where we put the final app directory structure
/dist
# Path where the NW.js apps get built
/apps
/.vscode/
/out
.eslintrc.json
/.project

21
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,21 @@
{
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Configurator",
"runtimeExecutable": "${workspaceFolder}/node_modules/@electron-forge/cli/script/vscode.sh",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/@electron-forge/cli/script/vscode.cmd",
},
"args": ["--inspect"],
"env": {
"NODE_ENV": "development",
"NODE_PATH": "${workspaceFolder}/js/"
},
"cwd": "${workspaceFolder}",
"console": "integratedTerminal"
}
]
}

View file

@ -18,14 +18,6 @@ everything, the hardware is not working, or you have any other _support_ problem
* [RC Groups Support](https://www.rcgroups.com/forums/showthread.php?2495732-Cleanflight-iNav-(navigation-rewrite)-project)
* [INAV Official on Telegram](https://t.me/INAVFlight)
## INAV Configurator starts minimized, what should I do?
You have to remove the `C:\Users%Your_UserName%\AppData\Local\inav-configurator` folder and all its content.
[https://www.youtube.com/watch?v=XMoULyiFDp4](https://www.youtube.com/watch?v=XMoULyiFDp4)
Alternatively, on Windows with PowerShell, you can use the `post_install_cleanup.ps1` script that will do the cleaning. (thank you, James Cherrill)
## Installation
Depending on the target operating system, _INAV Configurator_ is distributed as a _standalone_ application or Chrome App.
@ -33,10 +25,12 @@ Depending on the target operating system, _INAV Configurator_ is distributed as
### Windows
1. Visit [release page](https://github.com/iNavFlight/inav-configurator/releases)
1. Download Configurator for Windows platform (win32 or win64 is present)
1. Extract ZIP archive
1. Run the INAV Configurator app from the unpacked folder
1. Configurator is not signed, so you have to allow Windows to run untrusted applications. There might be a monit for it during the first run
2. Download Configurator for Windows platform (win32 or win64 is present)
3. Install
* Extract ZIP archive and run the INAV Configurator app from the unpacked folder
* OR just use the setup program `INAV Configurator.msi`
4. Configurator is not signed, so you have to allow Windows to run untrusted applications. There might be a monit for it during the first run
### Linux
@ -44,12 +38,12 @@ Depending on the target operating system, _INAV Configurator_ is distributed as
2. Download Configurator for Linux platform (linux32 and linux64 are present)
* **.rpm** is the Fedora installation file. Just download and install using `sudo dnf localinstall /path/to/INAV-Configurator_linux64-x.y.z-x86_64.rpm` or open it with a package manager (e.g. via Files)
* **.deb** is the Debian/Ubuntu installation file. Just download and install using `sudo apt install /path/to/INAV-Configurator_linux64_x.y.z.deb` or open it with a package manager (e.g. via the File Manager)
* **.tar.gz** is a universal archive. Download and continue with these instructions to install
3. Change to the directory containing the downloaded **tar.gz** file
* **.zip** is a universal archive. Download and continue with these instructions to install
3. Change to the directory containing the downloaded **zip** file
4. download [this](https://raw.githubusercontent.com/iNavFlight/inav-configurator/master/assets/linux/inav-configurator.desktop) file to the same directory. Its filename should be `inav-configurator.desktop`.
5. Extract **tar.gz** archive
5. Extract **zip** archive
```
tar -C /tmp/ -xf INAV-Configurator_linuxNN_x.y.z.tar.gz
unzip INAV-Configurator_linuxNN_x.y.z.tar.gz -d /tmp/
```
**NN** is the bits of your OS. **x.y.z** is the INAV Configurator version number.
@ -73,26 +67,15 @@ sudo mv inav-configurator.desktop /usr/share/applications/
```
10. Make the following files executable:
* inav-configurator `chmod +x /opt/inav/inav-configurator/inav-configurator`
* (5.0.0+) chrome_crashpad_handler `chmod +x /opt/inav/inav-configurator/chrome_crashpad_handler`
11. Run the INAV Configurator app from the unpacked folder `/opt/inav/inav-configurator/inav-configurator`
#### Notes
On some Linux distros, you may be missing `libatomic` and/or `NW.JS` (especially `libnode.so`) dependencies. If so, please install `libatomic` using your distro's package manager, e.g:
* Arch Linux: `sudo pacman -S --needed libatomic_ops`
* Debian / Ubuntu: `sudo apt install libatomic1`
* Fedora: `sudo dnf install libatomic`
1. Don't forget to add your user to the dialout group "sudo usermod -aG dialout YOUR_USERNAME" for serial access
2. If you have 3D model animation problems, enable "Override software rendering list" in Chrome flags chrome://flags/#ignore-gpu-blacklist
### Mac
1. Visit [release page](https://github.com/iNavFlight/inav-configurator/releases)
1. Download Configurator for the Mac platform
1. Extract ZIP archive
1. Run INAV Configurator
2. Download Configurator for the Mac platform
3. Install
* Extract ZIP archive and run INAV Configurator
* OR use the DMG package for installation
## Building and running INAV Configurator locally (for development)
@ -100,30 +83,33 @@ For local development, the **node.js** build system is used.
1. Install node.js
1. From the project folder run `npm install`
1. To build the JS and CSS files and start the configurator:
- With NW.js: Run `npm start`.
- With Chrome: Run `npm run gulp`. Then open `chrome://extensions`, enable
the `Developer mode`, click on the `Load unpacked extension...` button, and select the `inav-configurator` directory.
1. To build the and start the configurator:
- Run `npm start`.
Other tasks are also defined in `gulpfile.js`. To run a task, use `node ./node_modules/gulp/bin/gulp.js task-name`. Available ones are:
To build the App run `npm run make` to build for your platform.
- **build**: Generate JS and CSS output files used by the configurator from their sources. It must be run whenever changes are made to any `.js` or `.css` files in order to have those changes appear
in the configurator. If new files are added, they must be included in `gulpfile.js`. See the comments at the top of `gulpfile.js` to learn how to do so. See also the `watch` task.
- **watch**: Watch JS and CSS sources for changes and run the `build` task whenever they're edited.
- **dist**: Create a distribution of the app (valid for packaging both as a Chrome app or NW.js app)
in the `./dist/` directory.
- **release**: Create NW.js apps for each supported platform (win32, osx64 and linux64) in the `./apps`
directory. Running this task on macOS or Linux requires Wine since it's needed to set the icon
for the Windows app. If you don't have Wine installed, you can create a release by running the **release-only-Linux** task.
<br>`--installer` argument can be added to build installers for a particular OS. NOTE: MacOS Installer can be built with MacOS only.
Options:
* Architecture: --arch - Allowed values are: "ia32", "x64", "armv7l", "arm64", "universal", or "mips64el".
To build a specific release, use the command `release --platform="win64"` for example.
See [Electron Forge CLI Documentation](https://www.electronforge.io/cli#options-2) for details
To build the setup program for windows, you have to install [WiX Toolset V3](https://github.com/wixtoolset/wix3/releases) and add the `bin` folder to you `PATH`, e.g.
```C:\Program Files (x86)\WiX Toolset v3.14\bin```
To build deb and rpm packages for Linux, you have to install the following packages:
- Ubuntu/Debian: `dpkg, fakeroot, rpmbuild, build-essential, libudev-dev`
- OpenSuse/Fedora: `dpkg, fakeroot, rpmbuild, systemd-devel, devel-basis (zypper install -t pattern devel_basis), zip`
Example (note the double -- ):
``` npm run make -- --arch="x64" ```
### Running with debug | Inspector
To be able to open Inspector, you will need SDK flavours of NW.js
To be able to open Inspector, set envorinment variable `NODE_ENV` to `develpoment` or set the flag directly when run `npm start`:
`npm install nw@0.61.0 --nwjs_build_type=sdk`
```NODE_ENV=development npm start```
Or use vscode and start a debug session `Debug Configurator` (Just hit F5!)
## Different map providers

View file

@ -1 +0,0 @@
theme: jekyll-theme-slate

View file

@ -2,7 +2,7 @@
Name=INAV Configurator
Comment=Crossplatform configuration tool for the INAV flight control system
Exec=/opt/inav/inav-configurator/inav-configurator
Icon=/opt/inav/inav-configurator/icon/inav_icon_128.png
Icon=/opt/inav/inav-configurator/resources/app/assets/linux/icon/inav_icon_128.png
Terminal=false
Type=Application
Categories=Utility

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
assets/windows/banner.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View file

@ -1,169 +0,0 @@
; ------------------------------------------
; Installer for INAV
; ------------------------------------------
; It receives from the command line with /D the parameters:
; version
; archName
; archAllowed
; archInstallIn64bit
; sourceFolder
; targetFolder
#define ApplicationName "INAV Configurator"
#define CompanyName "The INAV open source project"
#define CompanyUrl "https://github.com/iNavFlight/inav"
#define ExecutableFileName "inav-configurator.exe"
#define GroupName "INAV"
#define InstallerFileName "INAV-Configurator_" + archName + "_" + version
#define SourcePath "..\..\" + sourceFolder + "\inav-configurator\" + archName
#define TargetFolderName "INAV-Configurator"
#define UpdatesUrl "https://github.com/iNavFlight/inav-configurator/releases"
[CustomMessages]
AppName=inav-configurator
LaunchProgram=Start {#ApplicationName}
[Files]
Source: "{#SourcePath}\*"; DestDir: "{app}"; Flags: recursesubdirs
[Icons]
; Programs group
Name: "{group}\{#ApplicationName}"; Filename: "{app}\{#ExecutableFileName}";
; Desktop icon
Name: "{autodesktop}\{#ApplicationName}"; Filename: "{app}\{#ExecutableFileName}";
; Non admin users, uninstall icon
Name: "{group}\Uninstall {#ApplicationName}"; Filename: "{uninstallexe}"; Check: not IsAdminInstallMode
[Languages]
; English default, it must be first
Name: "en"; MessagesFile: "compiler:Default.isl"
; Official languages
;Name: "ca"; MessagesFile: "compiler:Languages\Catalan.isl"
;Name: "da"; MessagesFile: "compiler:Languages\Danish.isl"
;Name: "de"; MessagesFile: "compiler:Languages\German.isl"
;Name: "es"; MessagesFile: "compiler:Languages\Spanish.isl"
;Name: "fr"; MessagesFile: "compiler:Languages\French.isl"
;Name: "it"; MessagesFile: "compiler:Languages\Italian.isl"
;Name: "ja"; MessagesFile: "compiler:Languages\Japanese.isl"
;Name: "nl"; MessagesFile: "compiler:Languages\Dutch.isl"
;Name: "pt"; MessagesFile: "compiler:Languages\Portuguese.isl"
;Name: "pl"; MessagesFile: "compiler:Languages\Polish.isl"
;Name: "ru"; MessagesFile: "compiler:Languages\Russian.isl"
; Not official. Sometimes not updated to latest version (strings missing)
;Name: "ga"; MessagesFile: "unofficial_inno_languages\Galician.isl"
;Name: "eu"; MessagesFile: "unofficial_inno_languages\Basque.isl"
;Name: "hr"; MessagesFile: "unofficial_inno_languages\Croatian.isl"
;Name: "hu"; MessagesFile: "unofficial_inno_languages\Hungarian.isl"
;Name: "id"; MessagesFile: "unofficial_inno_languages\Indonesian.isl"
;Name: "ko"; MessagesFile: "unofficial_inno_languages\Korean.isl"
;Name: "lv"; MessagesFile: "unofficial_inno_languages\Latvian.isl"
;Name: "sv"; MessagesFile: "unofficial_inno_languages\Swedish.isl"
;Name: "zh_CN"; MessagesFile: "unofficial_inno_languages\ChineseSimplified.isl"
;Name: "zh_TW"; MessagesFile: "unofficial_inno_languages\ChineseTraditional.isl"
; Not available
; pt_BR (Portuguese Brasileiro)
[Run]
; Add a checkbox to start the app after installed
Filename: {app}\{cm:AppName}.exe; Description: {cm:LaunchProgram,{cm:AppName}}; Flags: nowait postinstall skipifsilent
[Setup]
AppId=2e5662ca-1fb3-8f1e-a7e1-e390add7a19d
AppName={#ApplicationName}
AppPublisher={#CompanyName}
AppPublisherURL={#CompanyUrl}
AppUpdatesURL={#UpdatesUrl}
AppVersion={#version}
ArchitecturesAllowed={#archAllowed}
ArchitecturesInstallIn64BitMode={#archInstallIn64bit}
Compression=lzma2
DefaultDirName={autopf}\{#GroupName}\{#TargetFolderName}
DefaultGroupName={#GroupName}\{#ApplicationName}
LicenseFile=..\..\LICENSE
MinVersion=6.2
OutputBaseFilename={#InstallerFileName}
OutputDir=..\..\{#targetFolder}\
PrivilegesRequiredOverridesAllowed=commandline dialog
SetupIconFile=inav_installer_icon.ico
ShowLanguageDialog=yes
SolidCompression=yes
UninstallDisplayIcon={app}\{#ExecutableFileName}
UninstallDisplayName={#ApplicationName}
WizardImageFile=inav_installer.bmp
WizardSmallImageFile=inav_installer_small.bmp
WizardStyle=modern
[Code]
function GetOldNsisUninstallerPath(): String;
var
RegKey: String;
begin
Result := '';
// Look into the different registry entries: win32, win64 and without user rights
if not RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\INAV Configurator', 'UninstallString', Result) then
begin
if not RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\INAV Configurator', 'UninstallString', Result) then
begin
RegQueryStringValue(HKCU, 'SOFTWARE\INAV\INAV Configurator', 'UninstallString', Result)
end;
end;
end;
function GetQuietUninstallerPath(): String;
var
RegKey: String;
begin
Result := '';
RegKey := Format('%s\%s_is1', ['Software\Microsoft\Windows\CurrentVersion\Uninstall', '{#emit SetupSetting("AppId")}']);
if not RegQueryStringValue(HKEY_LOCAL_MACHINE, RegKey, 'QuietUninstallString', Result) then
begin
RegQueryStringValue(HKEY_CURRENT_USER, RegKey, 'QuietUninstallString', Result);
end;
end;
function InitializeSetup(): Boolean;
var
ResultCode: Integer;
ParameterStr : String;
UninstPath : String;
begin
Result := True;
// Check if the application is already installed by the old NSIS installer, and uninstall it
UninstPath := GetOldNsisUninstallerPath();
// Found, start uninstall
if UninstPath <> '' then
begin
UninstPath := RemoveQuotes(UninstPath);
// Add this parameter to not return until uninstall finished. The drawback is that the uninstaller file is not deleted
ParameterStr := '_?=' + ExtractFilePath(UninstPath);
if Exec(UninstPath, ParameterStr, '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
// Delete the unistaller file and empty folders. Not deleting the files.
DeleteFile(UninstPath);
DelTree(ExtractFilePath(UninstPath), True, False, True);
end
else begin
Result := False;
MsgBox('Error uninstalling old Configurator ' + SysErrorMessage(ResultCode) + '.', mbError, MB_OK);
end;
end
else begin
// Search for new Inno Setup installations
UninstPath := GetQuietUninstallerPath();
if UninstPath <> '' then
begin
if not Exec('>', UninstPath, '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
Result := False;
MsgBox('Error uninstalling Configurator ' + SysErrorMessage(ResultCode) + '.', mbError, MB_OK);
end;
end;
end;
end;

162
assets/windows/wix.xml Normal file
View file

@ -0,0 +1,162 @@
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<!-- http://wixtoolset.org/documentation/manual/v3/xsd/wix/product.html -->
<Product Id="{{ProductCode}}"
UpgradeCode="{{UpgradeCode}}"
Name = "{{ApplicationName}}"
Version="{{Version}}"
Manufacturer="{{Manufacturer}}"
Language="{{Language}}">
<!-- Only run this installer on Windows 7 or up (or if it"s already installed, I guess) -->
<!-- <Condition Message="This application is only supported on Windows 7 or higher.">
<![CDATA[Installed OR (VersionNT >= 601)]]>
</Condition> -->
<!-- http://wixtoolset.org/documentation/manual/v3/xsd/wix/package.html -->
<Package InstallerVersion="405"
Compressed="yes"
Comments="Windows Installer Package"
Platform="{{Platform}}"
InstallScope="{{PackageScope}}"/>
<!-- Don't allow downgrades -->
<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A later version of this product is already installed. Setup will now exit."/>
<!-- This will hide our Uninstall entry in Apps & Features. We doing this so
we can write our own which we can better control. -->
<Property Id="ARPSYSTEMCOMPONENT" Value="1" />
<!-- While the MSI package is hidden in Apps & Features, it can still be queried
via PowerShell and other means. To differentiate we give the public entry a slightly
different name to make admins life easier. -->
<Property Id="VisibleProductName" Value="{{ApplicationName}}" />
<!-- Tells the package to install perUser or perMachine. In case of perUser, all
files will be redirected to the user profile and all registry entries to HKCU. -->
<Property Id="MSIINSTALLPERUSER" Secure="yes" Value="{{InstallPerUser}}" />
<!-- Overides the default install mode. It solves a problem where
individual packaged files that have the same version as in previous
installed App version will be deleted if the files are in use during
this upgrade. Unfortunately this causes an ICE 40 warning during linking. -->
<Property Id="REINSTALLMODE" Value="emus" />
<!-- Overrides the default reboot behavior if files are in use during the upgrade.
By default, this will be set to "ReallySuppress" to make sure no unexpected reboot will happpen.-->
<Property Id="REBOOT" Value="{{RebootMode}}" />
<!-- Installlation level to use that determines which features are installed.
see guides/enduser.md to check which Install Level maps to which feature that will
correspondingly get installed.
If not set, this will default to "2" (Main Feature, Launch On Login) -->
<Property Id="INSTALLLEVEL" Value="{{InstallLevel}}" />
<!-- Allows to customize the Windows user group that gets access rights on
the install folder in cas the auto-updater is installed. User that run the App
must be part of that user group to be able to auto-update. -->
<Property Id="UPDATERUSERGROUP" Value="Users" />
<!-- A property to define whether the auto-updater is enabled when the
feature gets installed. This way the update can be installed but also be disabled
by overwriting the default value. -->
<Property Id="AUTOUPDATEENABLED" Value="1" />
<!-- Necessary registry search to find the install path which is used by the
PurgeOnUninstall action. Since this package can be installed perUser or perMachine,
we have to look in both places. First successful search wins. -->
<Property Id="INSTALLPATH">
<RegistrySearch Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{{ProductCode}}}.msq"
Root="HKCU"
Type="raw"
Id="INSTALLPATH_REGSEARCH_HKCU"
Name="InstallPath"
Win64="{{Win64YesNo}}"/>
<RegistrySearch Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{{ProductCode}}}.msq"
Root="HKLM"
Type="raw"
Id="INSTALLPATH_REGSEARCH_HKLM"
Name="InstallPath"
Win64="{{Win64YesNo}}"/>
</Property>
<!-- Lets change the product name depending on the perUser installMode.
This way the user and admins can see in which scope the MSI was installed. -->
<SetProperty Action="SetVisibleProductName" Id="VisibleProductName" Sequence="both" Before="AppSearch" Value="{{ApplicationName}} (User)">
<![CDATA[MSIINSTALLPERUSER = "1"]]>
</SetProperty>
<!-- Again we give thee MSI generaten entry a slightly different name to help
differentiate between the public one and the invisible one. -->
<SetProperty Action="SetProductName" Id="ProductName" Sequence="both" Before="AppSearch" Value="{{ApplicationName}} (User - MSI)">
<![CDATA[MSIINSTALLPERUSER = "1"]]>
</SetProperty>
<Media Id="1" Cabinet="product.cab" EmbedCab="yes"/>
<!-- {{Icon}}-->
<!-- {{UI}} -->
<!-- Step 2: Add files and directories -->
<Directory Id="TARGETDIR" Name="SourceDir">
<!-- Installation files to %PROGRAMFILES% -->
<Directory Id="{{ProgramFilesFolder}}">
<!-- {{Directories}} -->
</Directory>
<!-- Desktop -->
<Directory Id="DesktopFolder" Name="Desktop" />
<!-- Start Menu -->
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="{{ShortcutFolderName}}"/>
</Directory>
</Directory>
<!-- Step 3: Add app to Start Menu -->
<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="ApplicationShortcut" Guid="{{ApplicationShortcutGuid}}" Win64="{{Win64YesNo}}">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="{{ShortcutName}}"
Description="{{ApplicationDescription}}"
Target="[APPLICATIONROOTDIRECTORY]{{ApplicationBinary}}.exe"
WorkingDirectory="APPLICATIONROOTDIRECTORY">
<!-- {{ShortcutProperties}} -->
</Shortcut>
<RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
<RegistryValue Root="HKCU"
Key="Software\Microsoft\{{ApplicationShortName}}"
Name="installed"
Type="integer"
Value="1"
KeyPath="yes"/>
</Component>
</DirectoryRef>
<!-- Step 4: Add app desktop shortcut -->
<DirectoryRef Id="DesktopFolder">
<Component Id="DesktopShortcut" Guid="{{DesktopShortcutGuid}}" >
<Shortcut Id="MyDesktopShortcut"
Name="{{ShortcutName}}"
Description="{{ApplicationDescription}}"
Target="[APPLICATIONROOTDIRECTORY]{{ApplicationBinary}}.exe"
WorkingDirectory="APPLICATIONROOTDIRECTORY"/>
<RegistryValue Root="HKCU"
Key="Software\Microsoft\{{ApplicationShortName}}"
Name="installed"
Type="integer"
Value="1"
KeyPath="yes" />
</Component>
</DirectoryRef>
<!-- {{AutoUpdatePermissions}} -->
<!-- Lets cleanup any files that are were not part of the initial install
via this MSI. Such as newer versions installed by the auto-updater. -->
<DirectoryRef Id="APPLICATIONROOTDIRECTORY">
<Component Id="PurgeOnUninstall" Guid="{{RandomGuid}}" Win64="{{Win64YesNo}}">
<CreateFolder/>
<util:RemoveFolderEx On="uninstall" Property="INSTALLPATH" />
</Component>
</DirectoryRef>
<Feature Id="Complete" Title="{{ApplicationName}} ({{SemanticVersion}})" Description="The complete package." Display="expand" Level="1" {{ConfigurableDirectory}}>
<!-- Step 5: Tell WiX to install the files -->
<Feature Id="MainApplication" Title="Main Application" Level="1" Description="The main components to run the applications." >
<!-- {{ComponentRefs}} -->
<ComponentRef Id="ApplicationShortcut" />
<ComponentRef Id="DesktopShortcut" />
<ComponentRef Id="PurgeOnUninstall" />
</Feature>
<!-- {{AutoLaunchFeature}} -->
<!-- {{AutoUpdateFeature}} -->
</Feature>
<!-- {{AutoRun}} -->
</Product>
</Wix>

View file

@ -1,117 +0,0 @@
/*
If an id is also specified and a window with a matching id has been shown before, the remembered bounds of the window will be used instead.
*/
'use strict';
function startApplication() {
var applicationStartTime = new Date().getTime();
chrome.app.window.create('main.html', {
id: 'main-window',
frame: 'chrome',
innerBounds: {
minWidth: 1024,
minHeight: 550
}
}, function (createdWindow) {
createdWindow.contentWindow.addEventListener('load', function () {
createdWindow.contentWindow.catch_startup_time(applicationStartTime);
});
createdWindow.onClosed.addListener(function () {
// automatically close the port when application closes
// save connectionId in separate variable before createdWindow.contentWindow is destroyed
var connectionId = createdWindow.contentWindow.CONFIGURATOR.connection.connectionId,
valid_connection = createdWindow.contentWindow.CONFIGURATOR.connectionValid,
mincommand = createdWindow.contentWindow.MISC.mincommand;
console.log("EP:" + connectionId);
if (connectionId && valid_connection) {
// code below is handmade MSP message (without pretty JS wrapper), it behaves exactly like MSP.send_message
// sending exit command just in case the cli tab was open.
// reset motors to default (mincommand)
var bufferOut = new ArrayBuffer(5),
bufView = new Uint8Array(bufferOut);
bufView[0] = 0x65; // e
bufView[1] = 0x78; // x
bufView[2] = 0x69; // i
bufView[3] = 0x74; // t
bufView[4] = 0x0D; // enter
chrome.serial.send(connectionId, bufferOut, function () { console.log('Send exit') });
setTimeout(function() {
bufferOut = new ArrayBuffer(22);
bufView = new Uint8Array(bufferOut);
var checksum = 0;
bufView[0] = 36; // $
bufView[1] = 77; // M
bufView[2] = 60; // <
bufView[3] = 16; // data length
bufView[4] = 214; // MSP_SET_MOTOR
checksum = bufView[3] ^ bufView[4];
for (var i = 0; i < 16; i += 2) {
bufView[i + 5] = mincommand & 0x00FF;
bufView[i + 6] = mincommand >> 8;
checksum ^= bufView[i + 5];
checksum ^= bufView[i + 6];
}
bufView[5 + 16] = checksum;
chrome.serial.send(connectionId, bufferOut, function (sendInfo) {
chrome.serial.disconnect(connectionId, function (result) {
console.log('SERIAL: Connection closed - ' + result);
});
});
}, 100);
} else if (connectionId) {
chrome.serial.disconnect(connectionId, function (result) {
console.log('SERIAL: Connection closed - ' + result);
});
}
});
});
}
chrome.app.runtime.onLaunched.addListener(startApplication);
chrome.runtime.onInstalled.addListener(function (details) {
if (details.reason == 'update') {
var previousVersionArr = details.previousVersion.split('.'),
currentVersionArr = chrome.runtime.getManifest().version.split('.');
// only fire up notification sequence when one of the major version numbers changed
if (currentVersionArr[0] > previousVersionArr[0] || currentVersionArr[1] > previousVersionArr[1]) {
chrome.storage.local.get('update_notify', function (result) {
if (result.update_notify === 'undefined' || result.update_notify) {
var manifest = chrome.runtime.getManifest();
var options = {
priority: 0,
type: 'basic',
title: manifest.name,
message: chrome.i18n.getMessage('notifications_app_just_updated_to_version', [manifest.version]),
iconUrl: '/images/icon_128.png',
buttons: [{'title': chrome.i18n.getMessage('notifications_click_here_to_start_app')}]
};
chrome.notifications.create('baseflight_update', options, function (notificationId) {
// empty
});
}
});
}
}
});
chrome.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) {
if (notificationId == 'baseflight_update') {
startApplication();
}
});

113
forge.config.js Normal file
View file

@ -0,0 +1,113 @@
const path = require('path');
const fs = require('fs');
module.exports = {
packagerConfig: {
executableName: "inav-configurator",
asar: false,
icon: 'images/inav',
ignore: [
"^(\/\.vscode$)",
"^(\/support$)",
".gitattributes",
".gitignore",
"3D_model_creation.md",
"LICENSE",
"MAPPROXY.md",
"package-lock.json",
"README.md",
"inav_icon_128.psd",
]
},
hooks: {
// Uniform artifact file names
postMake: async (config, makeResults) => {
makeResults.forEach(result => {
var baseName = `${result.packageJSON.productName.replace(' ', '-')}_${result.platform}_${result.arch}_${result.packageJSON.version}`;
result.artifacts.forEach(artifact => {
var artifactStr = artifact.toString();
var newPath = path.join(path.dirname(artifactStr), baseName + path.extname(artifactStr));
fs.renameSync(artifactStr, newPath);
console.log('Artifact: ' + newPath);
});
});
}
},
rebuildConfig: {},
makers: [
{
name: '@electron-forge/maker-wix',
config: {
name: "INAV Configurator",
shortName: "INAV",
exe: "inav-configurator",
description: "Configurator for the open source flight controller software INAV.",
programFilesFolderName: "inav-configurator",
shortcutFolderName: "INAV",
manufacturer: "The INAV open source project",
appUserModelId: "com.inav.configurator",
icon: path.join(__dirname, "./assets/windows/inav_installer_icon.ico"),
upgradeCode: "13606ff3-b0bc-4dde-8fac-805bc8aed2f8",
ui : {
enabled: false,
chooseDirectory: true,
images: {
background: path.join(__dirname, "./assets/windows/background.jpg"),
banner: path.join(__dirname, "./assets/windows/banner.jpg")
}
},
// Standard WiX template appends the unsightly "(Machine - WSI)" to the name, so use our own template
beforeCreate: (msiCreator) => {
return new Promise((resolve, reject) => {
fs.readFile(path.join(__dirname,"./assets/windows/wix.xml"), "utf8" , (err, content) => {
if (err) {
reject (err);
}
msiCreator.wixTemplate = content;
resolve();
});
});
}
}
},
{
name: '@electron-forge/maker-dmg',
config: {
name: "INAV Configurator",
background: "./assets/osx/dmg-background.png"
}
},
{
name: '@electron-forge/maker-zip',
platforms: ['win32', 'linux', 'darwin'],
},
{
name: '@electron-forge/maker-deb',
config: {
options: {
name: "inav-configurator",
productName: "INAV Configurator",
categories: ["Utility"],
icon: "./assets/linux/icon/inav_icon_128.png",
description: "Configurator for the open source flight controller software INAV.",
homepage: "https://github.com/inavflight/",
}
},
},
{
name: '@electron-forge/maker-rpm',
config: {
options: {
name: "inav-configurator",
productName: "INAV Configurator",
license: "GPL-3.0",
categories: ["Utility"],
icon: "./assets/linux/icon/inav_icon_128.png",
description: "Configurator for the open source flight controller software INAV.",
homepage: "https://github.com/inavflight/",
}
},
},
],
};

View file

@ -1,754 +0,0 @@
'use strict';
var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var minimist = require('minimist');
var archiver = require('archiver');
var del = require('del');
var NwBuilder = require('nw-builder');
var semver = require('semver');
var gulp = require('gulp');
var concat = require('gulp-concat');
const commandExistsSync = require('command-exists').sync;
// Each key in the *sources* variable must be an array of
// the source files that will be combined into a single
// file and stored in *outputDir*. Each key in *sources*
// must be also present in *output*, whose value indicates
// the filename for the output file which combines the
// contents of the source files.
//
// Keys must be camel cased and end with either 'Css' or
// 'Js' (e.g. someSourcesCss or someSourcesJs). For each
// key, a build task will be generated named by prepending
// 'build-' and converting the key to dash-separated words
// (e.g. someSourcesCss will generate build-some-sources-css).
//
// Tasks with names ending with '-js' will be executed by the
// build-all-js task, while the ones ending with '-css' will
// be done by build-all-css. There's also a build task which
// runs both build-all-css and build-all-js.
//
// The watch task will monitor any files mentioned in the *sources*
// variable and regenerate the corresponding output file when
// they change.
//
// See README.md for details on the other tasks.
var sources = {};
sources.css = [
'./main.css',
'./js/libraries/jquery.nouislider.min.css',
'./js/libraries/jquery.nouislider.pips.min.css',
'./js/libraries/flightindicators.css',
'./src/css/tabs/*.css',
'./src/css/opensans_webfontkit/fonts.css',
'./src/css/font-awesome/css/font-awesome.css',
'./src/css/dropdown-lists/css/style_lists.css',
'./js/libraries/switchery/switchery.css',
'./js/libraries/jbox/jBox.css',
'./node_modules/openlayers/dist/ol.css',
'./src/css/logic.css',
'./src/css/defaults_dialog.css',
'./src/css/groundstation.css',
];
sources.js = [
'./js/libraries/google-analytics-bundle.js',
'./node_modules/jquery/dist/jquery.min.js',
'./node_modules/jquery-ui-npm/jquery-ui.min.js',
'./node_modules/marked/lib/marked.js',
'./js/libraries/d3.min.js',
'./js/libraries/jquery.nouislider.all.min.js',
'./node_modules/three/build/three.min.js',
'./node_modules/three/examples/js/loaders/GLTFLoader.js',
'./node_modules/three/examples/js/controls/OrbitControls.js',
'./js/libraries/nw-dialog.js',
'./js/libraries/bundle_xml2js.js',
'./js/libraries/Projector.js',
'./js/libraries/CanvasRenderer.js',
'./js/libraries/jquery.flightindicators.js',
'./js/libraries/semver.js',
'./js/libraries/jbox/jBox.min.js',
'./js/libraries/switchery/switchery.js',
'./js/libraries/jquery.ba-throttle-debounce.js',
'./js/helpers.js',
'./node_modules/inflection/inflection.min.js',
'./node_modules/bluebird/js/browser/bluebird.min.js',
'./js/injected_methods.js',
'./js/intervals.js',
'./js/timeouts.js',
'./js/pid_controller.js',
'./js/simple_smooth_filter.js',
'./js/walking_average_filter.js',
'./js/gui.js',
'./js/serialPortHelper.js',
'./js/msp/MSPCodes.js',
'./js/msp/MSPHelper.js',
'./js/msp/MSPchainer.js',
'./js/port_handler.js',
'./js/connection/connection.js',
'./js/connection/connectionBle.js',
'./js/connection/connectionSerial.js',
'./js/connection/connectionTcp.js',
'./js/connection/connectionUdp.js',
'./js/servoMixRule.js',
'./js/motorMixRule.js',
'./js/logicCondition.js',
'./js/settings.js',
'./js/outputMapping.js',
'./js/model.js',
'./js/serial_backend.js',
'./js/data_storage.js',
'./js/fc.js',
'./js/msp.js',
'./js/protocols/stm32.js',
'./js/protocols/stm32usbdfu.js',
'./js/localization.js',
'./js/boards.js',
'./js/servoMixerRuleCollection.js',
'./js/motorMixerRuleCollection.js',
'./js/logicConditionsCollection.js',
'./js/logicConditionsStatus.js',
'./js/globalVariablesStatus.js',
'./js/programmingPid.js',
'./js/programmingPidCollection.js',
'./js/programmingPidStatus.js',
'./js/vtx.js',
'./main.js',
'./js/tabs.js',
'./tabs/*.js',
'./js/eventFrequencyAnalyzer.js',
'./js/periodicStatusUpdater.js',
'./js/serial_queue.js',
'./js/msp_balanced_interval.js',
'./tabs/advanced_tuning.js',
'./js/peripherals.js',
'./js/appUpdater.js',
'./js/feature_framework.js',
'./js/defaults_dialog.js',
'./js/safehomeCollection.js',
'./js/safehome.js',
'./js/waypointCollection.js',
'./js/waypoint.js',
'./node_modules/openlayers/dist/ol.js',
'./js/libraries/plotly-latest.min.js',
'./js/sitl.js',
'./js/CliAutoComplete.js',
'./node_modules/jquery-textcomplete/dist/jquery.textcomplete.js',
'./js/fwApproach.js',
'./js/fwApproachCollection.js',
'./js/ltmDecoder.js',
'./js/groundstation.js'
];
sources.receiverCss = [
'./src/css/tabs/receiver_msp.css',
'./src/css/opensans_webfontkit/fonts.css',
'./js/libraries/jquery.nouislider.min.css',
'./js/libraries/jquery.nouislider.pips.min.css',
];
sources.receiverJs = [
'./node_modules/jquery/dist/jquery.min.js',
'./node_modules/jquery-ui-npm/jquery-ui.min.js',
'./js/libraries/jquery.nouislider.all.min.js',
'./tabs/receiver_msp.js'
];
sources.debugTraceJs = [
'./js/debug_trace.js'
];
sources.hexParserJs = [
'./js/workers/hex_parser.js',
];
var output = {
css: 'styles.css',
js: 'script.js',
receiverCss: 'receiver-msp.css',
receiverJs: 'receiver-msp.js',
debugTraceJs: 'debug-trace.js',
hexParserJs: 'hex_parser.js',
};
var outputDir = './build/';
var distDir = './dist/';
var appsDir = './apps/';
function get_task_name(key) {
return 'build-' + key.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();});
}
function getArguments() {
return minimist(process.argv.slice(2));
}
function getPlatforms() {
const defaultPlatforms = ['win32', 'win64', 'osx64', 'linux32', 'linux64'];
const platform = getArguments().platform;
if (platform) {
if (defaultPlatforms.indexOf(platform) < 0) {
throw new Error(`Invalid platform "${platform}". Available ones are: ${defaultPlatforms}`)
}
return [platform];
}
return defaultPlatforms;
}
function execSync() {
const cmd = arguments[0];
const args = Array.prototype.slice.call(arguments, 1);
const result = child_process.spawnSync(cmd, args, {stdio: 'inherit'});
if (result.error) {
throw result.error;
}
}
// Define build tasks dynamically based on the sources
// and output variables.
var buildCssTasks = [];
var buildJsTasks = [];
(function() {
// Convers fooBarBaz to foo-bar-baz
for (var k in output) {
(function (key) {
var name = get_task_name(key);
if (name.endsWith('-css')) {
buildCssTasks.push(name);
} else if (name.endsWith('-js')) {
buildJsTasks.push(name);
} else {
throw 'Invalid task name: "' + name + '": must end with -css or -js';
}
gulp.task(name, function() {
return gulp.src(sources[key])
.pipe(concat(output[key]))
.pipe(gulp.dest(outputDir));
});
})(k);
}
})();
gulp.task('build-all-js', gulp.parallel(buildJsTasks))
gulp.task('build-all-css', gulp.parallel(buildCssTasks));
gulp.task('build', gulp.parallel('build-all-css', 'build-all-js'));
gulp.task('clean', function() { return del(['./build/**', './dist/**'], {force: true}); });
// Real work for dist task. Done in another task to call it via
// run-sequence.
gulp.task('dist-build', gulp.series('build', function() {
var distSources = [
'./package.json', // For NW.js
'./manifest.json', // For Chrome app
'./eventPage.js',
'./*.html',
'./tabs/*.html',
'./images/**/*',
'./_locales/**/*',
'./build/*',
'./src/css/font-awesome/webfonts/*',
'./src/css/opensans_webfontkit/*.{eot,svg,ttf,woff,woff2}',
'./resources/*.json',
'./resources/models/*',
'./resources/osd/analogue/*.mcm',
'./resources/motor_order/*.svg',
'./resources/sitl/windows/*',
'./resources/sitl/linux/*'
];
return gulp.src(distSources, { base: '.' })
.pipe(gulp.dest(distDir));
}));
gulp.task('dist', gulp.series('clean', 'dist-build'));
// Create app directories in ./apps
gulp.task('apps', gulp.series('dist', function(done) {
var builder = new NwBuilder({
files: './dist/**/*',
buildDir: appsDir,
platforms: getPlatforms(),
flavor: 'normal',
macIcns: './images/inav.icns',
winIco: './images/inav.ico',
version: get_nw_version(),
zip: false
});
builder.on('log', console.log);
builder.build(function (err) {
if (err) {
console.log("Error building NW apps:" + err);
done();
return;
}
// Package apps as .zip files
done();
});
}));
function get_nw_version() {
return semver.valid(semver.coerce(require('./package.json').dependencies.nw));
}
function get_release_filename_base(platform) {
return 'INAV-Configurator_' + platform;
}
function get_release_filename(platform, ext, addition = '') {
var pkg = require('./package.json');
return get_release_filename_base(platform) + addition + '_' + pkg.version + '.' + ext;
}
function build_win_zip(arch) {
return function build_win_zip_proc(done) {
var pkg = require('./package.json');
// Create ZIP
console.log(`Creating ${arch} ZIP file...`);
var src = path.join(appsDir, pkg.name, arch);
var output = fs.createWriteStream(path.join(appsDir, get_release_filename(arch, 'zip')));
var archive = archiver('zip', {
zlib: { level: 9 }
});
archive.on('warning', function(err) { throw err; });
archive.on('error', function(err) { throw err; });
archive.pipe(output);
archive.directory(src, 'INAV Configurator');
return archive.finalize();
}
}
function build_win_iss(arch) {
return function build_win_iss_proc(done) {
if (!getArguments().installer) {
done();
return null;
}
// Create Installer
console.log(`Creating ${arch} Installer...`);
const innoSetup = require('@quanle94/innosetup');
const APPS_DIR = './apps/';
const pkg = require('./package.json');
// Parameters passed to the installer script
const parameters = [];
// Extra parameters to replace inside the iss file
parameters.push(`/Dversion=${pkg.version}`);
parameters.push(`/DarchName=${arch}`);
parameters.push(`/DarchAllowed=${(arch === 'win32') ? 'x86 x64' : 'x64'}`);
parameters.push(`/DarchInstallIn64bit=${(arch === 'win32') ? '' : 'x64'}`);
parameters.push(`/DsourceFolder=${APPS_DIR}`);
parameters.push(`/DtargetFolder=${APPS_DIR}`);
// Show only errors in console
parameters.push(`/Q`);
// Script file to execute
parameters.push("assets/windows/installer.iss");
innoSetup(parameters, {},
function(error) {
if (error != null) {
console.error(`Installer for platform ${arch} finished with error ${error}`);
} else {
console.log(`Installer for platform ${arch} finished`);
}
done();
});
}
}
gulp.task('release-win32', gulp.series(build_win_zip('win32'), build_win_iss('win32')));
gulp.task('release-win64', gulp.series(build_win_zip('win64'), build_win_iss('win64')));
gulp.task('release-osx64', function(done) {
var pkg = require('./package.json');
var src = path.join(appsDir, pkg.name, 'osx64', pkg.name + '.app');
// Check if we want to sign the .app bundle
if (getArguments().codesign) {
// macapptool can be downloaded from
// https://github.com/fiam/macapptool
//
// Make sure the bundle is well formed
execSync('macapptool', '-v', '1', 'fix', src);
// Sign
const codesignArgs = ['macapptool', '-v', '1', 'sign'];
const codesignIdentity = getArguments()['codesign-identity'];
if (codesignIdentity) {
codesignArgs.push('-i', codesignIdentity);
}
codesignArgs.push('-e', 'entitlements.plist');
codesignArgs.push(src)
execSync.apply(this, codesignArgs);
// Check if the bundle is signed
const codesignCheckArgs = [ 'codesign', '-vvv', '--deep', '--strict', src ];
execSync.apply(this, codesignCheckArgs);
}
// 'old' .zip mode
if (!getArguments().installer) {
const zipFilename = path.join(appsDir, get_release_filename('macOS', 'zip'));
console.log('Creating ZIP file: ' + zipFilename);
var output = fs.createWriteStream(zipFilename);
var archive = archiver('zip', {
zlib: { level: 9 }
});
archive.on('warning', function(err) { throw err; });
archive.on('error', function(err) { throw err; });
archive.pipe(output);
archive.directory(src, 'INAV Configurator.app');
output.on('close', function() {
if (getArguments().notarize) {
console.log('Notarizing ZIP file: ' + zipFilename);
const notarizeArgs = ['xcrun', 'notarytool', 'submit'];
notarizeArgs.push(zipFilename);
const notarizationUsername = getArguments()['notarization-username'];
if (notarizationUsername) {
notarizeArgs.push('--apple-id', notarizationUsername)
} else {
throw new Error('Missing notarization username');
}
const notarizationPassword = getArguments()['notarization-password'];
if (notarizationPassword) {
notarizeArgs.push('--password', notarizationPassword)
} else {
throw new Error('Missing notarization password');
}
const notarizationTeamId = getArguments()['notarization-team-id'];
if (notarizationTeamId) {
notarizeArgs.push('--team-id', notarizationTeamId)
} else {
throw new Error('Missing notarization Team ID');
}
notarizeArgs.push('--wait');
const notarizationWebhook = getArguments()['notarization-webhook'];
if (notarizationWebhook) {
notarizeArgs.push('--webhook', notarizationWebhook);
}
execSync.apply(this, notarizeArgs);
console.log('Stapling ZIP file: ' + zipFilename);
const stapleArgs = ['macapptool', '-v', '1', 'staple'];
stapleArgs.push(zipFilename)
execSync.apply(this, stapleArgs);
}
done();
});
archive.finalize();
}
// 'new' .dmg mode
else {
const appdmg = require('appdmg');
var target = path.join(appsDir, get_release_filename('macOS', 'dmg'));
console.log('Creating DMG file: ' + target);
var basepath = path.join(appsDir, pkg.name, 'osx64');
console.log('Base path: ' + basepath);
if (fs.existsSync(target)) {
fs.unlinkSync(target);
}
var specs = {};
specs["title"] = "INAV Configurator";
specs["contents"] = [
{ "x": 448, "y": 342, "type": "link", "path": "/Applications" },
{ "x": 192, "y": 344, "type": "file", "path": pkg.name + ".app", "name": "INAV Configurator.app" },
];
specs["background"] = path.join(__dirname, 'assets/osx/dmg-background.png');
specs["format"] = "UDZO";
specs["window"] = {
"size": {
"width": 638,
"height": 479,
}
};
const codesignIdentity = getArguments()['codesign-identity'];
if (getArguments().codesign) {
specs['code-sign'] = {
'signing-identity': codesignIdentity,
}
}
const ee = appdmg({
target: target,
basepath: basepath,
specification: specs,
});
ee.on('progress', function(info) {
//console.log(info);
});
ee.on('error', function(err) {
console.log(err);
});
ee.on('finish', function() {
if (getArguments().codesign) {
// Check if the bundle is signed
const codesignCheckArgs = [ 'codesign', '-vvv', '--deep', '--strict', target ];
execSync.apply(this, codesignCheckArgs);
}
if (getArguments().notarize) {
console.log('Notarizing DMG file: ' + target);
const notarizeArgs = ['xcrun', 'notarytool', 'submit'];
notarizeArgs.push(target);
const notarizationUsername = getArguments()['notarization-username'];
if (notarizationUsername) {
notarizeArgs.push('--apple-id', notarizationUsername)
} else {
throw new Error('Missing notarization username');
}
const notarizationPassword = getArguments()['notarization-password'];
if (notarizationPassword) {
notarizeArgs.push('--password', notarizationPassword)
} else {
throw new Error('Missing notarization password');
}
const notarizationTeamId = getArguments()['notarization-team-id'];
if (notarizationTeamId) {
notarizeArgs.push('--team-id', notarizationTeamId)
} else {
throw new Error('Missing notarization Team ID');
}
notarizeArgs.push('--wait');
const notarizationWebhook = getArguments()['notarization-webhook'];
if (notarizationWebhook) {
notarizeArgs.push('--webhook', notarizationWebhook);
}
execSync.apply(this, notarizeArgs);
console.log('Stapling DMG file: ' + target);
const stapleArgs = ['xcrun', 'stapler', 'staple'];
stapleArgs.push(target);
execSync.apply(this, stapleArgs);
console.log('Checking DMG file: ' + target);
const checkArgs = ['spctl', '-vvv', '--assess', '--type', 'install', target];
execSync.apply(this, checkArgs);
}
done();
});
}
});
function post_build(arch, folder) {
return function post_build_linux(done) {
if ((arch === 'linux32') || (arch === 'linux64')) {
const metadata = require('./package.json');
// Copy Ubuntu launcher scripts to destination dir
const launcherDir = path.join(folder, metadata.name, arch);
console.log(`Copy Ubuntu launcher scripts to ${launcherDir}`);
return gulp.src('assets/linux/**')
.pipe(gulp.dest(launcherDir));
}
return done();
}
}
// Create the dir directory, with write permissions
function createDirIfNotExists(dir) {
fs.mkdir(dir, '0775', function(err) {
if (err && err.code !== 'EEXIST') {
throw err;
}
});
}
function release_deb(arch) {
return function release_deb_proc(done) {
if (!getArguments().installer) {
done();
return null;
}
// Check if dpkg-deb exists
if (!commandExistsSync('dpkg-deb')) {
console.warn(`dpkg-deb command not found, not generating deb package for ${arch}`);
done();
return null;
}
const deb = require('gulp-debian');
const LINUX_INSTALL_DIR = '/opt/inav';
const metadata = require('./package.json');
console.log(`Generating deb package for ${arch}`);
return gulp.src([path.join(appsDir, metadata.name, arch, '*')])
.pipe(deb({
package: metadata.name,
version: metadata.version,
section: 'base',
priority: 'optional',
architecture: getLinuxPackageArch('deb', arch),
maintainer: metadata.author,
description: metadata.description,
preinst: [`rm -rf ${LINUX_INSTALL_DIR}/${metadata.name}`],
postinst: [
`chown root:root ${LINUX_INSTALL_DIR}`,
`chown -R root:root ${LINUX_INSTALL_DIR}/${metadata.name}`,
`xdg-desktop-menu install ${LINUX_INSTALL_DIR}/${metadata.name}/${metadata.name}.desktop`,
],
prerm: [`xdg-desktop-menu uninstall ${metadata.name}.desktop`],
depends: ['libatomic1'],
changelog: [],
_target: `${LINUX_INSTALL_DIR}/${metadata.name}`,
_out: appsDir,
_copyright: 'assets/linux/copyright',
_clean: true,
}));
}
}
function post_release_deb(arch) {
return function post_release_linux_deb(done) {
if (!getArguments().installer) {
done();
return null;
}
if ((arch === 'linux32') || (arch === 'linux64')) {
var rename = require("gulp-rename");
const metadata = require('./package.json');
const renameFrom = path.join(appsDir, metadata.name + '_' + metadata.version + '_' + getLinuxPackageArch('.deb', arch) + '.deb');
const renameTo = path.join(appsDir, get_release_filename_base(arch) + '_' + metadata.version + '.deb');
// Rename .deb build to common naming
console.log(`Renaming .deb installer ${renameFrom} to ${renameTo}`);
return gulp.src(renameFrom)
.pipe(rename(renameTo))
.pipe(gulp.dest("."));
}
return done();
}
}
function release_rpm(arch) {
return function release_rpm_proc(done) {
if (!getArguments().installer) {
done();
return null;
}
// Check if rpmbuild exists
if (!commandExistsSync('rpmbuild')) {
console.warn(`rpmbuild command not found, not generating rpm package for ${arch}`);
done();
return;
}
const buildRpm = require('rpm-builder');
const NAME_REGEX = /-/g;
const LINUX_INSTALL_DIR = '/opt/inav';
const metadata = require('./package.json');
console.log(`Generating rpm package for ${arch}`);
// The buildRpm does not generate the folder correctly, manually
createDirIfNotExists(appsDir);
const options = {
name: get_release_filename_base(arch), // metadata.name,
version: metadata.version.replace(NAME_REGEX, '_'), // RPM does not like release candidate versions
buildArch: getLinuxPackageArch('rpm', arch),
vendor: metadata.author,
summary: metadata.description,
license: 'GNU General Public License v3.0',
requires: ['libatomic1'],
prefix: '/opt',
files: [{
cwd: path.join(appsDir, metadata.name, arch),
src: '*',
dest: `${LINUX_INSTALL_DIR}/${metadata.name}`,
}],
postInstallScript: [`xdg-desktop-menu install ${LINUX_INSTALL_DIR}/${metadata.name}/${metadata.name}.desktop`],
preUninstallScript: [`xdg-desktop-menu uninstall ${metadata.name}.desktop`],
tempDir: path.join(appsDir, `tmp-rpm-build-${arch}`),
keepTemp: false,
verbose: false,
rpmDest: appsDir,
execOpts: { maxBuffer: 1024 * 1024 * 16 },
};
buildRpm(options, function(err) {
if (err) {
console.error(`Error generating rpm package: ${err}`);
}
done();
});
}
}
function getLinuxPackageArch(type, arch) {
let packArch;
switch (arch) {
case 'linux32':
packArch = 'i386';
break;
case 'linux64':
if (type === 'rpm') {
packArch = 'x86_64';
} else {
packArch = 'amd64';
}
break;
default:
console.error(`Package error, arch: ${arch}`);
process.exit(1);
break;
}
return packArch;
}
function releaseLinux(bits) {
return function() {
console.log(`Generating zip package for linux${bits}`);
var dirname = 'linux' + bits;
var pkg = require('./package.json');
var src = path.join(appsDir, pkg.name, dirname);
var output = fs.createWriteStream(path.join(appsDir, get_release_filename(dirname, 'tar.gz')));
var archive = archiver('tar', {
zlib: { level: 9 },
gzip: true
});
archive.on('warning', function(err) { throw err; });
archive.on('error', function(err) { throw err; });
archive.pipe(output);
archive.directory(src, 'INAV Configurator');
return archive.finalize();
}
}
gulp.task('release-linux32', gulp.series(releaseLinux(32), post_build('linux32', appsDir), release_deb('linux32'), post_release_deb('linux32')));
gulp.task('release-linux64', gulp.series(releaseLinux(64), post_build('linux64', appsDir), release_deb('linux64'), post_release_deb('linux64'), release_rpm('linux64')));
// Create distributable .zip files in ./apps
gulp.task('release', gulp.series('apps', getPlatforms().map(function(v) { return 'release-' + v; })));
gulp.task('watch', function () {
for(var k in output) {
gulp.watch(sources[k], gulp.series(get_task_name(k)));
}
});
gulp.task('default', gulp.series('build'));

BIN
images/inav-installing.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

12
main.html → index.html Executable file → Normal file
View file

@ -3,9 +3,13 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link type="text/css" rel="stylesheet" href="./build/styles.css" media="all" />
<script type="text/javascript" src="./build/script.js"></script>
<title></title>
<link type="text/css" rel="stylesheet" href="./src/css/styles.css" media="all" />
<script type="text/javascript">require('./js/configurator_main.js');</script>
<!-- TODO: Update to newer Versions and use proper modules (require) -->
<script type="text/javascript" src="./js/libraries/three/three.min.js"></script>
<script type="text/javascript" src="./js/libraries/three/OrbitControls.js"></script>
<script type="text/javascript" src="./js/libraries/three/GLTFLoader.js"></script>
<title>INAV Configurator</title>
</head>
<body>
@ -352,7 +356,7 @@
</div>
</div>
<div id="modal-reconnect" class="is-hidden">
<div data-i18n="deviceRebooting"></div>
<div data-i18n="deviceRebooting">Device - <span style="color: red">Rebooting</span></div>
</div>
<div id="modal-saving-defaults" class="is-hidden">
<div data-i18n="savingDefaults"></div>

View file

@ -4,13 +4,18 @@
* Uses: https://github.com/yuku/jquery-textcomplete
* Check out the docs at https://github.com/yuku/jquery-textcomplete/tree/v1/doc
*/
const FC = require('./fc')
const CONFIGURATOR = require('./data_storage');
const timeout = require('./timeouts');
const CliAutoComplete = {
configEnabled: false,
builder: { state: 'reset', numFails: 0 },
};
CliAutoComplete.isEnabled = function() {
return this.isBuilding() || (this.configEnabled && CONFIG.flightControllerIdentifier === "INAV" && this.builder.state !== 'fail');
return this.isBuilding() || (this.configEnabled && FC.CONFIG.flightControllerIdentifier === "INAV" && this.builder.state !== 'fail');
};
CliAutoComplete.isBuilding = function() {
@ -67,7 +72,7 @@ CliAutoComplete._builderWatchdogTouch = function() {
this._builderWatchdogStop();
helper.timeout.add('autocomplete_builder_watchdog', function() {
timeout.add('autocomplete_builder_watchdog', function() {
if (self.builder.numFails) {
self.builder.numFails++;
self.builder.state = 'fail';
@ -82,7 +87,7 @@ CliAutoComplete._builderWatchdogTouch = function() {
};
CliAutoComplete._builderWatchdogStop = function() {
helper.timeout.remove('autocomplete_builder_watchdog');
timeout.remove('autocomplete_builder_watchdog');
};
CliAutoComplete.builderStart = function() {
@ -552,3 +557,5 @@ CliAutoComplete._initTextcomplete = function() {
}),
]);
};
module.exports = CliAutoComplete;

View file

@ -1,21 +1,27 @@
'use strict';
const semver = require('semver');
const { GUI } = require('./gui');
const jBox = require('./libraries/jBox/jBox.min');
const i18n = require('./localization');
var appUpdater = appUpdater || {};
appUpdater.checkRelease = function (currVersion) {
var modalStart;
$.get('https://api.github.com/repos/iNavFlight/inav-configurator/releases', function (releaseData) {
GUI.log(chrome.i18n.getMessage('loadedReleaseInfo'));
GUI.log(i18n.getMessage('loadedReleaseInfo'));
//Git return sorted list, 0 - last release
let newVersion = releaseData[0].tag_name;
let newPrerelase = releaseData[0].prerelease;
if (newPrerelase == false && semver.gt(newVersion, currVersion)) {
GUI.log(newVersion, chrome.runtime.getManifest().version);
GUI.log(newVersion, app.getVersion());
GUI.log(currVersion);
GUI.log(chrome.i18n.getMessage('newVersionAvailable'));
GUI.log(i18n.getMessage('newVersionAvailable'));
modalStart = new jBox('Modal', {
width: 400,
height: 200,
@ -34,3 +40,5 @@ appUpdater.checkRelease = function (currVersion) {
modalStart.close();
});
};
module.exports = appUpdater;

34
js/bitHelper.js Normal file
View file

@ -0,0 +1,34 @@
'use strict'
var BitHelper = function() {
var self = {};
self.highByte = function (num) {
return num >> 8;
}
self.lowByte = function (num) {
return 0x00FF & num;
}
self.specificByte = function (num, pos) {
return 0x000000FF & (num >> (8 * pos));
}
self.bit_check = function (num, bit) {
return ((num >> bit) % 2 != 0);
}
self.bit_set = function (num, bit) {
return num | 1 << bit;
}
self.bit_clear = function(num, bit) {
return num & ~(1 << bit);
}
return self;
}();
module.exports = BitHelper;

View file

@ -59,3 +59,6 @@ BOARD.findDefinition = function (identifier) {
}
return DEFAULT_BOARD_DEFINITION;
};
module.exports = BOARD;

622
js/configurator_main.js Normal file
View file

@ -0,0 +1,622 @@
window.$ = window.jQuery = require('jquery'),
require('jquery-ui-dist/jquery-ui'),
require('jquery-textcomplete'),
require('./libraries/jquery.flightindicators'),
require('./libraries/jquery.nouislider.all.min'),
require('./libraries/jquery.ba-throttle-debounce');
const { app } = require('@electron/remote');
const d3 = require('./libraries/d3.min');
const Store = require('electron-store');
const store = new Store();
const { GUI, TABS } = require('./gui');
const CONFIGURATOR = require('./data_storage');
const FC = require('./fc');
const { globalSettings, UnitType } = require('./globalSettings');
const { PLATFORM } = require('./model')
const i18n = require('./localization');
const SerialBackend = require('./serial_backend');
const MSP = require('./msp');
const MSPCodes = require('./../js/msp/MSPCodes');
const mspHelper = require('./msp/MSPHelper');
const update = require('./globalUpdates');
const appUpdater = require('./appUpdater');
const CliAutoComplete = require('./CliAutoComplete');
const { SITLProcess } = require('./sitl');
process.on('uncaughtException', function (error) {
if (process.env.NODE_ENV !== 'development') {
GUI.log(i18n.getMessage('unexpectedError', error.message));
if (GUI.connected_to || GUI.connecting_to) {
GUI.log(i18n.getMessage('disconnecting'));
$('a.connect').trigger('click');
}
} else {
throw error;
}
});
// Set how the units render on the configurator only
$(function() {
i18n.init( () => {
i18n.localize();
MSP.init();
mspHelper.init();
SerialBackend.init();
GUI.updateEzTuneTabVisibility = function(loadMixerConfig) {
let useEzTune = true;
if (CONFIGURATOR.connectionValid) {
if (loadMixerConfig) {
mspHelper.loadMixerConfig(function () {
if (FC.MIXER_CONFIG.platformType == PLATFORM.MULTIROTOR || FC.MIXER_CONFIG.platformType == PLATFORM.TRICOPTER) {
$('.tab_ez_tune').removeClass("is-hidden");
} else {
$('.tab_ez_tune').addClass("is-hidden");
useEzTune = false;
}
});
} else {
if (FC.MIXER_CONFIG.platformType == PLATFORM.MULTIROTOR || FC.MIXER_CONFIG.platformType == PLATFORM.TRICOPTER) {
$('.tab_ez_tune').removeClass("is-hidden");
} else {
$('.tab_ez_tune').addClass("is-hidden");
useEzTune = false;
}
}
}
return useEzTune;
};
GUI.updateActivatedTab = function() {
var activeTab = $('#tabs > ul li.active');
activeTab.removeClass('active');
$('a', activeTab).trigger('click');
}
globalSettings.unitType = store.get('unit_type', UnitType.none);
globalSettings.mapProviderType = store.get('map_provider_type', 'osm');
globalSettings.mapApiKey = store.get('map_api_key', '');
globalSettings.proxyURL = store.get('proxyurl', 'http://192.168.1.222/mapproxy/service?');
globalSettings.proxyLayer = store.get('proxylayer', 'your_proxy_layer_name');
globalSettings.showProfileParameters = store.get('show_profile_parameters', 1);
updateProfilesHighlightColours();
var cliAutocomplete = store.get('cli_autocomplete', true);
globalSettings.cliAutocomplete = cliAutocomplete;
CliAutoComplete.setEnabled(cliAutocomplete);
// Resets the OSD units used by the unit coversion when the FC is disconnected.
if (!CONFIGURATOR.connectionValid) {
globalSettings.osdUnits = null;
}
// alternative - window.navigator.appVersion.match(/Chrome\/([0-9.]*)/)[1];
GUI.log(i18n.getMessage('getRunningOS') + GUI.operating_system + '</strong>, ' +
'Chrome: <strong>' + process.versions['chrome'] + '</strong>, ' +
i18n.getMessage('getConfiguratorVersion') + app.getVersion() + '</strong>');
$('#status-bar .version').text(app.getVersion());
$('#logo .version').text(app.getVersion());
update.firmwareVersion();
if (store.get('logopen', false)) {
$("#showlog").trigger('click');
}
if (store.get('update_notify', true)) {
appUpdater.checkRelease(app.getVersion());
}
// log library versions in console to make version tracking easier
console.log('Libraries: jQuery - ' + $.fn.jquery + ', d3 - ' + d3.version + ', three.js - ' + THREE.REVISION);
// Tabs
var ui_tabs = $('#tabs > ul');
$('a', ui_tabs).on('click', function() {
if ($(this).parent().hasClass("tab_help")) {
return;
}
if ($(this).parent().hasClass('active') == false && !GUI.tab_switch_in_progress) { // only initialize when the tab isn't already active
var self = this,
tabClass = $(self).parent().prop('class');
var tabRequiresConnection = $(self).parent().hasClass('mode-connected');
var tab = tabClass.substring(4);
var tabName = $(self).text();
if (tabRequiresConnection && !CONFIGURATOR.connectionValid) {
GUI.log(i18n.getMessage('tabSwitchConnectionRequired'));
return;
}
if (GUI.connect_lock) { // tab switching disabled while operation is in progress
GUI.log(i18n.getMessage('tabSwitchWaitForOperation'));
return;
}
if (GUI.allowedTabs.indexOf(tab) < 0) {
GUI.log(i18n.getMessage('tabSwitchUpgradeRequired', [tabName]));
return;
}
GUI.tab_switch_in_progress = true;
GUI.tab_switch_cleanup(function () {
// disable previously active tab highlight
$('li', ui_tabs).removeClass('active');
// Highlight selected tab
$(self).parent().addClass('active');
// detach listeners and remove element data
var content = $('#content');
content.data('empty', !!content.is(':empty'));
content.empty();
// display loading screen
$('#cache .data-loading').clone().appendTo(content);
function content_ready() {
GUI.tab_switch_in_progress = false;
// Update CSS on to show highlighing or not
updateProfilesHighlightColours();
}
switch (tab) {
case 'landing':
require('./../tabs/landing')
TABS.landing.initialize(content_ready);
break;
case 'firmware_flasher':
require('./../tabs/firmware_flasher')
TABS.firmware_flasher.initialize(content_ready);
break;
case 'sitl':
require('./../tabs/sitl')
TABS.sitl.initialize(content_ready);
break;
case 'auxiliary':
require('./../tabs/auxiliary')
TABS.auxiliary.initialize(content_ready);
break;
case 'adjustments':
require('./../tabs/adjustments')
TABS.adjustments.initialize(content_ready);
break;
case 'ports':
require('./../tabs/ports');
TABS.ports.initialize(content_ready);
break;
case 'led_strip':
require('./../tabs/led_strip');
TABS.led_strip.initialize(content_ready);
break;
case 'failsafe':
require('./../tabs/failsafe');
TABS.failsafe.initialize(content_ready);
break;
case 'setup':
require('./../tabs/setup');
TABS.setup.initialize(content_ready);
break;
case 'calibration':
require('./../tabs/calibration');
TABS.calibration.initialize(content_ready);
break;
case 'configuration':
require('./../tabs/configuration');
TABS.configuration.initialize(content_ready);
break;
case 'pid_tuning':
require('./../tabs/pid_tuning');
TABS.pid_tuning.initialize(content_ready);
break;
case 'receiver':
require('./../tabs/receiver');
TABS.receiver.initialize(content_ready);
break;
case 'modes':
require('./../tabs/modes');
TABS.modes.initialize(content_ready);
break;
case 'gps':
require('./../tabs/gps');
TABS.gps.initialize(content_ready);
break;
case 'magnetometer':
require('./../tabs/magnetometer');
TABS.magnetometer.initialize(content_ready);
break;
case 'mission_control':
require('./../tabs/mission_control');
TABS.mission_control.initialize(content_ready);
break;
case 'mixer':
require('./../tabs/mixer');
TABS.mixer.initialize(content_ready);
break;
case 'outputs':
require('./../tabs/outputs');
TABS.outputs.initialize(content_ready);
break;
case 'osd':
require('./../tabs/osd');
TABS.osd.initialize(content_ready);
break;
case 'sensors':
require('./../tabs/sensors');
TABS.sensors.initialize(content_ready);
break;
case 'logging':
require('./../tabs/logging');
TABS.logging.initialize(content_ready);
break;
case 'onboard_logging':
require('./../tabs/onboard_logging');
TABS.onboard_logging.initialize(content_ready);
break;
case 'advanced_tuning':
require('./../tabs/advanced_tuning');
TABS.advanced_tuning.initialize(content_ready);
break;
case 'programming':
require('./../tabs/programming');
TABS.programming.initialize(content_ready);
break;
case 'cli':
require('./../tabs/cli');
TABS.cli.initialize(content_ready);
break;
case 'ez_tune':
require('./../tabs/ez_tune');
TABS.ez_tune.initialize(content_ready);
break;
default:
console.log('Tab not found:' + tab);
}
});
}
});
$('#tabs ul.mode-disconnected li a:first').trigger( "click" );
// options
$('#options').on('click', function() {
var el = $(this);
if (!el.hasClass('active')) {
el.addClass('active');
el.after('<div id="options-window"></div>');
$('div#options-window').load('./tabs/options.html', function () {
// translate to user-selected language
i18n.localize();
// if notifications are enabled, or wasn't set, check the notifications checkbox
if (store.get('update_notify', true)) {
$('div.notifications input').prop('checked', true);
}
$('div.notifications input').on('change', function () {
var check = $(this).is(':checked');
store.set('update_notify', check);
});
$('div.statistics input').on('change', function () {
var check = $(this).is(':checked');
});
$('div.show_profile_parameters input').on('change', function () {
globalSettings.showProfileParameters = $(this).is(':checked');
store.set('show_profile_parameters', globalSettings.showProfileParameters);
// Update CSS on select boxes
updateProfilesHighlightColours();
// Horrible way to reload the tab
const activeTab = $('#tabs li.active');
activeTab.removeClass('active');
activeTab.find('a').trigger( "click" );
});
$('div.cli_autocomplete input').on('change', function () {
globalSettings.cliAutocomplete = $(this).is(':checked');
store.set('cli_autocomplete', globalSettings.cliAutocomplete);
CliAutoComplete.setEnabled($(this).is(':checked'));
});
$('#ui-unit-type').val(globalSettings.unitType);
$('#map-provider-type').val(globalSettings.mapProviderType);
$('#map-api-key').val(globalSettings.mapApiKey);
$('#proxyurl').val(globalSettings.proxyURL);
$('#proxylayer').val(globalSettings.proxyLayer);
$('#showProfileParameters').prop('checked', globalSettings.showProfileParameters);
$('#cliAutocomplete').prop('checked', globalSettings.cliAutocomplete);
i18n.getLanguages().forEach(lng => {
$('#languageOption').append("<option value='{0}'>{1}</option>".format(lng, i18n.getMessage("language_" + lng)));
});
$('#languageOption').val(i18n.getCurrentLanguage());
$('#languageOption').on('change', () => {
i18n.changeLanguage($('#languageOption').val());
});
// Set the value of the unit type
// none, OSD, imperial, metric
$('#ui-unit-type').on('change', function () {
store.set('unit_type', $(this).val());
globalSettings.unitType = $(this).val();
// Update the osd units in global settings
// but only if we need it
if (globalSettings.unitType === UnitType.OSD) {
get_osd_settings();
}
// Horrible way to reload the tab
const activeTab = $('#tabs li.active');
activeTab.removeClass('active');
activeTab.find('a').trigger( "click" );
});
$('#map-provider-type').on('change', function () {
store.set('map_provider_type', $(this).val());
globalSettings.mapProviderType = $(this).val();
});
$('#map-api-key').on('change', function () {
store.set('map_api_key', $(this).val());
globalSettings.mapApiKey = $(this).val();
});
$('#proxyurl').on('change', function () {
store.set('proxyurl', $(this).val());
globalSettings.proxyURL = $(this).val();
});
$('#proxylayer').on('change', function () {
store.set('proxylayer', $(this).val());
globalSettings.proxyLayer = $(this).val();
});
$('#demoModeReset').on('click', function () {
SITLProcess.deleteEepromFile('demo.bin');
});
function close_and_cleanup(e) {
if (e.type == 'click' && !$.contains($('div#options-window')[0], e.target) || e.type == 'keyup' && e.keyCode == 27) {
$(document).unbind('click keyup', close_and_cleanup);
$('div#options-window').slideUp(250, function () {
el.removeClass('active');
$(this).empty().remove();
});
}
}
$(document).bind('click keyup', close_and_cleanup);
$(this).slideDown(250);
});
}
});
var $content = $("#content");
// listen to all input change events and adjust the value within limits if necessary
$content.on('focus', 'input[type="number"]', function () {
var element = $(this),
val = element.val();
if (!isNaN(val)) {
element.data('previousValue', parseFloat(val));
}
});
$content.on('keydown', 'input[type="number"]', function (e) {
// whitelist all that we need for numeric control
var whitelist = [
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, // numpad and standard number keypad
109, 189, // minus on numpad and in standard keyboard
8, 46, 9, // backspace, delete, tab
190, 110, // decimal point
37, 38, 39, 40, 13 // arrows and enter
];
if (whitelist.indexOf(e.keyCode) == -1) {
e.preventDefault();
}
});
$content.on('change', 'input[type="number"]', function () {
var element = $(this),
min = parseFloat(element.prop('min')),
max = parseFloat(element.prop('max')),
step = parseFloat(element.prop('step')),
val = parseFloat(element.val()),
decimal_places;
// only adjust minimal end if bound is set
if (element.prop('min')) {
if (val < min) {
element.val(min);
val = min;
}
}
// only adjust maximal end if bound is set
if (element.prop('max')) {
if (val > max) {
element.val(max);
val = max;
}
}
// if entered value is illegal use previous value instead
if (isNaN(val)) {
element.val(element.data('previousValue'));
val = element.data('previousValue');
}
// if step is not set or step is int and value is float use previous value instead
if (isNaN(step) || step % 1 === 0) {
if (val % 1 !== 0) {
element.val(element.data('previousValue'));
val = element.data('previousValue');
}
}
// if step is set and is float and value is int, convert to float, keep decimal places in float according to step *experimental*
if (!isNaN(step) && step % 1 !== 0) {
decimal_places = String(step).split('.')[1].length;
if (val % 1 === 0) {
element.val(val.toFixed(decimal_places));
} else if (String(val).split('.')[1].length != decimal_places) {
element.val(val.toFixed(decimal_places));
}
}
});
$("#showlog").on('click', function() {
var state = $(this).data('state'),
$log = $("#log");
if (state) {
$log.animate({height: 27}, 200, function() {
var command_log = $('div#log');
//noinspection JSValidateTypes
command_log.scrollTop($('div.wrapper', command_log).height());
});
$log.removeClass('active');
$("#content").removeClass('logopen');
$(".tab_container").removeClass('logopen');
$("#scrollicon").removeClass('active');
store.set('logopen', false);
state = false;
}else{
$log.animate({height: 111}, 200);
$log.addClass('active');
$("#content").addClass('logopen');
$(".tab_container").addClass('logopen');
$("#scrollicon").addClass('active');
store.set('logopen', true);
state = true;
}
$(this).html(state ? i18n.getMessage("mainHideLog") : i18n.getMessage("mainShowLog"));
$(this).data('state', state);
});
var mixerprofile_e = $('#mixerprofilechange');
mixerprofile_e.on('change', function () {
var mixerprofile = parseInt($(this).val());
MSP.send_message(MSPCodes.MSP2_INAV_SELECT_MIXER_PROFILE, [mixerprofile], false, function () {
GUI.log(i18n.getMessage('loadedMixerProfile', [mixerprofile + 1]));
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, function () {
GUI.log(i18n.getMessage('deviceRebooting'));
GUI.handleReconnect();
});
});
});
var profile_e = $('#profilechange');
profile_e.on('change', function () {
var profile = parseInt($(this).val());
MSP.send_message(MSPCodes.MSP_SELECT_SETTING, [profile], false, function () {
GUI.log(i18n.getMessage('pidTuning_LoadedProfile', [profile + 1]));
});
});
var batteryprofile_e = $('#batteryprofilechange');
batteryprofile_e.on('change', function () {
var batteryprofile = parseInt($(this).val());
MSP.send_message(MSPCodes.MSP2_INAV_SELECT_BATTERY_PROFILE, [batteryprofile], false, function () {
GUI.log(i18n.getMessage('loadedBatteryProfile', [batteryprofile + 1]));
});
});
});
});
function get_osd_settings() {
if (globalSettings.osdUnits !== undefined && globalSettings.osdUnits !== null) {
return;
}
MSP.promise(MSPCodes.MSP2_INAV_OSD_PREFERENCES).then(function (resp) {
var prefs = resp.data;
prefs.readU8();
prefs.readU8();
prefs.readU8();
prefs.readU8();
prefs.readU8();
prefs.readU8();
prefs.readU8();
globalSettings.osdUnits = prefs.readU8();
});
}
function updateProfilesHighlightColours() {
if (globalSettings.showProfileParameters) {
$('.dropdown-dark #profilechange').addClass('showProfileParams');
$('.dropdown-dark #batteryprofilechange').addClass('showProfileParams');
$('.batteryProfileHighlight').each(function () {
$(this).addClass('batteryProfileHighlightActive');
$(this).removeClass('batteryProfileHighlight');
});
$('.controlProfileHighlight').each(function () {
$(this).addClass('controlProfileHighlightActive');
$(this).removeClass('controlProfileHighlight');
});
} else {
$('.dropdown-dark #profilechange').removeClass('showProfileParams');
$('.dropdown-dark #batteryprofilechange').removeClass('showProfileParams');
$('.batteryProfileHighlightActive').each(function () {
$(this).addClass('batteryProfileHighlight');
$(this).removeClass('batteryProfileHighlightActive');
});
$('.controlProfileHighlightActive').each(function () {
$(this).addClass('controlProfileHighlight');
$(this).removeClass('controlProfileHighlightActive');
});
}
}
Number.prototype.clamp = function (min, max) {
return Math.min(Math.max(this, min), max);
};
/**
* String formatting now supports currying (partial application).
* For a format string with N replacement indices, you can call .format
* with M <= N arguments. The result is going to be a format string
* with N-M replacement indices, properly counting from 0 .. N-M.
* The following Example should explain the usage of partial applied format:
* "{0}:{1}:{2}".format("a","b","c") === "{0}:{1}:{2}".format("a","b").format("c")
* "{0}:{1}:{2}".format("a").format("b").format("c") === "{0}:{1}:{2}".format("a").format("b", "c")
**/
String.prototype.format = function () {
var args = arguments;
return this.replace(/\{(\d+)\}/g, function (t, i) {
return args[i] !== void 0 ? args[i] : "{" + (i - args.length) + "}";
});
};

View file

@ -1,5 +1,7 @@
'use strict';
const { GUI } = require('./../gui');
const ConnectionType = {
Serial: 0,
TCP: 1,
@ -10,7 +12,7 @@ const ConnectionType = {
class Connection {
constructor() {
this._connectionId = false;
this._connectionId = 0;
this._openRequested = false;
this._openCanceled = false;
this._bitrate = 0;
@ -20,6 +22,7 @@ class Connection {
this._outputBuffer = [];
this._onReceiveListeners = [];
this._onReceiveErrorListeners = [];
this._type = null;
if (this.constructor === Connection) {
throw new TypeError("Abstract class, cannot be instanced.");
@ -59,40 +62,8 @@ class Connection {
}
get type() {
switch (this.constructor.name) {
case ConnectionSerial.name:
return ConnectionType.Serial;
case ConnectionTcp.name:
return ConnectionType.TCP;
case ConnectionUdp.name:
return ConnectionType.UDP;
case ConnectionBle.name:
return ConnectionType.BLE;
return this._type;
}
}
static create(type) {
if (Connection.instance && (Connection.instance.type == type || Connection.instance.connectionId)){
return Connection.instance;
}
switch (type) {
case ConnectionType.BLE:
Connection.instance = new ConnectionBle();
break;
case ConnectionType.TCP:
Connection.instance = new ConnectionTcp();
break;
case ConnectionType.UDP:
Connection.instance = new ConnectionUdp();
break;
default:
case ConnectionType.Serial:
Connection.instance = new ConnectionSerial();
break;
}
return Connection.instance;
};
connectImplementation(path, options, callback) {
throw new TypeError("Abstract method");
@ -100,6 +71,7 @@ class Connection {
connect(path, options, callback) {
this._openRequested = true;
this._openCanceled = false;
this._failed = 0;
this.connectImplementation(path, options, connectionInfo => {
if (connectionInfo && !this._openCanceled) {
@ -118,7 +90,7 @@ class Connection {
if (callback) {
callback(connectionInfo);
}
} else if (connectionInfo && this.openCanceled) {
} else if (connectionInfo && this._openCanceled) {
// connection opened, but this connect sequence was canceled
// we will disconnect without triggering any callbacks
this._connectionId = connectionInfo.connectionId;
@ -145,7 +117,6 @@ class Connection {
} else {
this._openRequested = false;
console.log('Failed to open');
googleAnalytics.sendException('FailedToOpen', false);
if (callback) {
callback(false);
}
@ -163,13 +134,11 @@ class Connection {
this.removeAllListeners();
this.disconnectImplementation(result => {
this.checkChromeLastError();
if (result) {
console.log('Connection with ID: ' + this._connectionId + ' closed, Sent: ' + this._bytesSent + ' bytes, Received: ' + this._bytesReceived + ' bytes');
} else {
console.log('Failed to close connection with ID: ' + this._connectionId + ' closed, Sent: ' + this._bytesSent + ' bytes, Received: ' + this._bytesReceived + ' bytes');
googleAnalytics.sendException('Connection: FailedToClose', false);
}
this._connectionId = false;
@ -240,12 +209,6 @@ class Connection {
}
}
checkChromeLastError() {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
}
}
addOnReceiveCallback(callback) {
throw new TypeError("Abstract method");
}
@ -297,3 +260,5 @@ class Connection {
}
}
}
module.exports = { ConnectionType, Connection};

View file

@ -1,5 +1,10 @@
'use strict'
const { GUI } = require('./../gui');
const { ConnectionType, Connection } = require('./connection');
const i18n = require('./../localization');
// BLE 20 bytes buffer
const BLE_WRITE_BUFFER_LENGTH = 20;
@ -48,6 +53,7 @@ class ConnectionBle extends Connection {
this._reconnects = 0;
this._handleOnCharateristicValueChanged = false;
this._handleDisconnect = false;
super._type = ConnectionType.BLE;
}
get deviceDescription() {
@ -59,7 +65,7 @@ class ConnectionBle extends Connection {
await this.openDevice()
.then(() => {
this.addOnReceiveErrorListener(error => {
GUI.log(chrome.i18n.getMessage('connectionBleInterrupted'));
GUI.log(i18n.getMessage('connectionBleInterrupted'));
this.abort();
});
@ -71,7 +77,7 @@ class ConnectionBle extends Connection {
});
}
}).catch(error => {
GUI.log(chrome.i18n.getMessage('connectionBleError', [error]));
GUI.log(i18n.getMessage('connectionBleError', [error]));
if (callback) {
callback(false);
}
@ -119,7 +125,7 @@ class ConnectionBle extends Connection {
return device.gatt.connect()
.then(server => {
console.log("Connect to: " + device.name);
GUI.log(chrome.i18n.getMessage('connectionConnected', [device.name]));
GUI.log(i18n.getMessage('connectionConnected', [device.name]));
return server.getPrimaryServices();
}).then(services => {
let connectedService = services.find(service => {
@ -131,7 +137,7 @@ class ConnectionBle extends Connection {
throw new Error("Unsupported device (service UUID mismatch).");
}
GUI.log(chrome.i18n.getMessage('connectionBleType', [this._deviceDescription.name]));
GUI.log(i18n.getMessage('connectionBleType', [this._deviceDescription.name]));
return connectedService.getCharacteristics();
}).then(characteristics => {
characteristics.forEach(characteristic => {
@ -250,4 +256,14 @@ class ConnectionBle extends Connection {
removeOnReceiveErrorCallback(callback) {
this._onDisconnectListeners = this._onDisconnectListeners.filter(listener => listener !== callback);
}
static getBleUUIDs() {
var ids = [];
BleDevices.forEach(device => {
ids.push(device.serviceUuid)
});
return ids;
}
}
module.exports = ConnectionBle;

View file

@ -0,0 +1,32 @@
'use strict'
const { ConnectionType } = require('./connection');
const ConnectionBle = require('./connectionBle');
const ConnectionSerial = require('./connectionSerial');
const ConnectionTcp = require('./connectionTcp');
const ConnectionUdp = require('./connectionUdp');
var connectionFactory = function(type, instance) {
if (instance && (instance.type == type || instance.connectionId)){
return instance;
}
switch (type) {
case ConnectionType.BLE:
instance = new ConnectionBle();
break;
case ConnectionType.TCP:
instance = new ConnectionTcp();
break;
case ConnectionType.UDP:
instance = new ConnectionUdp();
break;
default:
case ConnectionType.Serial:
instance = new ConnectionSerial();
break;
}
return instance;
};
module.exports = connectionFactory;

View file

@ -1,139 +1,124 @@
'use strict'
class ConnectionSerial extends Connection {
const { GUI } = require('./../gui');
const { ConnectionType, Connection } = require('./connection')
const { SerialPort } = require('serialport');
const { SerialPortStream } = require('@serialport/stream');
const { autoDetect } = require('@serialport/bindings-cpp')
const binding = autoDetect();
class ConnectionSerial extends Connection {
constructor() {
super();
this._failed = 0;
this._serialport = null;
this._errorListeners = [];
this._onReceiveListeners = [];
this._onErrorListener = [];
super._type = ConnectionType.Serial;
}
connectImplementation(path, options, callback) {
chrome.serial.connect(path, options, (connectionInfo) => {
this.checkChromeLastError();
if (connectionInfo && !this._openCanceled) {
this.addOnReceiveErrorListener(info => {
console.error(info);
googleAnalytics.sendException('Serial: ' + info.error, false);
switch (info.error) {
case 'system_error': // we might be able to recover from this one
if (!this._failed++) {
chrome.serial.setPaused(this._connectionId, false, function () {
SerialCom.getInfo((info) => {
if (info) {
if (!info.paused) {
console.log('SERIAL: Connection recovered from last onReceiveError');
googleAnalytics.sendException('Serial: onReceiveError - recovered', false);
this._failed = 0;
} else {
console.log('SERIAL: Connection did not recover from last onReceiveError, disconnecting');
GUI.log(chrome.i18n.getMessage('serialPortUnrecoverable'));
googleAnalytics.sendException('Serial: onReceiveError - unrecoverable', false);
this.abort();
}
} else {
this.checkChromeLastError();
}
});
});
}
break;
case 'break': // This occurs on F1 boards with old firmware during reboot
case 'overrun':
case 'frame_error': //Got disconnected
// wait 50 ms and attempt recovery
var error = info.error;
setTimeout(() => {
chrome.serial.setPaused(info.connectionId, false, function() {
SerialCom.getInfo(function (info) {
if (info) {
if (info.paused) {
// assume unrecoverable, disconnect
console.log('SERIAL: Connection did not recover from ' + error + ' condition, disconnecting');
GUI.log(chrome.i18n.getMessage('serialPortUnrecoverable'));;
googleAnalytics.sendException('Serial: ' + error + ' - unrecoverable', false);
this.abort();
} else {
console.log('SERIAL: Connection recovered from ' + error + ' condition');
googleAnalytics.sendException('Serial: ' + error + ' - recovered', false);
}
}
});
});
}, 50);
break;
case 'timeout':
// TODO
break;
case 'device_lost':
case 'disconnected':
default:
this.abort();
}
});
GUI.log(chrome.i18n.getMessage('connectionConnected', [path]));
}
try {
this._serialport = new SerialPortStream({binding, path: path, baudRate: options.bitrate, autoOpen: true}, () => {
if (callback) {
callback(connectionInfo);
callback({
connectionId: ++this._connectionId,
bitrate: options.bitrate
});
}
});
} catch (error) {
console.log(error);
callback(false);
}
this._serialport.on('data', buffer => {
this._onReceiveListeners.forEach(listener => {
listener({
connectionId: this._connectionId,
data: buffer
});
});
});
this._serialport.on('close', error => {
this.abort();
});
this._serialport.on('error', error => {
this.abort();
console.log("Serial error: " + error);
this._onReceiveErrorListeners.forEach(listener => {
listener(error);
});
});
}
disconnectImplementation(callback) {
chrome.serial.disconnect(this._connectionId, (result) => {
if (callback) {
callback(result);
if (this._serialport && this._serialport.isOpen) {
this._serialport.close(error => {
if (error) {
console.log("Unable to close serial: " + error)
}
});
}
if (callback) {
callback(true);
}
}
sendImplementation(data, callback) {
chrome.serial.send(this._connectionId, data, callback);
if (this._serialport && this._serialport.isOpen) {
this._serialport.write(Buffer.from(data), error => {
var result = 0;
var sent = data.byteLength;
if (error) {
result = 1;
sent = 0;
console.log("Serial write error: " + error)
}
if (callback) {
callback({
bytesSent: sent,
resultCode: result
});
}
});
}
}
addOnReceiveCallback(callback){
chrome.serial.onReceive.addListener(callback);
this._onReceiveErrorListeners.push(callback);
}
removeOnReceiveCallback(callback){
chrome.serial.onReceive.removeListener(callback);
this._onReceiveListeners = this._onReceiveErrorListeners.filter(listener => listener !== callback);
}
addOnReceiveErrorCallback(callback) {
chrome.serial.onReceiveError.addListener(callback);
this._onReceiveErrorListeners.push(callback);
}
removeOnReceiveErrorCallback(callback) {
chrome.serial.onReceiveError.removeListener(callback);
this._onReceiveErrorListeners = this._onReceiveErrorListeners.filter(listener => listener !== callback);
}
static getDevices(callback) {
chrome.serial.getDevices((devices_array) => {
static async getDevices(callback) {
SerialPort.list().then((ports, error) => {
var devices = [];
devices_array.forEach((device) => {
devices.push(device.path);
if (error) {
GUI.log("Unable to list serial ports.");
} else {
ports.forEach(port => {
devices.push(port.path);
});
}
if (callback)
callback(devices);
});
}
static getInfo(connectionId, callback) {
chrome.serial.getInfo(connectionId, callback);
}
static getControlSignals(connectionId, callback) {
chrome.serial.getControlSignals(connectionId, callback);
}
static setControlSignals(connectionId, signals, callback) {
chrome.serial.setControlSignals(connectionId, signals, callback);
}
}
module.exports = ConnectionSerial;

View file

@ -1,5 +1,11 @@
'use strict'
const net = require('net')
const { GUI } = require('./../gui');
const { ConnectionType, Connection } = require('./connection')
const i18n = require('./../localization');
const STANDARD_TCP_PORT = 5761;
class ConnectionTcp extends Connection {
@ -7,8 +13,12 @@ class ConnectionTcp extends Connection {
constructor() {
super();
this._socket = null;
this._connectionIP = "";
this.connectionPort = 0;
this._onReceiveListeners = [];
this._onErrorListener = [];
super._type = ConnectionType.TCP;
}
connectImplementation(address, options, callback) {
@ -18,99 +28,65 @@ class ConnectionTcp extends Connection {
this._connectionPort = parseInt(addr[1])
} else {
this._connectionIP = address[0];
this._connectionPort = STANDARD_PORT;
this._connectionPort = STANDARD_TCP_PORT;
}
chrome.sockets.tcp.create({
name: "iNavTCP",
bufferSize: 65535
}, createInfo => {
this.checkChromeLastError();
if (createInfo && !this._openCanceled) {
chrome.sockets.tcp.connect(createInfo.socketId, this._connectionIP, this._connectionPort, result => {
this.checkChromeLastError();
if (result == 0) {
// Disable Nagle's algorithm
chrome.sockets.tcp.setNoDelay(createInfo.socketId, true, noDelayResult => {
this.checkChromeLastError();
if (noDelayResult < 0) {
console.warn("Unable to set TCP_NODELAY: " + noDelayResult);
if (callback) {
callback(false);
}
}
this.addOnReceiveErrorListener(info => {
console.error(info);
googleAnalytics.sendException('TCP: ' + info.error, false);
let message;
switch (info.resultCode) {
case -15:
// connection is lost, cannot write to it anymore, preventing further disconnect attempts
message = 'error: ERR_SOCKET_NOT_CONNECTED';
console.log(`TCP: ${message}: ${info.resultCode}`);
this._connectionId = false;
return;
case -21:
message = 'error: NETWORK_CHANGED';
break;
case -100:
message = 'error: CONNECTION_CLOSED';
break;
case -102:
message = 'error: CONNECTION_REFUSED';
break;
case -105:
message = 'error: NAME_NOT_RESOLVED';
break;
case -106:
message = 'error: INTERNET_DISCONNECTED';
break;
case -109:
message = 'error: ADDRESS_UNREACHABLE';
break;
}
let resultMessage = message ? `${message} ${info.resultCode}` : info.resultCode;
console.warn(`TCP: ${resultMessage} ID: ${this._connectionId}`);
this.abort();
});
GUI.log(chrome.i18n.getMessage('connectionConnected', ["tcp://" + this._connectionIP + ":" + this._connectionPort]));
try {
this._socket = net.connect({ host: this._connectionIP, port: this._connectionPort }, () => {
this._socket.setNoDelay(true);
GUI.log(i18n.getMessage('connectionConnected', ["tcp://" + this._connectionIP + ":" + this._connectionPort]));
if (callback) {
callback({
bitrate: 115200,
connectionId: createInfo.socketId
connectionId: ++this._connectionId
});
}
});
} else {
console.error("Unable to open TCP socket: " + result);
if (callback) {
} catch (error) {
console.log(error);
callback(false);
}
this._socket.on('data', buffer => {
this._onReceiveListeners.forEach(listener => {
listener({
connectionId: this._connectionId,
data: buffer
});
});
})
this._socket.on('end', () => {
console.log("TCP Remote has closed the connection");
if (this._socket) {
this.abort();
}
});
} else {
console.error("Unable to create TCP socket.");
if (callback) {
callback(false);
}
this._socket.on('error', (error) => {
GUI.log(error);
console.log(error);
if (this._socket) {
this.abort();
}
this._onReceiveErrorListeners.forEach(listener => {
listener(error);
});
});
}
disconnectImplementation(callback) {
chrome.sockets.tcp.disconnect(this._connectionId);
this.checkChromeLastError();
if (this._socket && !this._socket.destroyed) {
this._socket.end();
}
this._connectionIP = "";
this._connectionPort = 0;
this._socket = null;
if (callback) {
callback(true);
@ -118,22 +94,33 @@ class ConnectionTcp extends Connection {
}
sendImplementation(data, callback) {;
chrome.sockets.tcp.send(this._connectionId, data, callback);
if (this._socket && !this._socket.destroyed) {
this._socket.write(Buffer.from(data), () => {
if (callback) {
callback({
bytesSent: data.byteLength,
resultCode: 0
});
}
})
}
}
addOnReceiveCallback(callback){
chrome.sockets.tcp.onReceive.addListener(callback);
this._onReceiveErrorListeners.push(callback);
}
removeOnReceiveCallback(callback){
chrome.sockets.tcp.onReceive.removeListener(callback);
this._onReceiveListeners = this._onReceiveErrorListeners.filter(listener => listener !== callback);
}
addOnReceiveErrorCallback(callback) {
chrome.sockets.tcp.onReceiveError.addListener(callback);
this._onReceiveErrorListeners.push(callback);
}
removeOnReceiveErrorCallback(callback) {
chrome.sockets.tcp.onReceiveError.removeListener(callback);
this._onReceiveErrorListeners = this._onReceiveErrorListeners.filter(listener => listener !== callback);
}
}
module.exports = ConnectionTcp;

View file

@ -1,22 +1,23 @@
'use strict'
const STANDARD_UDP_PORT = 5762;
const { ConnectionType, Connection } = require('./connection')
const dgram = require('node:dgram');
const socket = dgram.createSocket('udp4');
const { GUI } = require('./../gui');
const i18n = require('./../localization');
const STANDARD_UDP_PORT = 5761;
class ConnectionUdp extends Connection {
constructor() {
super();
this._connectionIP = "";
this._connectionPort = 0;
this._timeoutId = false;
this._isCli = false;
}
/**
* @param {boolean} value
*/
set isCli(value) {
this._isCli = value;
this._onReceiveListeners = [];
this._onErrorListener = [];
super._type = ConnectionType.UDP;
}
connectImplementation(address, options, callback) {
@ -29,121 +30,96 @@ class ConnectionUdp extends Connection {
this._connectionPort = STANDARD_UDP_PORT;
}
chrome.sockets.udp.create({
name: "iNavUDP",
bufferSize: 65535,
}, createInfo => {
this.checkChromeLastError();
if (createInfo && !this._openCanceled) {
chrome.sockets.udp.bind(createInfo.socketId, "0.0.0.0", this._connectionPort, result => {
this.checkChromeLastError();
if (result == 0) {
// UDP connections don't trigger an event if they are interrupted, a simple timeout mechanism must suffice here.
this.addOnReceiveCallback(() => {
if (this._timeoutId) {
clearTimeout(this._timeoutId);
}
this._timeoutId = setTimeout(() => {
if (!this._isCli) { // Disable timeout for CLI
GUI.log(chrome.i18n.getMessage('connectionUdpTimeout'));
this.abort();
}
}, 10000);
})
// Actually useless, but according to chrome documentation also UDP triggers error events ¯\_(ツ)_/¯
this.addOnReceiveErrorListener(info => {
console.error(info);
googleAnalytics.sendException('UDP: ' + info.error, false);
let message;
switch (info.resultCode) {
case -15:
// connection is lost, cannot write to it anymore, preventing further disconnect attempts
message = 'error: ERR_SOCKET_NOT_CONNECTED';
console.log(`UDP: ${message}: ${info.resultCode}`);
this._connectionId = false;
return;
case -21:
message = 'error: NETWORK_CHANGED';
break;
case -100:
message = 'error: CONNECTION_CLOSED';
break;
case -102:
message = 'error: CONNECTION_REFUSED';
break;
case -105:
message = 'error: NAME_NOT_RESOLVED';
break;
case -106:
message = 'error: INTERNET_DISCONNECTED';
break;
case -109:
message = 'error: ADDRESS_UNREACHABLE';
break;
}
let resultMessage = message ? `${message} ${info.resultCode}` : info.resultCode;
console.warn(`UDP: ${resultMessage} ID: ${this._connectionId}`);
this.abort();
});
GUI.log(chrome.i18n.getMessage('connectionConnected', ["udp://" + this._connectionIP + ":" + this._connectionPort]));
try {
socket.bind(this._connectionPort, () => {
GUI.log(i18n.getMessage('connectionConnected', ["udp://" + this._connectionIP + ":" + this._connectionPort]));
if (callback) {
callback({
bitrate: 115200,
connectionId: createInfo.socketId
connectionId: ++this._connectionId
});
}
} else {
console.error("Unable to open UDP socket: " + result);
if (callback) {
callback(false);
}
}
});
} else {
console.error("Unable to create UDP socket.");
if (callback) {
} catch (error) {
console.log(error);
callback(false);
}
}
socket.on('message', (msg, _rinfo) => {
this._onReceiveListeners.forEach(listener => {
listener({
connectionId: ++this._connectionId,
data: msg
});
});
})
socket.on('error', (error) => {
GUI.log("UDP error: " + error);
console.log("UDP error: " + error);
this.abort();
this._onReceiveErrorListeners.forEach(listener => {
listener(error);
});
});
}
disconnectImplementation(callback) {
chrome.sockets.udp.close(this._connectionId);
this.checkChromeLastError();
var ret = true;
try {
socket.disconnect();
} catch (error) {
console.log("Disconecct error: " + error)
ret = false;
}
this._connectionIP = "";
this._connectionPort = 0;
clearTimeout(this._timeoutId);
this._timeoutId = false;
if (callback) {
callback(true);
callback(ret);
}
}
sendImplementation(data, callback) {;
chrome.sockets.udp.send(this._connectionId, data, this._connectionIP, this._connectionPort, callback);
try {
socket.send(Buffer.from(data), this._connectionPort, this._connectionIP, (error) => {
var result = 0;
var sent = data.byteLength;
if (error) {
result = 1;
sent = 0;
console.log("Serial wrire error: " + error)
}
if (callback) {
callback({
bytesSent: sent,
resultCode: result
});
}
});
} catch (error) {
console.log("UDP write error: " + error)
}
}
addOnReceiveCallback(callback){
chrome.sockets.udp.onReceive.addListener(callback);
this._onReceiveErrorListeners.push(callback);
}
removeOnReceiveCallback(callback){
chrome.sockets.udp.onReceive.removeListener(callback);
this._onReceiveListeners = this._onReceiveErrorListeners.filter(listener => listener !== callback);
}
addOnReceiveErrorCallback(callback) {
chrome.sockets.udp.onReceiveError.addListener(callback);
this._onReceiveErrorListeners.push(callback);
}
removeOnReceiveErrorCallback(callback) {
chrome.sockets.udp.onReceiveError.removeListener(callback);
this._onReceiveErrorListeners = this._onReceiveErrorListeners.filter(listener => listener !== callback);
}
}
module.exports = ConnectionUdp;

View file

@ -10,3 +10,5 @@ var CONFIGURATOR = {
'cliValid': false,
'connection': false
};
module.exports = CONFIGURATOR;

View file

@ -1,10 +1,20 @@
/*global mspHelper,$,GUI,MSP,chrome*/
'use strict';
var helper = helper || {};
const { GUI } = require('./../js/gui');
const FC = require('./fc');
const MSP = require('./msp');
const MSPCodes = require('./../js/msp/MSPCodes');
const mspHelper = require('./msp/MSPHelper');
const MSPChainerClass = require('./msp/MSPchainer');
const features = require('./feature_framework');
const periodicStatusUpdater = require('./periodicStatusUpdater');
const { mixer } = require('./model');
const jBox = require('./libraries/jBox/jBox.min');
const i18n = require('./localization');
var savingDefaultsModal;
helper.defaultsDialog = (function () {
var defaultsDialog = (function () {
let publicScope = {},
privateScope = {};
@ -891,17 +901,17 @@ helper.defaultsDialog = (function () {
privateScope.setFeaturesBits = function (selectedDefaultPreset) {
if (selectedDefaultPreset.features && selectedDefaultPreset.features.length > 0) {
helper.features.reset();
features.reset();
for (const feature of selectedDefaultPreset.features) {
if (feature.state) {
helper.features.set(feature.bit);
features.set(feature.bit);
} else {
helper.features.unset(feature.bit);
features.unset(feature.bit);
}
}
helper.features.execute(function () {
features.execute(function () {
privateScope.setSettings(selectedDefaultPreset);
});
} else {
@ -912,7 +922,7 @@ helper.defaultsDialog = (function () {
privateScope.finalize = function (selectedDefaultPreset) {
mspHelper.saveToEeprom(function () {
//noinspection JSUnresolvedVariable
GUI.log(chrome.i18n.getMessage('configurationEepromSaved'));
GUI.log(i18n.getMessage('configurationEepromSaved'));
if (selectedDefaultPreset.reboot) {
GUI.tab_switch_cleanup(function () {
@ -921,7 +931,7 @@ helper.defaultsDialog = (function () {
if (typeof savingDefaultsModal !== 'undefined') {
savingDefaultsModal.close();
}
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
GUI.log(i18n.getMessage('deviceRebooting'));
GUI.handleReconnect();
});
});
@ -930,6 +940,9 @@ helper.defaultsDialog = (function () {
};
privateScope.setSettings = function (selectedDefaultPreset) {
periodicStatusUpdater.stop();
var currentControlProfile = parseInt($("#profilechange").val());
var currentBatteryProfile = parseInt($("#batteryprofilechange").val());
@ -947,9 +960,6 @@ helper.defaultsDialog = (function () {
}
});
//Save analytics
googleAnalytics.sendEvent('Setting', 'Defaults', selectedDefaultPreset.title);
var settingsChainer = MSPChainerClass();
var chain = [];
@ -1016,20 +1026,20 @@ helper.defaultsDialog = (function () {
// Set Mixers
if (selectedDefaultPreset.mixerToApply) {
let currentMixerPreset = helper.mixer.getById(selectedDefaultPreset.mixerToApply);
let currentMixerPreset = mixer.getById(selectedDefaultPreset.mixerToApply);
helper.mixer.loadServoRules(currentMixerPreset);
helper.mixer.loadMotorRules(currentMixerPreset);
mixer.loadServoRules(FC, currentMixerPreset);
mixer.loadMotorRules(FC, currentMixerPreset);
MIXER_CONFIG.platformType = currentMixerPreset.platform;
MIXER_CONFIG.appliedMixerPreset = selectedDefaultPreset.mixerToApply;
MIXER_CONFIG.motorStopOnLow = (currentMixerPreset.motorStopOnLow === true) ? true : false;
MIXER_CONFIG.hasFlaps = (currentMixerPreset.hasFlaps === true) ? true : false;
FC.MIXER_CONFIG.platformType = currentMixerPreset.platform;
FC.MIXER_CONFIG.appliedMixerPreset = selectedDefaultPreset.mixerToApply;
FC.MIXER_CONFIG.motorStopOnLow = (currentMixerPreset.motorStopOnLow === true) ? true : false;
FC.MIXER_CONFIG.hasFlaps = (currentMixerPreset.hasFlaps === true) ? true : false;
SERVO_RULES.cleanup();
SERVO_RULES.inflate();
MOTOR_RULES.cleanup();
MOTOR_RULES.inflate();
FC.SERVO_RULES.cleanup();
FC.SERVO_RULES.inflate();
FC.MOTOR_RULES.cleanup();
FC.MOTOR_RULES.inflate();
chain = chain.concat([
mspHelper.saveMixerConfig,
@ -1057,7 +1067,7 @@ helper.defaultsDialog = (function () {
privateScope.onPresetClick = function (event) {
savingDefaultsModal = new jBox('Modal', {
width: 400,
height: 100,
height: 120,
animation: false,
closeOnClick: false,
closeOnEsc: false,
@ -1097,7 +1107,7 @@ helper.defaultsDialog = (function () {
}
$element.find("a").html(preset.title);
$element.data("index", i).click(privateScope.onPresetClick)
$element.data("index", i).on('click', privateScope.onPresetClick)
$element.appendTo($place);
}
}
@ -1114,3 +1124,5 @@ helper.defaultsDialog = (function () {
return publicScope;
})();
module.exports = defaultsDialog;

View file

@ -1,12 +1,11 @@
'use s';
'use strict';
var helper = helper || {};
/**
* Simple analyzer that returns frequency of events using 5s buffer
* Usage: register periodic events with 'put', then call 'get' to get results
*/
helper.eventFrequencyAnalyzer = (function () {
var eventFrequencyAnalyzer = (function () {
var privateScope = {},
publicScope = {},
@ -75,3 +74,5 @@ helper.eventFrequencyAnalyzer = (function () {
return publicScope;
})();
module.exports = eventFrequencyAnalyzer;

306
js/fc.js
View file

@ -1,84 +1,100 @@
'use strict';
// define all the global variables that are uses to hold FC state
var CONFIG,
LED_STRIP,
LED_COLORS,
LED_MODE_COLORS,
PID,
PID_names,
PIDs,
RC_MAP,
RC,
RC_tuning,
AUX_CONFIG,
AUX_CONFIG_IDS,
MODE_RANGES,
ADJUSTMENT_RANGES,
SERVO_CONFIG,
SERVO_RULES,
MOTOR_RULES,
LOGIC_CONDITIONS,
LOGIC_CONDITIONS_STATUS,
GLOBAL_FUNCTIONS,
GLOBAL_VARIABLES_STATUS,
PROGRAMMING_PID,
PROGRAMMING_PID_STATUS,
SERIAL_CONFIG,
SENSOR_DATA,
MOTOR_DATA,
SERVO_DATA,
GPS_DATA,
ADSB_VEHICLES,
MISSION_PLANNER,
ANALOG,
FC_CONFIG,
MISC,
REVERSIBLE_MOTORS,
DATAFLASH,
SDCARD,
BLACKBOX,
RC_deadband,
SENSOR_ALIGNMENT,
RX_CONFIG,
FAILSAFE_CONFIG,
RXFAIL_CONFIG,
VTX_CONFIG,
ADVANCED_CONFIG,
INAV_PID_CONFIG,
PID_ADVANCED,
FILTER_CONFIG,
SENSOR_STATUS,
SENSOR_CONFIG,
NAV_POSHOLD,
CALIBRATION_DATA,
POSITION_ESTIMATOR,
RTH_AND_LAND_CONFIG,
FW_CONFIG,
DEBUG_TRACE,
MIXER_CONFIG,
BATTERY_CONFIG,
OUTPUT_MAPPING,
SETTINGS,
BRAKING_CONFIG,
SAFEHOMES,
BOARD_ALIGNMENT,
CURRENT_METER_CONFIG,
FEATURES,
RATE_DYNAMICS,
FW_APPROACH,
OSD_CUSTOM_ELEMENTS;
const ServoMixerRuleCollection = require('./servoMixerRuleCollection');
const MotorMixerRuleCollection = require('./motorMixerRuleCollection');
const LogicConditionsCollection = require('./logicConditionsCollection');
const LogicConditionsStatus = require('./logicConditionsStatus');
const GlobalVariablesStatus = require('./globalVariablesStatus');
const ProgrammingPidCollection = require('./programmingPidCollection');
const ProgrammingPidStatus = require('./programmingPidStatus');
const WaypointCollection = require('./waypointCollection');
const OutputMappingCollection = require('./outputMapping');
const SafehomeCollection = require('./safehomeCollection');
const FwApproachCollection = require('./fwApproachCollection')
const { PLATFORM } = require('./model')
const VTX = require('./vtx');
const BitHelper = require('./bitHelper');
var FC = {
// define all the global variables that are uses to hold FC state
CONFIG: null,
LED_STRIP: null,
LED_COLORS: null,
LED_MODE_COLORS: null,
PID: null,
PID_names: null,
PIDs: null,
RC_MAP: null,
RC: null,
RC_tuning: null,
AUX_CONFIG: null,
AUX_CONFIG_IDS: null,
MODE_RANGES: null,
ADJUSTMENT_RANGES: null,
SERVO_CONFIG: null,
SERVO_RULES: null,
MOTOR_RULES: null,
LOGIC_CONDITIONS: null,
LOGIC_CONDITIONS_STATUS: null,
GLOBAL_FUNCTIONS: null,
GLOBAL_VARIABLES_STATUS: null,
PROGRAMMING_PID: null,
PROGRAMMING_PID_STATUS: null,
SERIAL_CONFIG: null,
SENSOR_DATA: null,
MOTOR_DATA: null,
SERVO_DATA: null,
GPS_DATA: null,
ADSB_VEHICLES: null,
MISSION_PLANNER: null,
ANALOG: null,
ARMING_CONFIG: null,
FC_CONFIG: null,
MISC: null,
REVERSIBLE_MOTORS: null,
DATAFLASH: null,
SDCARD: null,
BLACKBOX: null,
RC_deadband: null,
SENSOR_ALIGNMENT: null,
RX_CONFIG: null,
FAILSAFE_CONFIG: null,
RXFAIL_CONFIG: null,
VTX_CONFIG: null,
ADVANCED_CONFIG: null,
INAV_PID_CONFIG: null,
PID_ADVANCED: null,
FILTER_CONFIG: null,
SENSOR_STATUS: null,
SENSOR_CONFIG: null,
NAV_POSHOLD: null,
CALIBRATION_DATA: null,
POSITION_ESTIMATOR: null,
RTH_AND_LAND_CONFIG: null,
FW_CONFIG: null,
DEBUG_TRACE: null,
MIXER_CONFIG: null,
BATTERY_CONFIG: null,
OUTPUT_MAPPING: null,
SETTINGS: null,
BRAKING_CONFIG: null,
SAFEHOMES: null,
BOARD_ALIGNMENT: null,
CURRENT_METER_CONFIG: null,
FEATURES: null,
RATE_DYNAMICS: null,
EZ_TUNE: null,
restartRequired: false,
MAX_SERVO_RATE: 125,
MIN_SERVO_RATE: 0,
isAirplane: function () {
return (MIXER_CONFIG.platformType == PLATFORM_AIRPLANE);
return (this.MIXER_CONFIG.platformType == PLATFORM.AIRPLANE);
},
isMultirotor: function () {
return (MIXER_CONFIG.platformType == PLATFORM_MULTIROTOR || MIXER_CONFIG.platformType == PLATFORM_TRICOPTER);
return (this.MIXER_CONFIG.platformType == PLATFORM.MULTIROTOR || this.MIXER_CONFIG.platformType == PLATFORM.TRICOPTER);
},
isRpyFfComponentUsed: function () {
return true; // Currently all planes have roll, pitch and yaw FF
@ -87,7 +103,7 @@ var FC = {
return true; // Currently all platforms use D term
},
resetState: function () {
SENSOR_STATUS = {
this.SENSOR_STATUS = {
isHardwareHealthy: 0,
gyroHwStatus: 0,
accHwStatus: 0,
@ -99,7 +115,7 @@ var FC = {
flowHwStatus: 0
};
SENSOR_CONFIG = {
this.SENSOR_CONFIG = {
accelerometer: 0,
barometer: 0,
magnetometer: 0,
@ -108,7 +124,7 @@ var FC = {
opflow: 0
};
CONFIG = {
this.CONFIG = {
apiVersion: "0.0.0",
flightControllerIdentifier: '',
flightControllerVersion: '',
@ -130,40 +146,40 @@ var FC = {
name: ''
};
BOARD_ALIGNMENT = {
this.BOARD_ALIGNMENT = {
roll: 0,
pitch: 0,
yaw: 0
};
CURRENT_METER_CONFIG = {
this.CURRENT_METER_CONFIG = {
scale: 0,
offset: 0,
type: 0,
capacity: 0
};
LED_STRIP = [];
LED_COLORS = [];
LED_MODE_COLORS = [];
this.LED_STRIP = [];
this.LED_COLORS = [];
this.LED_MODE_COLORS = [];
FEATURES = 0;
this.FEATURES = 0;
PID = {
this.PID = {
};
PID_names = [];
PIDs = [];
RC_MAP = [];
this.PID_names = [];
this.PIDs = [];
this.RC_MAP = [];
// defaults
// roll, pitch, yaw, throttle, aux 1, ... aux n
RC = {
this.RC = {
active_channels: 0,
channels: new Array(32)
};
RC_tuning = {
this.RC_tuning = {
RC_RATE: 0,
RC_EXPO: 0,
roll_pitch_rate: 0, // pre 1.7 api only
@ -182,22 +198,22 @@ var FC = {
manual_yaw_rate: 0,
};
AUX_CONFIG = [];
AUX_CONFIG_IDS = [];
this.AUX_CONFIG = [];
this.AUX_CONFIG_IDS = [];
MODE_RANGES = [];
ADJUSTMENT_RANGES = [];
this.MODE_RANGES = [];
this.ADJUSTMENT_RANGES = [];
SERVO_CONFIG = [];
SERVO_RULES = new ServoMixerRuleCollection();
MOTOR_RULES = new MotorMixerRuleCollection();
LOGIC_CONDITIONS = new LogicConditionsCollection();
LOGIC_CONDITIONS_STATUS = new LogicConditionsStatus();
GLOBAL_VARIABLES_STATUS = new GlobalVariablesStatus();
PROGRAMMING_PID = new ProgrammingPidCollection();
PROGRAMMING_PID_STATUS = new ProgrammingPidStatus();
this.SERVO_CONFIG = [];
this.SERVO_RULES = new ServoMixerRuleCollection();
this.MOTOR_RULES = new MotorMixerRuleCollection();
this.LOGIC_CONDITIONS = new LogicConditionsCollection();
this.LOGIC_CONDITIONS_STATUS = new LogicConditionsStatus();
this.GLOBAL_VARIABLES_STATUS = new GlobalVariablesStatus();
this.PROGRAMMING_PID = new ProgrammingPidCollection();
this.PROGRAMMING_PID_STATUS = new ProgrammingPidStatus();
MIXER_CONFIG = {
this.MIXER_CONFIG = {
yawMotorDirection: 0,
yawJumpPreventionLimit: 0,
motorStopOnLow: false,
@ -208,7 +224,7 @@ var FC = {
numberOfServos: 0
},
SERIAL_CONFIG = {
this.SERIAL_CONFIG = {
ports: [],
// pre 1.6 settings
@ -218,7 +234,7 @@ var FC = {
cliBaudRate: 0
};
SENSOR_DATA = {
this.SENSOR_DATA = {
gyroscope: [0, 0, 0],
accelerometer: [0, 0, 0],
magnetometer: [0, 0, 0],
@ -231,10 +247,10 @@ var FC = {
debug: [0, 0, 0, 0]
};
MOTOR_DATA = new Array(8);
SERVO_DATA = new Array(16);
this.MOTOR_DATA = new Array(8);
this.SERVO_DATA = new Array(16);
GPS_DATA = {
this.GPS_DATA = {
fix: 0,
numSat: 0,
lat: 0,
@ -254,15 +270,15 @@ var FC = {
packetCount: 0
};
ADSB_VEHICLES = {
this.ADSB_VEHICLES = {
vehiclesCount: 0,
callsignLength: 0,
vehicles: []
};
MISSION_PLANNER = new WaypointCollection();
this.MISSION_PLANNER = new WaypointCollection();
ANALOG = {
this.ANALOG = {
voltage: 0,
mAhdrawn: 0,
mWhdrawn: 0,
@ -277,11 +293,11 @@ var FC = {
battery_flags: 0
};
FC_CONFIG = {
this.FC_CONFIG = {
loopTime: 0
};
MISC = {
this.MISC = {
midrc: 0,
minthrottle: 0,
maxthrottle: 0,
@ -306,7 +322,7 @@ var FC = {
battery_capacity_unit: 'mAh'
};
BATTERY_CONFIG = {
this.BATTERY_CONFIG = {
vbatscale: 0,
vbatdetectcellvoltage: 0,
vbatmincellvoltage: 0,
@ -320,7 +336,7 @@ var FC = {
capacity_unit: 0
};
VTX_CONFIG = {
this.VTX_CONFIG = {
device_type: VTX.DEV_UNKNOWN,
band: 0,
channel: 1,
@ -329,7 +345,7 @@ var FC = {
low_power_disarm: 0,
};
ADVANCED_CONFIG = {
this.ADVANCED_CONFIG = {
gyroSyncDenominator: null,
pidProcessDenom: null,
useUnsyncedPwm: null,
@ -339,7 +355,7 @@ var FC = {
gyroSync: null
};
FILTER_CONFIG = {
this.FILTER_CONFIG = {
gyroSoftLpfHz: null,
dtermLpfHz: null,
yawLpfHz: null,
@ -354,7 +370,7 @@ var FC = {
gyroStage2LowpassHz: null
};
PID_ADVANCED = {
this.PID_ADVANCED = {
rollPitchItermIgnoreRate: null,
yawItermIgnoreRate: null,
yawPLimit: null,
@ -364,7 +380,7 @@ var FC = {
pidSumLimit: null
};
INAV_PID_CONFIG = {
this.INAV_PID_CONFIG = {
asynchronousMode: null,
accelerometerTaskFrequency: null,
attitudeTaskFrequency: null,
@ -375,7 +391,7 @@ var FC = {
accSoftLpfHz: null
};
NAV_POSHOLD = {
this.NAV_POSHOLD = {
userControlMode: null,
maxSpeed: null,
maxClimbRate: null,
@ -386,7 +402,7 @@ var FC = {
hoverThrottle: null
};
CALIBRATION_DATA = {
this.CALIBRATION_DATA = {
acc: {
Pos0: null,
Pos1: null,
@ -420,7 +436,7 @@ var FC = {
}
};
RTH_AND_LAND_CONFIG = {
this.RTH_AND_LAND_CONFIG = {
minRthDistance: null,
rthClimbFirst: null,
rthClimbIgnoreEmergency: null,
@ -435,14 +451,14 @@ var FC = {
emergencyDescentRate: null
};
REVERSIBLE_MOTORS = {
this.REVERSIBLE_MOTORS = {
deadband_low: 0,
deadband_high: 0,
neutral: 0,
deadband_throttle: 0
};
DATAFLASH = {
this.DATAFLASH = {
ready: false,
supported: false,
sectors: 0,
@ -450,7 +466,7 @@ var FC = {
usedSize: 0
};
SDCARD = {
this.SDCARD = {
supported: false,
state: 0,
filesystemLastError: 0,
@ -458,27 +474,27 @@ var FC = {
totalSizeKB: 0
};
BLACKBOX = {
this.BLACKBOX = {
supported: false,
blackboxDevice: 0,
blackboxRateNum: 1,
blackboxRateDenom: 1
};
RC_deadband = {
this.RC_deadband = {
deadband: 0,
yaw_deadband: 0,
alt_hold_deadband: 0
};
SENSOR_ALIGNMENT = {
this.SENSOR_ALIGNMENT = {
align_gyro: 0,
align_acc: 0,
align_mag: 0,
align_opflow: 0
};
RX_CONFIG = {
this.RX_CONFIG = {
receiver_type: 0,
serialrx_provider: 0,
maxcheck: 0,
@ -492,7 +508,7 @@ var FC = {
spirx_channel_count: 0,
};
POSITION_ESTIMATOR = {
this.POSITION_ESTIMATOR = {
w_z_baro_p: null,
w_z_gps_p: null,
w_z_gps_v: null,
@ -502,7 +518,7 @@ var FC = {
use_gps_velned: null
};
FAILSAFE_CONFIG = {
this.FAILSAFE_CONFIG = {
failsafe_delay: 0,
failsafe_off_delay: 0,
failsafe_throttle: 0,
@ -518,7 +534,7 @@ var FC = {
failsafe_min_distance_procedure: 0
};
FW_CONFIG = {
this.FW_CONFIG = {
cruiseThrottle: null,
minThrottle: null,
maxThrottle: null,
@ -529,7 +545,7 @@ var FC = {
loiterRadius: null
};
BRAKING_CONFIG = {
this.BRAKING_CONFIG = {
speedThreshold: null,
disengageSpeed: null,
timeout: null,
@ -540,15 +556,15 @@ var FC = {
bankAngle: null
}
RXFAIL_CONFIG = [];
this.RXFAIL_CONFIG = [];
OUTPUT_MAPPING = new OutputMappingCollection();
this.OUTPUT_MAPPING = new OutputMappingCollection();
SETTINGS = {};
this.SETTINGS = {};
SAFEHOMES = new SafehomeCollection();
this.SAFEHOMES = new SafehomeCollection();
RATE_DYNAMICS = {
this.RATE_DYNAMICS = {
sensitivityCenter: null,
sensitivityEnd: null,
correctionCenter: null,
@ -557,7 +573,7 @@ var FC = {
weightEnd: null
};
EZ_TUNE = {
this.EZ_TUNE = {
enabled: null,
filterHz: null,
axisRatio: null,
@ -571,9 +587,9 @@ var FC = {
};
FW_APPROACH = new FwApproachCollection();
this.FW_APPROACH = new FwApproachCollection();
OSD_CUSTOM_ELEMENTS = {
this.OSD_CUSTOM_ELEMENTS = {
settings: {customElementsCount: 0, customElementTextSize: 0},
items: [],
};
@ -619,7 +635,7 @@ var FC = {
features = this.getFeatures();
}
for (var i = 0; i < features.length; i++) {
if (features[i].name == featureName && bit_check(FEATURES, features[i].bit)) {
if (features[i].name == featureName && BitHelper.bit_check(this.FEATURES, features[i].bit)) {
return true;
}
}
@ -805,14 +821,14 @@ var FC = {
var calibrated = true;
var flagNames = FC.getArmingFlags();
if (CALIBRATION_DATA.accGain.X === 4096 && CALIBRATION_DATA.accGain.Y === 4096 && CALIBRATION_DATA.accGain.Z === 4096 &&
CALIBRATION_DATA.accZero.X === 0 && CALIBRATION_DATA.accZero.Y === 0 && CALIBRATION_DATA.accZero.Z === 0
if (this.CALIBRATION_DATA.accGain.X === 4096 && this.CALIBRATION_DATA.accGain.Y === 4096 && this.CALIBRATION_DATA.accGain.Z === 4096 &&
this.CALIBRATION_DATA.accZero.X === 0 && this.CALIBRATION_DATA.accZero.Y === 0 && this.CALIBRATION_DATA.accZero.Z === 0
) {
calibrated = false;
}
if ((calibrated) && flagNames.hasOwnProperty(13)) {
if (bit_check(CONFIG.armingFlags, 13)) {
if (BitHelper.bit_check(this.CONFIG.armingFlags, 13)) {
calibrated = false;
}
}
@ -924,14 +940,14 @@ var FC = {
return this.getServoMixInputNames()[input];
},
getModeId: function (name) {
for (var i = 0; i < AUX_CONFIG.length; i++) {
if (AUX_CONFIG[i] == name)
for (var i = 0; i < this.AUX_CONFIG.length; i++) {
if (this.AUX_CONFIG[i] == name)
return i;
}
return -1;
},
isModeBitSet: function (i) {
return bit_check(CONFIG.mode[Math.trunc(i / 32)], i % 32);
return BitHelper.bit_check(this.CONFIG.mode[Math.trunc(i / 32)], i % 32);
},
isModeEnabled: function (name) {
return this.isModeBitSet(this.getModeId(name));
@ -1350,7 +1366,7 @@ var FC = {
4: {
name: "Logic Condition",
type: "range",
range: [0, (LOGIC_CONDITIONS.getMaxLogicConditionCount()-1)],
range: [0, (this.LOGIC_CONDITIONS.getMaxLogicConditionCount()-1)],
default: 0
},
5: {
@ -1542,3 +1558,5 @@ var FC = {
return ($.inArray(paramName, this.getControlProfileParameters()) != -1);
}
};
module.exports = FC;

View file

@ -1,8 +1,5 @@
/*global mspHelper,FEATURES,bit_clear,bit_set*/
'use strict';
var helper = helper || {};
/*
Helper to work with FEATURES via MSP
@ -23,7 +20,12 @@ helper.features.execute(function () {
});
*/
helper.features = (function() {
const mspHelper = require('./msp/MSPHelper');
const BitHelper = require('./bitHelper');
const FC = require('./fc');
var features = (function() {
let publicScope = {},
privateScope = {};
@ -48,7 +50,7 @@ helper.features = (function() {
publicScope.updateUI = function ($container, values) {
$container.find('[data-bit].feature').each(function () {
let $this = $(this);
$this.prop('checked', bit_check(values, $this.attr("data-bit")));
$this.prop('checked', BitHelper.bit_check(values, $this.attr("data-bit")));
});
};
@ -73,11 +75,11 @@ helper.features = (function() {
privateScope.setBits = function () {
for (const bit of toSet) {
FEATURES = bit_set(FEATURES, bit);
FC.FEATURES = BitHelper.bit_set(FC.FEATURES, bit);
}
for (const bit of toUnset) {
FEATURES = bit_clear(FEATURES, bit);
FC.FEATURES = BitHelper.bit_clear(FC.FEATURES, bit);
}
mspHelper.saveFeatures(exitPoint);
@ -85,3 +87,5 @@ helper.features = (function() {
return publicScope;
})();
module.exports = features;

View file

@ -1,4 +1,3 @@
/*global $*/
'use strict';
const ApproachDirection = Object.freeze({
@ -105,3 +104,5 @@ let FwApproach = function (number, approachAltAsl = 0, landAltAsl = 0, approachD
return self;
};
module.exports = { ApproachDirection, FwApproach };

View file

@ -1,5 +1,7 @@
'use strict';
const BitHelper = require('./bitHelper');
let FwApproachCollection = function () {
let self = {},
@ -66,19 +68,19 @@ let FwApproachCollection = function () {
let fwApproach = data[fwApproachId];
if (fwApproachId < self.fwApproachCount()) {
buffer.push(fwApproach.getNumber()); // sbufReadU8(src); // number
buffer.push(specificByte(fwApproach.getApproachAltAsl(), 0));
buffer.push(specificByte(fwApproach.getApproachAltAsl(), 1));
buffer.push(specificByte(fwApproach.getApproachAltAsl(), 2));
buffer.push(specificByte(fwApproach.getApproachAltAsl(), 3));
buffer.push(specificByte(fwApproach.getLandAltAsl(), 0));
buffer.push(specificByte(fwApproach.getLandAltAsl(), 1));
buffer.push(specificByte(fwApproach.getLandAltAsl(), 2));
buffer.push(specificByte(fwApproach.getLandAltAsl(), 3));
buffer.push(BitHelper.specificByte(fwApproach.getApproachAltAsl(), 0));
buffer.push(BitHelper.specificByte(fwApproach.getApproachAltAsl(), 1));
buffer.push(BitHelper.specificByte(fwApproach.getApproachAltAsl(), 2));
buffer.push(BitHelper.specificByte(fwApproach.getApproachAltAsl(), 3));
buffer.push(BitHelper.specificByte(fwApproach.getLandAltAsl(), 0));
buffer.push(BitHelper.specificByte(fwApproach.getLandAltAsl(), 1));
buffer.push(BitHelper.specificByte(fwApproach.getLandAltAsl(), 2));
buffer.push(BitHelper.specificByte(fwApproach.getLandAltAsl(), 3));
buffer.push(fwApproach.getApproachDirection());
buffer.push(specificByte(fwApproach.getLandHeading1(), 0));
buffer.push(specificByte(fwApproach.getLandHeading1(), 1));
buffer.push(specificByte(fwApproach.getLandHeading2(), 0));
buffer.push(specificByte(fwApproach.getLandHeading2(), 1));
buffer.push(BitHelper.specificByte(fwApproach.getLandHeading1(), 0));
buffer.push(BitHelper.specificByte(fwApproach.getLandHeading1(), 1));
buffer.push(BitHelper.specificByte(fwApproach.getLandHeading2(), 0));
buffer.push(BitHelper.specificByte(fwApproach.getLandHeading2(), 1));
buffer.push(fwApproach.getIsSeaLevelRef());
} else {
buffer = Array(15).fill(0);
@ -90,3 +92,5 @@ let FwApproachCollection = function () {
return self;
};
module.exports = FwApproachCollection;

26
js/globalSettings.js Normal file
View file

@ -0,0 +1,26 @@
const UnitType = {
none: "none",
OSD: "OSD",
imperial: "imperial",
metric: "metric",
}
var globalSettings = {
// Configurator rendering options
// Used to depict how the units are displayed within the UI
unitType: null,
// Used to convert units within the UI
osdUnits: null,
// Map
mapProviderType: null,
mapApiKey: null,
proxyURL: null,
proxyLayer: null,
// Show colours for profiles
showProfileParameters: null,
// tree target for documents
docsTreeLocation: 'master',
cliAutocomplete: true,
};
module.exports = { globalSettings, UnitType };

40
js/globalUpdates.js Normal file
View file

@ -0,0 +1,40 @@
'use strict'
const CONFIGURATOR = require('./data_storage');
const FC = require('./fc');
const { globalSettings } = require('./globalSettings');
const i18n = require('./localization');
var update = {
activatedTab: function() {
var activeTab = $('#tabs > ul li.active');
activeTab.removeClass('active');
$('a', activeTab).trigger('click');
},
firmwareVersion: function() {
if (CONFIGURATOR.connectionValid) {
$('#logo .firmware_version').text(FC.CONFIG.flightControllerVersion + " [" + FC.CONFIG.target + "]");
globalSettings.docsTreeLocation = 'https://github.com/iNavFlight/inav/blob/' + FC.CONFIG.flightControllerVersion + '/docs/';
// If this is a master branch firmware, this will find a 404 as there is no tag tree. So default to master for docs.
$.ajax({
url: globalSettings.docsTreeLocation + 'Settings.md',
method: "HEAD",
statusCode: {
404: function () {
globalSettings.docsTreeLocation = 'https://github.com/iNavFlight/inav/blob/master/docs/';
}
}
});
} else {
$('#logo .firmware_version').text(i18n.getMessage('fcNotConnected'));
globalSettings.docsTreeLocation = 'https://github.com/iNavFlight/inav/blob/master/docs/';
}
}
};
module.exports = update;

View file

@ -1,6 +1,6 @@
'use strict';
let GlobalVariablesStatus = function () {
var GlobalVariablesStatus = function () {
let self = {},
data = [];
@ -51,3 +51,5 @@ let GlobalVariablesStatus = function () {
return self;
};
module.exports = GlobalVariablesStatus;

View file

@ -1,8 +1,15 @@
'use strict';
var helper = helper || {};
const path = require('path');
const ol = require('openlayers');
helper.groundstation = (function () {
const { GUI } = require('./gui');
const ltmDecoder = require('./ltmDecoder');
const interval = require('./intervals');
const { globalSettings } = require('./globalSettings');
const i18n = require('./localization');
const groundstation = (function () {
let publicScope = {},
privateScope = {};
@ -37,14 +44,15 @@ helper.groundstation = (function () {
return;
}
helper.interval.add('gsUpdateGui', privateScope.updateGui, 200);
interval.add('gsUpdateGui', privateScope.updateGui, 200);
privateScope.$viewport = $viewport;
privateScope.$viewport.find(".tab_container").hide();
privateScope.$viewport.find('#content').hide();
privateScope.$viewport.find('#status-bar').hide();
privateScope.$viewport.find('#connectbutton a.connect_state').text(chrome.i18n.getMessage('disconnect')).addClass('active');
privateScope.$viewport.find('#connectbutton a.connect_state').text(i18n.getMessage('disconnect'));
privateScope.$viewport.find('#connectbutton a.connect').addClass('active');
privateScope.$gsViewport = $viewport.find('#view-groundstation');
privateScope.$gsViewport.show();
@ -53,7 +61,7 @@ helper.groundstation = (function () {
setTimeout(privateScope.initMap, 100);
privateScope.activated = true;
GUI.log(chrome.i18n.getMessage('gsActivated'));
GUI.log(i18n.getMessage('gsActivated'));
}
privateScope.initMap = function () {
@ -98,7 +106,7 @@ helper.groundstation = (function () {
return;
}
helper.interval.remove('gsUpdateGui');
interval.remove('gsUpdateGui');
if (privateScope.$viewport !== null) {
privateScope.$viewport.find(".tab_container").show();
@ -111,12 +119,12 @@ helper.groundstation = (function () {
}
privateScope.activated = false;
GUI.log(chrome.i18n.getMessage('gsDeactivated'));
GUI.log(i18n.getMessage('gsDeactivated'));
}
privateScope.updateGui = function () {
let telemetry = helper.ltmDecoder.get();
let telemetry = ltmDecoder.get();
if (telemetry.gpsFix && telemetry.gpsFix > 1) {
@ -132,7 +140,7 @@ helper.groundstation = (function () {
anchor: [0.5, 0.5],
opacity: 1,
scale: 0.6,
src: '../images/icons/icon_mission_airplane.png'
src: path.join(__dirname, './../images/icons/icon_mission_airplane.png')
}))
});
privateScope.cursorPosition = new ol.geom.Point(ol.proj.fromLonLat([lon, lat]));
@ -192,3 +200,5 @@ helper.groundstation = (function () {
return publicScope;
})();
module.exports = groundstation;

101
js/gui.js
View file

@ -1,6 +1,14 @@
/*global $*/
'use strict';
const CONFIGURATOR = require('./data_storage');
const Switchery = require('./libraries/switchery/switchery')
const MSP = require('./msp');
const FC = require('./fc');
const interval = require('./intervals');
const mspBalancedInterval = require('./msp_balanced_interval');
const { scaleRangeInt } = require('./helpers');
const i18n = require('./localization');
var TABS = {}; // filled by individual tab js file
var GUI_control = function () {
@ -54,16 +62,6 @@ var GUI_control = function () {
else if (navigator.appVersion.indexOf("X11") != -1) this.operating_system = "UNIX";
else this.operating_system = "Unknown";
this.colorTable = [
"#8ecae6",
"#2a9d8f",
"#e9c46a",
"#f4a261",
"#e76f51",
"#ef476f",
"#ffc300"
];
};
// message = string
@ -93,8 +91,8 @@ GUI_control.prototype.log = function (message) {
GUI_control.prototype.tab_switch_cleanup = function (callback) {
MSP.callbacks_cleanup(); // we don't care about any old data that might or might not arrive
helper.interval.killAll(['global_data_refresh', 'msp-load-update']);
helper.mspBalancedInterval.flush();
interval.killAll(['global_data_refresh', 'msp-load-update']);
mspBalancedInterval.flush();
if (this.active_tab) {
TABS[this.active_tab].cleanup(callback);
@ -183,7 +181,7 @@ GUI_control.prototype.content_ready = function (callback) {
const documentationDiv = $('<div>').addClass('cf_doc_version_bt');
$('<a>').attr('href', 'https://github.com/iNavFlight/inav/wiki')
.attr('target', '_blank').attr('id', 'button-documentation')
.html(chrome.i18n.getMessage('documentation')).appendTo(documentationDiv);
.html(i18n.getMessage('documentation')).appendTo(documentationDiv);
documentationDiv.insertAfter(tabTitle);
// loading tooltip
@ -249,26 +247,26 @@ GUI_control.prototype.updateStatusBar = function() {
var activeArmFlags = [];
for(var i=0;i<32;i++) {
var checkBit = (1 << i);
if(Object.values(armingFlags).includes(checkBit) && (checkBit & CONFIG.armingFlags)) {
if(Object.values(armingFlags).includes(checkBit) && (checkBit & FC.CONFIG.armingFlags)) {
activeArmFlags.push(Object.keys(armingFlags)[Object.values(armingFlags).indexOf(checkBit)]);
}
}
$('span.i2c-error').text(CONFIG.i2cError);
$('span.cycle-time').text(CONFIG.cycleTime);
$('span.cpu-load').text(chrome.i18n.getMessage('statusbar_cpu_load', [CONFIG.cpuload]));
$('span.i2c-error').text(FC.CONFIG.i2cError);
$('span.cycle-time').text(FC.CONFIG.cycleTime);
$('span.cpu-load').text(i18n.getMessage('statusbar_cpu_load', [FC.CONFIG.cpuload]));
$('span.arming-flags').text(activeArmFlags.length ? activeArmFlags.join(', ') : '-');
};
GUI_control.prototype.updateProfileChange = function(refresh) {
$('#mixerprofilechange').val(CONFIG.mixer_profile);
$('#profilechange').val(CONFIG.profile);
$('#batteryprofilechange').val(CONFIG.battery_profile);
$('#mixerprofilechange').val(FC.CONFIG.mixer_profile);
$('#profilechange').val(FC.CONFIG.profile);
$('#batteryprofilechange').val(FC.CONFIG.battery_profile);
if (refresh) {
GUI.log(chrome.i18n.getMessage('loadedMixerProfile', [CONFIG.mixer_profile + 1]));
GUI.log(chrome.i18n.getMessage('pidTuning_LoadedProfile', [CONFIG.profile + 1]));
GUI.log(chrome.i18n.getMessage('loadedBatteryProfile', [CONFIG.battery_profile + 1]));
updateActivatedTab();
GUI.log(i18n.getMessage('loadedMixerProfile', [FC.CONFIG.mixer_profile + 1]));
GUI.log(i18n.getMessage('pidTuning_LoadedProfile', [FC.CONFIG.profile + 1]));
GUI.log(i18n.getMessage('loadedBatteryProfile', [FC.CONFIG.battery_profile + 1]));
GUI.updateActivatedTab();
}
};
@ -302,7 +300,7 @@ GUI_control.prototype.simpleBind = function () {
return;
}
$this.change(function () {
$this.on('change', function () {
window[toBind[0]][toBind[1]] = $(this).val();
});
@ -366,7 +364,7 @@ GUI_control.prototype.renderOperandValue = function ($container, operandMetadata
break;
}
$container.find('.logic_element__operand--value').change(onChange);
$container.find('.logic_element__operand--value').on('change', onChange);
};
/**
@ -379,7 +377,7 @@ GUI_control.prototype.renderOperandValue = function ($container, operandMetadata
GUI_control.prototype.renderLogicConditionSelect = function ($container, logicConditions, current, onChange, withAlways, onlyEnabled) {
let $select = $container.append('<select class="mix-rule-condition">').find("select"),
lcCount = logicConditions.getCount();
lcCount = logicConditions.getCount(),
option = "";
if (withAlways) {
@ -399,7 +397,7 @@ GUI_control.prototype.renderLogicConditionSelect = function ($container, logicCo
}
}
$select.val(current).change(onChange);
$select.val(current).on('change', onChange);
}
GUI_control.prototype.sliderize = function ($input, value, min, max) {
@ -488,5 +486,50 @@ GUI_control.prototype.sliderize = function ($input, value, min, max) {
$input.trigger('change');
};
GUI_control.prototype.update_dataflash_global = function () {
function formatFilesize(bytes) {
if (bytes < 1024) {
return bytes + "B";
}
var kilobytes = bytes / 1024;
if (kilobytes < 1024) {
return Math.round(kilobytes) + "kB";
}
var megabytes = kilobytes / 1024;
return megabytes.toFixed(1) + "MB";
}
var supportsDataflash = FC.DATAFLASH.totalSize > 0;
if (supportsDataflash){
$(".noflash_global").css({
display: 'none'
});
$(".dataflash-contents_global").css({
display: 'block'
});
$(".dataflash-free_global").css({
width: (100-(FC.DATAFLASH.totalSize - FC.DATAFLASH.usedSize) / FC.DATAFLASH.totalSize * 100) + "%",
display: 'block'
});
$(".dataflash-free_global div").text('Dataflash: free ' + formatFilesize(FC.DATAFLASH.totalSize - FC.DATAFLASH.usedSize));
} else {
$(".noflash_global").css({
display: 'block'
});
$(".dataflash-contents_global").css({
display: 'none'
});
}
};
// initialize object into GUI variable
var GUI = new GUI_control();
module.exports = { GUI, TABS };

View file

@ -1,19 +1,6 @@
/*global $*/
'use strict';
function checkChromeRuntimeError() {
if (chrome.runtime.lastError) {
console.error(
`Chrome API Error: ${chrome.runtime.lastError.message}.\n Traced ${
new Error().stack
}`
);
return true;
}
return false;
}
function constrain(input, min, max) {
if (input < min) {
@ -114,3 +101,5 @@ function calculate_new_cooridatnes(coord, bearing, distance)
lon: rad2Deg(lonNew),
}
}
module.exports = { constrain, zeroPad, generateFilename, scaleRangeInt, distanceOnLine, wrap_360, rad2Deg, calculate_new_cooridatnes }

View file

@ -1,8 +1,6 @@
'use strict';
var helper = helper || {};
helper.interval = (function () {
var interval = function () {
var privateScope = {},
publicScope = {};
@ -134,4 +132,6 @@ helper.interval = (function () {
};
return publicScope;
})();
}();
module.exports = interval;

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<link type="text/css" rel="stylesheet" href="style.css" media="all" />
<script src="renderer.js"></script>
<title>Bluetooth Device Chooser</title>
</head>
<body>
<h1>Select Bluetooth device:</h1>
<div id="list">
<div id="cancel" class="item">
Cancel
</div>
</div
</body>
</html>

View file

@ -0,0 +1,6 @@
const { contextBridge, ipcRenderer } = require('electron/renderer');
contextBridge.exposeInMainWorld('electronAPI', {
bleScan: (callback) => ipcRenderer.on('ble-scan', (_event, data) => callback(data)),
deviceSelected: (deviceId) => ipcRenderer.send('deviceSelected', deviceId)
});

View file

@ -0,0 +1,23 @@
document.addEventListener("DOMContentLoaded", () => {
window.electronAPI.bleScan(data => {
data.forEach(device => {
var dev = document.getElementById(device.deviceId)
if (dev) {
dev.parentElement.removeChild(dev);
}
var item = document.createElement('div');
item.className = 'item'
item.id = device.deviceId;
item.addEventListener('click', () => {
window.electronAPI.deviceSelected(item.id);
window.close();
});
var text = device.deviceName + ' (' + device.deviceId + ')';
item.appendChild(document.createTextNode(text.length > 45 ? device.deviceName.substring(0, 45) : text.substring(0, 45)));
document.getElementById('list').prepend(item);
});
});
document.getElementById('cancel').addEventListener('click', () => {
window.close();
});
});

View file

@ -0,0 +1,38 @@
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: black;
margin: 10px;
}
h1 {
font-size: 20px;
font-weight: bold;
color: white;
}
#id {
margin: 5px;
}
#list {
color: white;
}
.item {
margin: 5px;
padding: 5px;
height: 40px;
line-height: 40px;
width: 345px;
display: inline-block;
vertical-align: middle;
border: 1px solid whitesmoke;
}
.item:hover {
background-color: rgb(88, 88, 192);
}
#cancel {
text-align: center;
}

View file

@ -548,14 +548,14 @@ Buffer.concat = function concat (list, length) {
var i
if (length === undefined) {
length = 0
for (i = 0; i < list.length; ++i) {
for (let i = 0; i < list.length; ++i) {
length += list[i].length
}
}
var buffer = Buffer.allocUnsafe(length)
var pos = 0
for (i = 0; i < list.length; ++i) {
for (let i = 0; i < list.length; ++i) {
var buf = list[i]
if (isInstance(buf, Uint8Array)) {
buf = Buffer.from(buf)
@ -922,7 +922,7 @@ function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
var i
if (dir) {
var foundIndex = -1
for (i = byteOffset; i < arrLength; i++) {
for (let i = byteOffset; i < arrLength; i++) {
if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
if (foundIndex === -1) foundIndex = i
if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
@ -933,7 +933,7 @@ function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
}
} else {
if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
for (i = byteOffset; i >= 0; i--) {
for (let i = byteOffset; i >= 0; i--) {
var found = true
for (var j = 0; j < valLength; j++) {
if (read(arr, i + j) !== read(val, j)) {
@ -1759,7 +1759,7 @@ Buffer.prototype.fill = function fill (val, start, end, encoding) {
var i
if (typeof val === 'number') {
for (i = start; i < end; ++i) {
for (let i = start; i < end; ++i) {
this[i] = val
}
} else {
@ -1771,7 +1771,7 @@ Buffer.prototype.fill = function fill (val, start, end, encoding) {
throw new TypeError('The value "' + val +
'" is invalid for argument "value"')
}
for (i = 0; i < end - start; ++i) {
for (let i = 0; i < end - start; ++i) {
this[i + start] = bytes[i % len]
}
}
@ -2240,7 +2240,7 @@ EventEmitter.prototype.emit = function emit(type) {
// slower
default:
args = new Array(len - 1);
for (i = 1; i < len; i++)
for (let i = 1; i < len; i++)
args[i - 1] = arguments[i];
emitMany(handler, isFn, this, args);
}
@ -2399,7 +2399,7 @@ EventEmitter.prototype.removeListener =
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
for (let i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
@ -2451,7 +2451,7 @@ EventEmitter.prototype.removeAllListeners =
if (arguments.length === 0) {
var keys = objectKeys(events);
var key;
for (i = 0; i < keys.length; ++i) {
for (let i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
@ -2468,7 +2468,7 @@ EventEmitter.prototype.removeAllListeners =
this.removeListener(type, listeners);
} else if (listeners) {
// LIFO order
for (i = listeners.length - 1; i >= 0; i--) {
for (let i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
@ -5815,12 +5815,12 @@ module.exports = deprecate;
* Mark that a method should not be used.
* Returns a modified function which warns once by default.
*
* If `localStorage.noDeprecation = true` is set, then it is a no-op.
* If `localstore.noDeprecation = true` is set, then it is a no-op.
*
* If `localStorage.throwDeprecation = true` is set, then deprecated functions
* If `localstore.throwDeprecation = true` is set, then deprecated functions
* will throw an Error when invoked.
*
* If `localStorage.traceDeprecation = true` is set, then deprecated functions
* If `localstore.traceDeprecation = true` is set, then deprecated functions
* will invoke `console.trace()` instead of `console.error()`.
*
* @param {Function} fn - the function to deprecate
@ -7516,7 +7516,7 @@ function config (name) {
element.txt(obj);
}
} else if (Array.isArray(obj)) {
for (index in obj) {
for (let index in obj) {
if (!hasProp.call(obj, index)) continue;
child = obj[index];
for (key in child) {
@ -7542,7 +7542,7 @@ function config (name) {
element = element.txt(child);
}
} else if (Array.isArray(child)) {
for (index in child) {
for (let index in child) {
if (!hasProp.call(child, index)) continue;
entry = child[index];
if (typeof entry === 'string') {
@ -7686,7 +7686,7 @@ function config (name) {
processItem = function(processors, item, key) {
var i, len, process;
for (i = 0, len = processors.length; i < len; i++) {
for (let i = 0, len = processors.length; i < len; i++) {
process = processors[i];
item = process(item, key);
}
@ -7865,7 +7865,7 @@ function config (name) {
xpath = "/" + ((function() {
var i, len, results;
results = [];
for (i = 0, len = stack.length; i < len; i++) {
for (let i = 0, len = stack.length; i < len; i++) {
node = stack[i];
results.push(node["#name"]);
}
@ -8106,7 +8106,7 @@ function config (name) {
if (isFunction(Object.assign)) {
Object.assign.apply(null, arguments);
} else {
for (i = 0, len = sources.length; i < len; i++) {
for (let i = 0, len = sources.length; i < len; i++) {
source = sources[i];
if (source != null) {
for (key in source) {
@ -8819,12 +8819,12 @@ function config (name) {
value = value.valueOf();
}
if (Array.isArray(target)) {
for (i = 0, len = target.length; i < len; i++) {
for (let i = 0, len = target.length; i < len; i++) {
insTarget = target[i];
this.instruction(insTarget);
}
} else if (isObject(target)) {
for (insTarget in target) {
for (let insTarget in target) {
if (!hasProp.call(target, insTarget)) continue;
insValue = target[insTarget];
this.instruction(insTarget, insValue);
@ -9145,7 +9145,7 @@ function config (name) {
}
name = name.valueOf();
if (Array.isArray(name)) {
for (i = 0, len = name.length; i < len; i++) {
for (let i = 0, len = name.length; i < len; i++) {
attName = name[i];
delete this.attributes[attName];
}
@ -9396,7 +9396,7 @@ function config (name) {
this.instruction(insTarget);
}
} else if (isObject(target)) {
for (insTarget in target) {
for (let insTarget in target) {
if (!hasProp.call(target, insTarget)) continue;
insValue = target[insTarget];
this.instruction(insTarget, insValue);
@ -9446,7 +9446,7 @@ function config (name) {
doc = this.document();
doctype = new XMLDocType(doc, pubID, sysID);
ref1 = doc.children;
for (i = j = 0, len = ref1.length; j < len; i = ++j) {
for (let i = j = 0, len = ref1.length; j < len; i = ++j) {
child = ref1[i];
if (child instanceof XMLDocType) {
doc.children[i] = doctype;
@ -9454,7 +9454,7 @@ function config (name) {
}
}
ref2 = doc.children;
for (i = k = 0, len1 = ref2.length; k < len1; i = ++k) {
for (let i = k = 0, len1 = ref2.length; k < len1; i = ++k) {
child = ref2[i];
if (child.isRoot) {
doc.children.splice(i, 0, doctype);
@ -9722,7 +9722,7 @@ function config (name) {
XMLStreamWriter.prototype.document = function(doc) {
var child, i, j, len, len1, ref, ref1, results;
ref = doc.children;
for (i = 0, len = ref.length; i < len; i++) {
for (let i = 0, len = ref.length; i < len; i++) {
child = ref[i];
child.isLastRootNode = false;
}
@ -9790,7 +9790,7 @@ function config (name) {
this.stream.write(' [');
this.stream.write(this.endline(node));
ref = node.children;
for (i = 0, len = ref.length; i < len; i++) {
for (let i = 0, len = ref.length; i < len; i++) {
child = ref[i];
switch (false) {
case !(child instanceof XMLDTDAttList):
@ -9850,7 +9850,7 @@ function config (name) {
} else {
this.stream.write('>' + this.newline);
ref1 = node.children;
for (i = 0, len = ref1.length; i < len; i++) {
for (let i = 0, len = ref1.length; i < len; i++) {
child = ref1[i];
switch (false) {
case !(child instanceof XMLCData):
@ -10004,7 +10004,7 @@ function config (name) {
this.textispresent = false;
r = '';
ref = doc.children;
for (i = 0, len = ref.length; i < len; i++) {
for (let i = 0, len = ref.length; i < len; i++) {
child = ref[i];
r += (function() {
switch (false) {
@ -10068,7 +10068,7 @@ function config (name) {
r += ' [';
r += this.newline;
ref = node.children;
for (i = 0, len = ref.length; i < len; i++) {
for (let i = 0, len = ref.length; i < len; i++) {
child = ref[i];
r += (function() {
switch (false) {
@ -10133,7 +10133,7 @@ function config (name) {
} else {
if (this.dontprettytextnodes) {
ref1 = node.children;
for (i = 0, len = ref1.length; i < len; i++) {
for (let i = 0, len = ref1.length; i < len; i++) {
child = ref1[i];
if (child.value != null) {
this.textispresent++;

View file

@ -1,87 +0,0 @@
(function() { var g,aa=aa||{},h=this,ba=function(){},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&
!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},m=function(a){return"array"==ca(a)},da=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},n=function(a){return"string"==typeof a},ea=function(a){return"number"==typeof a},p=function(a){return"function"==ca(a)},q=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},fa=function(a,b,c){return a.call.apply(a.bind,
arguments)},ga=function(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}},r=function(a,b,c){r=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?fa:ga;return r.apply(null,arguments)},ha=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=
c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},s=Date.now||function(){return+new Date},t=function(a,b){var c=a.split("."),d=h;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b},u=function(a,b){function c(){}c.prototype=b.prototype;a.L=b.prototype;a.prototype=new c;a.Pc=function(a,c,f){return b.prototype[c].apply(a,Array.prototype.slice.call(arguments,2))}};
Function.prototype.bind=Function.prototype.bind||function(a,b){if(1<arguments.length){var c=Array.prototype.slice.call(arguments,1);c.unshift(this,a);return r.apply(null,c)}return r(this,a)};var v=function(a){if(Error.captureStackTrace)Error.captureStackTrace(this,v);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))};u(v,Error);v.prototype.name="CustomError";var ia=function(a,b){return a<b?-1:a>b?1:0};var w=Array.prototype,ja=w.indexOf?function(a,b,c){return w.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(n(a))return n(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},ka=w.forEach?function(a,b,c){w.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=n(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},la=w.some?function(a,b,c){return w.some.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=n(a)?a.split(""):
a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return!0;return!1},ma=w.every?function(a,b,c){return w.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=n(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&!b.call(c,e[f],f,a))return!1;return!0},oa=function(a){var b;t:{b=na;for(var c=a.length,d=n(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break t}b=-1}return 0>b?null:n(a)?a.charAt(b):a[b]},pa=function(a,b){var c=ja(a,b),d;(d=0<=c)&&w.splice.call(a,c,1);return d},qa=function(a){return w.concat.apply(w,
arguments)},ra=function(a,b,c){return 2>=arguments.length?w.slice.call(a,b):w.slice.call(a,b,c)};var sa="StopIteration"in h?h.StopIteration:Error("StopIteration"),ta=function(){};ta.prototype.next=function(){throw sa;};ta.prototype.vc=function(){return this};var ua=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},va=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b},wa=function(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b},xa=function(a,b){var c;t:{for(c in a)if(b.call(void 0,a[c],c,a))break t;c=void 0}return c&&a[c]},ya="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),za=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<ya.length;f++)c=
ya[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};var x=function(a,b){this.p={};this.b=[];this.oa=this.h=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.ia(a)};x.prototype.t=function(){Aa(this);for(var a=[],b=0;b<this.b.length;b++)a.push(this.p[this.b[b]]);return a};x.prototype.F=function(){Aa(this);return this.b.concat()};x.prototype.Q=function(a){return y(this.p,a)};
x.prototype.remove=function(a){return y(this.p,a)?(delete this.p[a],this.h--,this.oa++,this.b.length>2*this.h&&Aa(this),!0):!1};var Aa=function(a){if(a.h!=a.b.length){for(var b=0,c=0;b<a.b.length;){var d=a.b[b];y(a.p,d)&&(a.b[c++]=d);b++}a.b.length=c}if(a.h!=a.b.length){for(var e={},c=b=0;b<a.b.length;)d=a.b[b],y(e,d)||(a.b[c++]=d,e[d]=1),b++;a.b.length=c}};g=x.prototype;g.get=function(a,b){return y(this.p,a)?this.p[a]:b};
g.set=function(a,b){y(this.p,a)||(this.h++,this.b.push(a),this.oa++);this.p[a]=b};g.ia=function(a){var b;a instanceof x?(b=a.F(),a=a.t()):(b=wa(a),a=va(a));for(var c=0;c<b.length;c++)this.set(b[c],a[c])};g.forEach=function(a,b){for(var c=this.F(),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f,e,this)}};g.clone=function(){return new x(this)};g.Jb=function(){Aa(this);for(var a={},b=0;b<this.b.length;b++){var c=this.b[b];a[c]=this.p[c]}return a};
g.vc=function(a){Aa(this);var b=0,c=this.b,d=this.p,e=this.oa,f=this,k=new ta;k.next=function(){for(;;){if(e!=f.oa)throw Error("The map has changed since the iterator was createdOn");if(b>=c.length)throw sa;var k=c[b++];return a?k:d[k]}};return k};var y=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var Ba,Ca,Da={id:"hitType",name:"t",valueType:"text",maxLength:void 0,defaultValue:void 0},Ea={id:"sessionControl",name:"sc",valueType:"text",maxLength:void 0,defaultValue:void 0},Fa={id:"description",name:"cd",valueType:"text",maxLength:2048,defaultValue:void 0},Ga={id:"eventCategory",name:"ec",valueType:"text",maxLength:150,defaultValue:void 0},Ha={id:"eventAction",name:"ea",valueType:"text",maxLength:500,defaultValue:void 0},Ia={id:"eventLabel",name:"el",valueType:"text",maxLength:500,defaultValue:void 0},
Ja={id:"eventValue",name:"ev",valueType:"integer",maxLength:void 0,defaultValue:void 0},Ka={pd:Da,Qc:{id:"anonymizeIp",name:"aip",valueType:"boolean",maxLength:void 0,defaultValue:void 0},Ad:{id:"queueTime",name:"qt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Wc:{id:"cacheBuster",name:"z",valueType:"text",maxLength:void 0,defaultValue:void 0},Gd:Ea,Wd:{id:"userId",name:"uid",valueType:"text",maxLength:void 0,defaultValue:void 0},xd:{id:"nonInteraction",name:"ni",valueType:"boolean",
maxLength:void 0,defaultValue:void 0},fd:Fa,Pd:{id:"title",name:"dt",valueType:"text",maxLength:1500,defaultValue:void 0},Sc:{id:"appId",name:"aid",valueType:"text",maxLength:150,defaultValue:void 0},Tc:{id:"appInstallerId",name:"aiid",valueType:"text",maxLength:150,defaultValue:void 0},jd:Ga,hd:Ha,kd:Ia,ld:Ja,Id:{id:"socialNetwork",name:"sn",valueType:"text",maxLength:50,defaultValue:void 0},Hd:{id:"socialAction",name:"sa",valueType:"text",maxLength:50,defaultValue:void 0},Jd:{id:"socialTarget",
name:"st",valueType:"text",maxLength:2048,defaultValue:void 0},Sd:{id:"transactionId",name:"ti",valueType:"text",maxLength:500,defaultValue:void 0},Rd:{id:"transactionAffiliation",name:"ta",valueType:"text",maxLength:500,defaultValue:void 0},Td:{id:"transactionRevenue",name:"tr",valueType:"currency",maxLength:void 0,defaultValue:void 0},Ud:{id:"transactionShipping",name:"ts",valueType:"currency",maxLength:void 0,defaultValue:void 0},Vd:{id:"transactionTax",name:"tt",valueType:"currency",maxLength:void 0,
defaultValue:void 0},dd:{id:"currencyCode",name:"cu",valueType:"text",maxLength:10,defaultValue:void 0},td:{id:"itemPrice",name:"ip",valueType:"currency",maxLength:void 0,defaultValue:void 0},ud:{id:"itemQuantity",name:"iq",valueType:"integer",maxLength:void 0,defaultValue:void 0},rd:{id:"itemCode",name:"ic",valueType:"text",maxLength:500,defaultValue:void 0},sd:{id:"itemName",name:"in",valueType:"text",maxLength:500,defaultValue:void 0},qd:{id:"itemCategory",name:"iv",valueType:"text",maxLength:500,
defaultValue:void 0},bd:{id:"campaignSource",name:"cs",valueType:"text",maxLength:100,defaultValue:void 0},$c:{id:"campaignMedium",name:"cm",valueType:"text",maxLength:50,defaultValue:void 0},ad:{id:"campaignName",name:"cn",valueType:"text",maxLength:100,defaultValue:void 0},Zc:{id:"campaignKeyword",name:"ck",valueType:"text",maxLength:500,defaultValue:void 0},Xc:{id:"campaignContent",name:"cc",valueType:"text",maxLength:500,defaultValue:void 0},Yc:{id:"campaignId",name:"ci",valueType:"text",maxLength:100,
defaultValue:void 0},od:{id:"gclid",name:"gclid",valueType:"text",maxLength:void 0,defaultValue:void 0},ed:{id:"dclid",name:"dclid",valueType:"text",maxLength:void 0,defaultValue:void 0},zd:{id:"pageLoadTime",name:"plt",valueType:"integer",maxLength:void 0,defaultValue:void 0},gd:{id:"dnsTime",name:"dns",valueType:"integer",maxLength:void 0,defaultValue:void 0},Kd:{id:"tcpConnectTime",name:"tcp",valueType:"integer",maxLength:void 0,defaultValue:void 0},Fd:{id:"serverResponseTime",name:"srt",valueType:"integer",
maxLength:void 0,defaultValue:void 0},yd:{id:"pageDownloadTime",name:"pdt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Bd:{id:"redirectResponseTime",name:"rrt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Ld:{id:"timingCategory",name:"utc",valueType:"text",maxLength:150,defaultValue:void 0},Od:{id:"timingVar",name:"utv",valueType:"text",maxLength:500,defaultValue:void 0},Nd:{id:"timingValue",name:"utt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Md:{id:"timingLabel",
name:"utl",valueType:"text",maxLength:500,defaultValue:void 0},md:{id:"exDescription",name:"exd",valueType:"text",maxLength:150,defaultValue:void 0},nd:{id:"exFatal",name:"exf",valueType:"boolean",maxLength:void 0,defaultValue:"1"}},La=function(a){if(1>a||200<a)throw Error("Expected dimension index range 1-200, but was : "+a);return{id:"dimension"+a,name:"cd"+a,valueType:"text",maxLength:150,defaultValue:void 0}},Ma=function(a){if(1>a||200<a)throw Error("Expected metric index range 1-200, but was : "+
a);return{id:"metric"+a,name:"cm"+a,valueType:"integer",maxLength:void 0,defaultValue:void 0}};var Na=function(a){if(1>a)return"0";if(3>a)return"1-2";a=Math.floor(Math.log(a-1)/Math.log(2));return Math.pow(2,a)+1+"-"+Math.pow(2,a+1)},Oa=function(a,b){for(var c=0,d=a.length-1,e=0;c<=d;){var f=Math.floor((c+d)/2),e=a[f];if(b<=e){d=0==f?0:a[f-1];if(b>d)return(d+1).toString()+"-"+e.toString();d=f-1}else if(b>e){if(f>=a.length-1)return(a[a.length-1]+1).toString()+"+";c=f+1}}return"<= 0"};var z=function(){this.ab=[]},Pa=function(){return new z};g=z.prototype;g.when=function(a){this.ab.push(a);return this};g.zb=function(a){var b=arguments;this.when(function(a){return 0<=ja(b,a.Gb())});return this};g.Oc=function(a,b){var c=ra(arguments,1);this.when(function(b){b=b.T().get(a);return 0<=ja(c,b)});return this};g.xb=function(a,b){if(q(this.e))throw Error("Filter has already been set.");this.e=q(b)?r(a,b):a;return this};
g.Ca=function(){if(0==this.ab.length)throw Error("Must specify at least one predicate using #when or a helper method.");if(!q(this.e))throw Error("Must specify a delegate filter using #applyFilter.");return r(function(a){ma(this.ab,function(b){return b(a)})&&this.e(a)},this)};var A=function(){this.Ab=!1;this.Bb="";this.qb=!1;this.za=null};A.prototype.wc=function(a){this.Ab=!0;this.Bb=a||" - ";return this};A.prototype.Nc=function(){this.qb=!0;return this};A.prototype.Ec=function(){return Qa(this,Na)};A.prototype.Fc=function(a){return Qa(this,ha(Oa,a))};
var Qa=function(a,b){if(null!=a.za)throw Error("LabelerBuilder: Only one labeling strategy may be used.");a.za=r(function(a){var d=a.T().get(Ja),e=a.T().get(Ia);ea(d)&&(d=b(d),null!=e&&this.Ab&&(d=e+this.Bb+d),a.T().set(Ia,d))},a);return a};A.prototype.Ca=function(){if(null==this.za)throw Error("LabelerBuilder: a labeling strategy must be specified prior to calling build().");return Pa().zb("event").xb(r(function(a){this.za(a);this.qb&&a.T().remove(Ja)},this)).Ca()};var Ra=function(a,b){var c=Array.prototype.slice.call(arguments),d=c.shift();if("undefined"==typeof d)throw Error("[goog.string.format] Template required");return d.replace(/%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g,function(a,b,d,l,N,J,U,V){if("%"==J)return"%";var Db=c.shift();if("undefined"==typeof Db)throw Error("[goog.string.format] Not enough arguments");arguments[0]=Db;return B[J].apply(null,arguments)})},B={s:function(a,b,c){return isNaN(c)||""==c||a.length>=c?a:a=-1<b.indexOf("-",0)?a+Array(c-
a.length+1).join(" "):Array(c-a.length+1).join(" ")+a},f:function(a,b,c,d,e){d=a.toString();isNaN(e)||""==e||(d=a.toFixed(e));var f;f=0>a?"-":0<=b.indexOf("+")?"+":0<=b.indexOf(" ")?" ":"";0<=a&&(d=f+d);if(isNaN(c)||d.length>=c)return d;d=isNaN(e)?Math.abs(a).toString():Math.abs(a).toFixed(e);a=c-d.length-f.length;return d=0<=b.indexOf("-",0)?f+d+Array(a+1).join(" "):f+Array(a+1).join(0<=b.indexOf("0",0)?"0":" ")+d},d:function(a,b,c,d,e,f,k,l){return B.f(parseInt(a,10),b,c,d,0,f,k,l)}};B.i=B.d;
B.u=B.d;var Sa=function(a){if("function"==typeof a.t)return a.t();if(n(a))return a.split("");if(da(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return va(a)},Ta=function(a,b){if("function"==typeof a.forEach)a.forEach(b,void 0);else if(da(a)||n(a))ka(a,b,void 0);else{var c;if("function"==typeof a.F)c=a.F();else if("function"!=typeof a.t)if(da(a)||n(a)){c=[];for(var d=a.length,e=0;e<d;e++)c.push(e)}else c=wa(a);else c=void 0;for(var d=Sa(a),e=d.length,f=0;f<e;f++)b.call(void 0,d[f],c&&c[f],
a)}};var C=function(a){this.B=new x;for(var b=arguments,c=0;c<b.length;c+=2)this.set(b[c],b[c+1])};C.prototype.set=function(a,b){this.B.set(a.name,{key:a,value:b})};C.prototype.remove=function(a){this.B.remove(a.name)};C.prototype.get=function(a){a=this.B.get(a.name,null);return null===a?null:a.value};C.prototype.ia=function(a){this.B.ia(a.B)};var Ua=function(a,b){ka(a.B.t(),function(a){b(a.key,a.value)})};C.prototype.Jb=function(){var a={};Ua(this,function(b,c){a[b.id]=c});return a};
C.prototype.clone=function(){var a=new C;a.B=this.B.clone();return a};C.prototype.toString=function(){var a={};Ua(this,function(b,c){a[b.id]=c});return JSON.stringify(a)};var D=function(a){this.e=a};g=D.prototype;g.xc=function(a){var b=new D(r(this.P,this));b.I=Ga;b.N=a;return b};g.action=function(a){var b=new D(r(this.P,this));b.I=Ha;b.N=a;return b};g.label=function(a){var b=new D(r(this.P,this));b.I=Ia;b.N=a;return b};g.value=function(a){var b=new D(r(this.P,this));b.I=Ja;b.N=a;return b};g.yc=function(a,b){var c=new D(r(this.P,this));c.I=La(a);c.N=b;return c};g.Dc=function(a,b){var c=new D(r(this.P,this));c.I=Ma(a);c.N=b;return c};
g.send=function(a){var b=new C;this.P(b);return a.send("event",b)};g.P=function(a){null!=this.I&&null!=this.N&&!a.B.Q(this.I.name)&&a.set(this.I,this.N);q(this.e)&&this.e(a)};var Va=new D(ba);var E=function(){this.Y=this.Y;this.Da=this.Da};E.prototype.Y=!1;E.prototype.xa=function(){this.Y||(this.Y=!0,this.l())};E.prototype.l=function(){if(this.Da)for(;this.Da.length;)this.Da.shift()()};var F=function(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.U=!1;this.kb=!0};F.prototype.l=function(){};F.prototype.xa=function(){};F.prototype.preventDefault=function(){this.defaultPrevented=!0;this.kb=!1};var Wa=function(a){Wa[" "](a);return a};Wa[" "]=ba;var G;t:{var Xa=h.navigator;if(Xa){var Ya=Xa.userAgent;if(Ya){G=Ya;break t}}G=""}var H=function(a){return-1!=G.indexOf(a)};var Za=H("Opera")||H("OPR"),I=H("Trident")||H("MSIE"),K=H("Gecko")&&-1==G.toLowerCase().indexOf("webkit")&&!(H("Trident")||H("MSIE")),L=-1!=G.toLowerCase().indexOf("webkit"),$a=function(){var a=h.document;return a?a.documentMode:void 0},ab=function(){var a="",b;if(Za&&h.opera)return a=h.opera.version,p(a)?a():a;K?b=/rv\:([^\);]+)(\)|;)/:I?b=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:L&&(b=/WebKit\/(\S+)/);b&&(a=(a=b.exec(G))?a[1]:"");return I&&(b=$a(),b>parseFloat(a))?String(b):a}(),bb={},M=function(a){var b;
if(!(b=bb[a])){b=0;for(var c=String(ab).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),d=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var k=c[f]||"",l=d[f]||"",N=RegExp("(\\d*)(\\D*)","g"),J=RegExp("(\\d*)(\\D*)","g");do{var U=N.exec(k)||["","",""],V=J.exec(l)||["","",""];if(0==U[0].length&&0==V[0].length)break;b=ia(0==U[1].length?0:parseInt(U[1],10),0==V[1].length?0:parseInt(V[1],10))||ia(0==U[2].length,0==V[2].length)||ia(U[2],V[2])}while(0==
b)}b=bb[a]=0<=b}return b},cb=h.document,db=cb&&I?$a()||("CSS1Compat"==cb.compatMode?parseInt(ab,10):5):void 0;var eb=!I||I&&9<=db,fb=I&&!M("9"),gb=!L||M("528"),hb=K&&M("1.9b")||I&&M("8")||Za&&M("9.5")||L&&M("528"),ib=K&&!M("8")||I&&!M("9");var O=function(a,b){F.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.Db=this.state=null;if(a){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(K){var e;t:{try{Wa(d.nodeName);e=!0;break t}catch(f){}e=!1}e||(d=null)}}else"mouseover"==
c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=L||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=L||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=
a.metaKey;this.state=a.state;this.Db=a;a.defaultPrevented&&this.preventDefault()}};u(O,F);O.prototype.preventDefault=function(){O.L.preventDefault.call(this);var a=this.Db;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,fb)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};O.prototype.l=function(){};var jb="closure_listenable_"+(1E6*Math.random()|0),kb=function(a){return!(!a||!a[jb])},lb=0;var mb=function(a,b,c,d,e){this.O=a;this.proxy=null;this.src=b;this.type=c;this.pa=!!d;this.sa=e;this.key=++lb;this.removed=this.qa=!1},nb=function(a){a.removed=!0;a.O=null;a.proxy=null;a.src=null;a.sa=null};var P=function(a){this.src=a;this.j={};this.Z=0};P.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.j[f];a||(a=this.j[f]=[],this.Z++);var k=ob(a,b,d,e);-1<k?(b=a[k],c||(b.qa=!1)):(b=new mb(b,this.src,f,!!d,e),b.qa=c,a.push(b));return b};P.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.j))return!1;var e=this.j[a];b=ob(e,b,c,d);return-1<b?(nb(e[b]),w.splice.call(e,b,1),0==e.length&&(delete this.j[a],this.Z--),!0):!1};
var pb=function(a,b){var c=b.type;if(!(c in a.j))return!1;var d=pa(a.j[c],b);d&&(nb(b),0==a.j[c].length&&(delete a.j[c],a.Z--));return d};P.prototype.removeAll=function(a){a=a&&a.toString();var b=0,c;for(c in this.j)if(!a||c==a){for(var d=this.j[c],e=0;e<d.length;e++)++b,nb(d[e]);delete this.j[c];this.Z--}return b};P.prototype.X=function(a,b,c,d){a=this.j[a.toString()];var e=-1;a&&(e=ob(a,b,c,d));return-1<e?a[e]:null};
var ob=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.removed&&f.O==b&&f.pa==!!c&&f.sa==d)return e}return-1};var qb="closure_lm_"+(1E6*Math.random()|0),rb={},sb=0,tb=function(a,b,c,d,e){if(m(b)){for(var f=0;f<b.length;f++)tb(a,b[f],c,d,e);return null}c=ub(c);return kb(a)?a.listen(b,c,d,e):vb(a,b,c,!1,d,e)},vb=function(a,b,c,d,e,f){if(!b)throw Error("Invalid event type");var k=!!e,l=wb(a);l||(a[qb]=l=new P(a));c=l.add(b,c,d,e,f);if(c.proxy)return c;d=xb();c.proxy=d;d.src=a;d.O=c;a.addEventListener?a.addEventListener(b.toString(),d,k):a.attachEvent(yb(b.toString()),d);sb++;return c},xb=function(){var a=zb,
b=eb?function(c){return a.call(b.src,b.O,c)}:function(c){c=a.call(b.src,b.O,c);if(!c)return c};return b},Ab=function(a,b,c,d,e){if(m(b)){for(var f=0;f<b.length;f++)Ab(a,b[f],c,d,e);return null}c=ub(c);return kb(a)?a.bb(b,c,d,e):vb(a,b,c,!0,d,e)},Bb=function(a,b,c,d,e){if(m(b))for(var f=0;f<b.length;f++)Bb(a,b[f],c,d,e);else c=ub(c),kb(a)?a.Va(b,c,d,e):a&&(a=wb(a))&&(b=a.X(b,c,!!d,e))&&Cb(b)},Cb=function(a){if(ea(a)||!a||a.removed)return!1;var b=a.src;if(kb(b))return pb(b.A,a);var c=a.type,d=a.proxy;
b.removeEventListener?b.removeEventListener(c,d,a.pa):b.detachEvent&&b.detachEvent(yb(c),d);sb--;(c=wb(b))?(pb(c,a),0==c.Z&&(c.src=null,b[qb]=null)):nb(a);return!0},yb=function(a){return a in rb?rb[a]:rb[a]="on"+a},Fb=function(a,b,c,d){var e=1;if(a=wb(a))if(b=a.j[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.pa==c&&!f.removed&&(e&=!1!==Eb(f,d))}return Boolean(e)},Eb=function(a,b){var c=a.O,d=a.sa||a.src;a.qa&&Cb(a);return c.call(d,b)},zb=function(a,b){if(a.removed)return!0;if(!eb){var c;
if(!(c=b))t:{c=["window","event"];for(var d=h,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break t}c=d}e=c;c=new O(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){t:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break t}catch(k){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,l=e.length-1;!c.U&&0<=l;l--)c.currentTarget=e[l],d&=Fb(e[l],f,!0,c);for(l=0;!c.U&&l<e.length;l++)c.currentTarget=e[l],d&=Fb(e[l],f,!1,c)}return d}return Eb(a,
new O(b,this))},wb=function(a){a=a[qb];return a instanceof P?a:null},Gb="__closure_events_fn_"+(1E9*Math.random()>>>0),ub=function(a){if(p(a))return a;a[Gb]||(a[Gb]=function(b){return a.handleEvent(b)});return a[Gb]};var Q=function(){E.call(this);this.A=new P(this);this.kc=this;this.Qa=null};u(Q,E);Q.prototype[jb]=!0;g=Q.prototype;g.addEventListener=function(a,b,c,d){tb(this,a,b,c,d)};g.removeEventListener=function(a,b,c,d){Bb(this,a,b,c,d)};
g.dispatchEvent=function(a){var b,c=this.Qa;if(c){b=[];for(var d=1;c;c=c.Qa)b.push(c),++d}c=this.kc;d=a.type||a;if(n(a))a=new F(a,c);else if(a instanceof F)a.target=a.target||c;else{var e=a;a=new F(d,c);za(a,e)}var e=!0,f;if(b)for(var k=b.length-1;!a.U&&0<=k;k--)f=a.currentTarget=b[k],e=Hb(f,d,!0,a)&&e;a.U||(f=a.currentTarget=c,e=Hb(f,d,!0,a)&&e,a.U||(e=Hb(f,d,!1,a)&&e));if(b)for(k=0;!a.U&&k<b.length;k++)f=a.currentTarget=b[k],e=Hb(f,d,!1,a)&&e;return e};
g.l=function(){Q.L.l.call(this);this.A&&this.A.removeAll(void 0);this.Qa=null};g.listen=function(a,b,c,d){return this.A.add(String(a),b,!1,c,d)};g.bb=function(a,b,c,d){return this.A.add(String(a),b,!0,c,d)};g.Va=function(a,b,c,d){return this.A.remove(String(a),b,c,d)};var Hb=function(a,b,c,d){b=a.A.j[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f<b.length;++f){var k=b[f];if(k&&!k.removed&&k.pa==c){var l=k.O,N=k.sa||k.src;k.qa&&pb(a.A,k);e=!1!==l.call(N,d)&&e}}return e&&0!=d.kb};
Q.prototype.X=function(a,b,c,d){return this.A.X(String(a),b,c,d)};var Ib=function(a){h.setTimeout(function(){throw a;},0)},Jb,Kb=function(){var a=h.MessageChannel;"undefined"===typeof a&&"undefined"!==typeof window&&window.postMessage&&window.addEventListener&&(a=function(){var a=document.createElement("iframe");a.style.display="none";a.src="";document.documentElement.appendChild(a);var b=a.contentWindow,a=b.document;a.open();a.write("");a.close();var c="callImmediate"+Math.random(),d="file:"==b.location.protocol?"*":b.location.protocol+"//"+b.location.host,a=r(function(a){if(a.origin==
d||a.data==c)this.port1.onmessage()},this);b.addEventListener("message",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if("undefined"!==typeof a&&!H("Trident")&&!H("MSIE")){var b=new a,c={},d=c;b.port1.onmessage=function(){c=c.next;var a=c.Fb;c.Fb=null;a()};return function(a){d.next={Fb:a};d=d.next;b.port2.postMessage(0)}}return"undefined"!==typeof document&&"onreadystatechange"in document.createElement("script")?function(a){var b=document.createElement("script");b.onreadystatechange=
function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){h.setTimeout(a,0)}};var Qb=function(a,b){Lb||Mb();Nb||(Lb(),Nb=!0);Ob.push(new Pb(a,b))},Lb,Mb=function(){if(h.Promise&&h.Promise.resolve){var a=h.Promise.resolve();Lb=function(){a.then(Rb)}}else Lb=function(){var a=Rb;!p(h.setImmediate)||h.Window&&h.Window.prototype.setImmediate==h.setImmediate?(Jb||(Jb=Kb()),Jb(a)):h.setImmediate(a)}},Nb=!1,Ob=[],Rb=function(){for(;Ob.length;){var a=Ob;Ob=[];for(var b=0;b<a.length;b++){var c=a[b];try{c.zc.call(c.scope)}catch(d){Ib(d)}}}Nb=!1},Pb=function(a,b){this.zc=a;this.scope=
b};var Sb=function(a){a.prototype.then=a.prototype.then;a.prototype.$goog_Thenable=!0},Tb=function(a){if(!a)return!1;try{return!!a.$goog_Thenable}catch(b){return!1}};var R=function(a,b){this.m=0;this.v=void 0;this.n=this.o=null;this.ua=this.La=!1;try{var c=this;a.call(b,function(a){Ub(c,2,a)},function(a){Ub(c,3,a)})}catch(d){Ub(this,3,d)}};R.prototype.then=function(a,b,c){return Vb(this,p(a)?a:null,p(b)?b:null,c)};Sb(R);R.prototype.cancel=function(a){0==this.m&&Qb(function(){var b=new Wb(a);Xb(this,b)},this)};
var Xb=function(a,b){if(0==a.m)if(a.o){var c=a.o;if(c.n){for(var d=0,e=-1,f=0,k;k=c.n[f];f++)if(k=k.wa)if(d++,k==a&&(e=f),0<=e&&1<d)break;0<=e&&(0==c.m&&1==d?Xb(c,b):(d=c.n.splice(e,1)[0],Yb(c),d.Ma(b)))}}else Ub(a,3,b)},$b=function(a,b){a.n&&a.n.length||2!=a.m&&3!=a.m||Zb(a);a.n||(a.n=[]);a.n.push(b)},Vb=function(a,b,c,d){var e={wa:null,jb:null,Ma:null};e.wa=new R(function(a,k){e.jb=b?function(c){try{var e=b.call(d,c);a(e)}catch(J){k(J)}}:a;e.Ma=c?function(b){try{var e=c.call(d,b);void 0===e&&b instanceof
Wb?k(b):a(e)}catch(J){k(J)}}:k});e.wa.o=a;$b(a,e);return e.wa};R.prototype.vb=function(a){this.m=0;Ub(this,2,a)};R.prototype.wb=function(a){this.m=0;Ub(this,3,a)};
var Ub=function(a,b,c){if(0==a.m){if(a==c)b=3,c=new TypeError("Promise cannot resolve to itself");else{if(Tb(c)){a.m=1;c.then(a.vb,a.wb,a);return}if(q(c))try{var d=c.then;if(p(d)){ac(a,c,d);return}}catch(e){b=3,c=e}}a.v=c;a.m=b;Zb(a);3!=b||c instanceof Wb||bc(a,c)}},ac=function(a,b,c){a.m=1;var d=!1,e=function(b){d||(d=!0,a.vb(b))},f=function(b){d||(d=!0,a.wb(b))};try{c.call(b,e,f)}catch(k){f(k)}},Zb=function(a){a.La||(a.La=!0,Qb(a.uc,a))};
R.prototype.uc=function(){for(;this.n&&this.n.length;){var a=this.n;this.n=[];for(var b=0;b<a.length;b++){var c=a[b],d=this.v;2==this.m?c.jb(d):(Yb(this),c.Ma(d))}}this.La=!1};var Yb=function(a){for(;a&&a.ua;a=a.o)a.ua=!1},bc=function(a,b){a.ua=!0;Qb(function(){a.ua&&cc.call(null,b)})},cc=Ib,Wb=function(a){v.call(this,a)};u(Wb,v);Wb.prototype.name="cancel";/*
Portions of this code are from MochiKit, received by
The Closure Authors under the MIT license. All other code is Copyright
2005-2009 The Closure Authors. All Rights Reserved.
*/
var S=function(a,b){this.ja=[];this.hb=a;this.gb=b||null;this.W=this.C=!1;this.v=void 0;this.Ka=this.Lb=this.Ja=!1;this.ka=0;this.o=null;this.Ia=0};S.prototype.cancel=function(a){if(this.C)this.v instanceof S&&this.v.cancel();else{if(this.o){var b=this.o;delete this.o;a?b.cancel(a):(b.Ia--,0>=b.Ia&&b.cancel())}this.hb?this.hb.call(this.gb,this):this.Ka=!0;this.C||this.w(new dc)}};S.prototype.ib=function(a,b){this.Ja=!1;ec(this,a,b)};
var ec=function(a,b,c){a.C=!0;a.v=c;a.W=!b;fc(a)},hc=function(a){if(a.C){if(!a.Ka)throw new gc;a.Ka=!1}};S.prototype.G=function(a){hc(this);ec(this,!0,a)};S.prototype.w=function(a){hc(this);ec(this,!1,a)};S.prototype.J=function(a,b){return ic(this,a,null,b)};var ic=function(a,b,c,d){a.ja.push([b,c,d]);a.C&&fc(a);return a};S.prototype.then=function(a,b,c){var d,e,f=new R(function(a,b){d=a;e=b});ic(this,d,function(a){a instanceof dc?f.cancel():e(a)});return f.then(a,b,c)};Sb(S);
var jc=function(a){var b=new S;ic(a,b.G,b.w,b);return b},kc=function(a){return la(a.ja,function(a){return p(a[1])})},fc=function(a){if(a.ka&&a.C&&kc(a)){var b=a.ka,c=lc[b];c&&(h.clearTimeout(c.ma),delete lc[b]);a.ka=0}a.o&&(a.o.Ia--,delete a.o);for(var b=a.v,d=c=!1;a.ja.length&&!a.Ja;){var e=a.ja.shift(),f=e[0],k=e[1],e=e[2];if(f=a.W?k:f)try{var l=f.call(e||a.gb,b);void 0!==l&&(a.W=a.W&&(l==b||l instanceof Error),a.v=b=l);Tb(b)&&(d=!0,a.Ja=!0)}catch(N){b=N,a.W=!0,kc(a)||(c=!0)}}a.v=b;d&&(l=r(a.ib,
a,!0),d=r(a.ib,a,!1),b instanceof S?(ic(b,l,d),b.Lb=!0):b.then(l,d));c&&(b=new mc(b),lc[b.ma]=b,a.ka=b.ma)},nc=function(a){var b=new S;b.G(a);return b},pc=function(){var a=oc,b=new S;b.w(a);return b},gc=function(){v.call(this)};u(gc,v);gc.prototype.message="Deferred has already fired";gc.prototype.name="AlreadyCalledError";var dc=function(){v.call(this)};u(dc,v);dc.prototype.message="Deferred was canceled";dc.prototype.name="CanceledError";
var mc=function(a){this.ma=h.setTimeout(r(this.pc,this),0);this.ga=a};mc.prototype.pc=function(){delete lc[this.ma];throw this.ga;};var lc={};var qc=function(a){this.$a=[];this.e=a};qc.prototype.S=function(a){if(!p(a))throw Error("Invalid filter. Must be a function.");this.$a.push(a)};qc.prototype.send=function(a,b){for(var c=new T(a,b),d=0;d<this.$a.length&&(this.$a[d](c),!c.Za);d++);return c.Za?nc():this.e.send(a,b)};var T=function(a,b){this.rc=a;this.qc=b;this.Za=!1};T.prototype.Gb=function(){return this.rc};T.prototype.T=function(){return this.qc};T.prototype.cancel=function(){this.Za=!0};var rc=function(a,b){this.width=a;this.height=b};rc.prototype.clone=function(){return new rc(this.width,this.height)};rc.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};!K&&!I||I&&I&&9<=db||K&&M("1.9.1");I&&M("9");var sc={id:"apiVersion",name:"v",valueType:"text",maxLength:void 0,defaultValue:void 0},tc={id:"appName",name:"an",valueType:"text",maxLength:100,defaultValue:void 0},uc={id:"appVersion",name:"av",valueType:"text",maxLength:100,defaultValue:void 0},vc={id:"clientId",name:"cid",valueType:"text",maxLength:void 0,defaultValue:void 0},wc={id:"language",name:"ul",valueType:"text",maxLength:20,defaultValue:void 0},xc={id:"libVersion",name:"_v",valueType:"text",maxLength:void 0,defaultValue:void 0},yc={id:"sampleRateOverride",
name:"usro",valueType:"integer",maxLength:void 0,defaultValue:void 0},zc={id:"screenColors",name:"sd",valueType:"text",maxLength:20,defaultValue:void 0},Ac={id:"screenResolution",name:"sr",valueType:"text",maxLength:20,defaultValue:void 0},Bc={id:"trackingId",name:"tid",valueType:"text",maxLength:void 0,defaultValue:void 0},Cc={id:"viewportSize",name:"vp",valueType:"text",maxLength:20,defaultValue:void 0},Dc={Rc:sc,Uc:tc,Vc:uc,cd:vc,vd:wc,wd:xc,Cd:yc,Dd:zc,Ed:Ac,Qd:Bc,Xd:Cc},Fc=function(a){if(!n(a))return a;
var b=Ec(a,Ka);if(q(b))return b;b=Ec(a,Dc);if(q(b))return b;b=/^dimension(\d+)$/.exec(a);if(null!==b)return La(parseInt(b[1],10));b=/^metric(\d+)$/.exec(a);if(null!==b)return Ma(parseInt(b[1],10));throw Error(a+" is not a valid parameter name.");},Ec=function(a,b){var c=xa(b,function(b){return b.id==a&&"metric"!=a&&"dimension"!=a});return q(c)?c:null};var W=function(a,b){this.Zb=b;this.q=b.Sa();this.sb=new C;this.Ya=!1};g=W.prototype;g.set=function(a,b){var c=Fc(a);this.sb.set(c,b)};g.S=function(a){this.Zb.S(a)};g.send=function(a,b){if(a instanceof D)return a.send(this);var c=this.sb.clone();b instanceof C?c.ia(b):q(b)&&ua(b,function(a,b){null!=a&&c.set(Fc(b),a)},this);this.Ya&&(this.Ya=!1,c.set(Ea,"start"));return this.q.send(a,c)};g.Gc=function(a){var b={description:a};this.set(Fa,a);return this.send("appview",b)};
g.Hc=function(a,b,c,d){return this.send("event",{eventCategory:a,eventAction:b,eventLabel:c,eventValue:d})};g.Jc=function(a,b,c){return this.send("social",{socialNetwork:a,socialAction:b,socialTarget:c})};g.Ic=function(a,b){return this.send("exception",{exDescription:a,exFatal:b})};g.Cb=function(a,b,c,d,e){return this.send("timing",{timingCategory:a,timingVar:b,timingLabel:d,timingValue:c,sampleRateOverride:e})};g.Ac=function(){this.Ya=!0};g.Mc=function(a,b,c,d){return new Gc(this,a,b,c,d)};
var Gc=function(a,b,c,d,e){this.yb=a;this.bc=b;this.ec=c;this.cc=d;this.V=e;this.dc=s()};Gc.prototype.send=function(){var a=this.yb.Cb(this.bc,this.ec,s()-this.dc,this.cc,this.V);this.yb=null;return a};var Hc=function(a,b,c,d,e){this.ic=a;this.fc=b;this.gc=c;this.k=d;this.hc=e};
Hc.prototype.Cc=function(a){var b=new W(0,this.hc.create());b.set(xc,this.ic);b.set(sc,1);b.set(tc,this.fc);b.set(uc,this.gc);b.set(Bc,a);a=window.navigator.language;b.set(wc,a);a=screen.colorDepth+"-bit";b.set(zc,a);a=[screen.width,screen.height].join("x");b.set(Ac,a);a=window.document;a="CSS1Compat"==a.compatMode?a.documentElement:a.body;a=new rc(a.clientWidth,a.clientHeight);a=[a.width,a.height].join("x");b.set(Cc,a);return b};Hc.prototype.Bc=function(){return jc(this.k.ha)};var Ic=function(a){this.sc=a};Ic.prototype.send=function(a,b){this.sc.push({Ub:a,Vb:b});return nc()};var Jc=function(a,b,c){this.k=a;this.ra=[];this.M={enabled:new Ic(this.ra),disabled:c};this.q=this.M.enabled;ic(jc(this.k.ha),ha(this.Pb,b),this.Ob,this)};Jc.prototype.Pb=function(a){this.M.enabled=a();Kc(this);ka(this.ra,function(a){this.send(a.Ub,a.Vb)},this);this.ra=null;Lc(this.k,r(this.Xb,this))};Jc.prototype.Ob=function(){this.q=this.M.enabled=this.M.disabled;this.ra=null};Jc.prototype.send=function(a,b){return this.q.send(a,b)};var Kc=function(a){a.q=a.k.va()?a.M.enabled:a.M.disabled};
Jc.prototype.Xb=function(a){switch(a){case "analytics.tracking-permitted":Kc(this)}};var Mc=function(a,b,c,d,e,f){S.call(this,e,f);this.Na=a;this.Oa=[];this.lb=!!b;this.Nb=!!c;this.Mb=!!d;for(b=this.mb=0;b<a.length;b++)ic(a[b],r(this.rb,this,b,!0),r(this.rb,this,b,!1));0!=a.length||this.lb||this.G(this.Oa)};u(Mc,S);Mc.prototype.rb=function(a,b,c){this.mb++;this.Oa[a]=[b,c];this.C||(this.lb&&b?this.G([a,c]):this.Nb&&!b?this.w(c):this.mb==this.Na.length&&this.G(this.Oa));this.Mb&&!b&&(c=null);return c};Mc.prototype.w=function(a){Mc.L.w.call(this,a);for(a=0;a<this.Na.length;a++)this.Na[a].cancel()};
var Nc=function(a){return(new Mc(a,!1,!0)).J(function(a){for(var c=[],d=0;d<a.length;d++)c[d]=a[d][1];return c})};var X=function(a){this.H=a;this.V=100;this.nb=[];this.Pa=this.la=null;this.ha=Oc(this);this.ha.J(function(){tb(this.H,"a",r(this.Rb,this))},this)},Oc=function(a){return Pc(a).J(function(){return this},a)},Pc=function(a){return Nc([Qc(a),Rc(a)])};X.prototype.Rb=function(){var a=this.la,b=this.va();Pc(this).J(function(){if(a!=this.la)throw Error("User ID changed unexpectedly!");b!=this.va()&&Sc(this)},this)};var Lc=function(a,b){a.nb.push(b)};
X.prototype.Lc=function(a){this.H.set("analytics.tracking-permitted",a).J(function(){this.Pa=a},this)};X.prototype.va=function(){var a;if(a=this.Pa)a=h._gaUserPrefs,a=!(a&&a.ioo&&a.ioo());return a};
var Qc=function(a){return a.H.get("analytics.tracking-permitted").J(function(a){this.Pa=void 0!==a?a:!0},a)},Rc=function(a){return a.H.get("analytics.user-id").J(function(a){if(!a){a="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".split("");for(var c=0,d=a.length;c<d;c++)switch(a[c]){case "x":a[c]=Math.floor(16*Math.random()).toString(16);break;case "y":a[c]=(Math.floor(4*Math.random())+8).toString(16)}a=a.join("");this.H.set("analytics.user-id",a)}this.la=a},a)};X.prototype.Kc=function(a){this.V=a};
var Sc=function(a){ka(a.nb,function(a){a("analytics.tracking-permitted")})};var Tc=function(a){Q.call(this);this.Wa=a;this.H=chrome.storage.local;chrome.storage.onChanged.addListener(r(this.nc,this))};u(Tc,Q);Tc.prototype.nc=function(a){Uc(this,a)&&this.dispatchEvent("a")};var Uc=function(a,b){return la(wa(b),function(a){return 0==a.lastIndexOf(this.Wa,0)},a)};Tc.prototype.get=function(a){var b=new S,c=this.Wa+"."+a;this.H.get(c,function(a){var e=chrome.runtime.lastError;e?b.w(e):b.G(a[c])});return b};
Tc.prototype.set=function(a,b){var c=new S,d={};d[this.Wa+"."+a]=b;this.H.set(d,function(){var a=chrome.runtime.lastError;a?c.w(a):c.G()});return c};var Y=function(){};Y.Yb=function(){return Y.Ib?Y.Ib:Y.Ib=new Y};Y.prototype.send=function(){return nc()};var Vc=function(a,b){this.Xa=[];var c=r(function(){this.Aa=new qc(b.Sa());ka(this.Xa,function(a){this.Aa.S(a)},this);this.Xa=null;return this.Aa},this);this.q=new Jc(a,c,Y.Yb())};Vc.prototype.Sa=function(){return this.q};Vc.prototype.S=function(a){this.Aa?this.Aa.S(a):this.Xa.push(a)};var Wc=function(a,b){this.k=a;this.mc=b};Wc.prototype.create=function(){return new Vc(this.k,this.mc)};var Xc=function(a,b){Q.call(this);this.ya=a||1;this.R=b||h;this.Ra=r(this.lc,this);this.Ta=s()};u(Xc,Q);g=Xc.prototype;g.enabled=!1;g.g=null;g.lc=function(){if(this.enabled){var a=s()-this.Ta;0<a&&a<.8*this.ya?this.g=this.R.setTimeout(this.Ra,this.ya-a):(this.g&&(this.R.clearTimeout(this.g),this.g=null),this.dispatchEvent("tick"),this.enabled&&(this.g=this.R.setTimeout(this.Ra,this.ya),this.Ta=s()))}};g.start=function(){this.enabled=!0;this.g||(this.g=this.R.setTimeout(this.Ra,this.ya),this.Ta=s())};
g.stop=function(){this.enabled=!1;this.g&&(this.R.clearTimeout(this.g),this.g=null)};g.l=function(){Xc.L.l.call(this);this.stop();delete this.R};var Yc=function(a,b,c){if(p(a))c&&(a=r(a,c));else if(a&&"function"==typeof a.handleEvent)a=r(a.handleEvent,a);else throw Error("Invalid listener argument");return 2147483647<b?-1:h.setTimeout(a,b||0)};var Z=function(a){E.call(this);this.Ua=a;this.b={}};u(Z,E);var Zc=[];Z.prototype.listen=function(a,b,c,d){m(b)||(b&&(Zc[0]=b.toString()),b=Zc);for(var e=0;e<b.length;e++){var f=tb(a,b[e],c||this.handleEvent,d||!1,this.Ua||this);if(!f)break;this.b[f.key]=f}return this};Z.prototype.bb=function(a,b,c,d){return $c(this,a,b,c,d)};var $c=function(a,b,c,d,e,f){if(m(c))for(var k=0;k<c.length;k++)$c(a,b,c[k],d,e,f);else{b=Ab(b,c,d||a.handleEvent,e,f||a.Ua||a);if(!b)return a;a.b[b.key]=b}return a};
Z.prototype.Va=function(a,b,c,d,e){if(m(b))for(var f=0;f<b.length;f++)this.Va(a,b[f],c,d,e);else c=c||this.handleEvent,e=e||this.Ua||this,c=ub(c),d=!!d,b=kb(a)?a.X(b,c,d,e):a?(a=wb(a))?a.X(b,c,d,e):null:null,b&&(Cb(b),delete this.b[b.key]);return this};Z.prototype.removeAll=function(){ua(this.b,Cb);this.b={}};Z.prototype.l=function(){Z.L.l.call(this);this.removeAll()};Z.prototype.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};var ad=function(){Q.call(this);this.ta=new Z(this);gb&&(hb?this.ta.listen(ib?document.body:window,["online","offline"],this.tb):(this.ub=gb?navigator.onLine:!0,this.g=new Xc(250),this.ta.listen(this.g,"tick",this.ac),this.g.start()))};u(ad,Q);ad.prototype.ac=function(){var a=gb?navigator.onLine:!0;a!=this.ub&&(this.ub=a,this.tb())};ad.prototype.tb=function(){this.dispatchEvent((gb?navigator.onLine:1)?"online":"offline")};
ad.prototype.l=function(){ad.L.l.call(this);this.ta.xa();this.ta=null;this.g&&(this.g.xa(),this.g=null)};var bd=function(a,b){this.k=a;this.e=b};bd.prototype.send=function(a,b){b.set(vc,this.k.la);return this.e.send(a,b)};var cd=function(a){this.e=a};cd.prototype.send=function(a,b){dd(b);ed(b);return this.e.send(a,b)};var dd=function(a){Ua(a,function(b,c){void 0!==b.maxLength&&"text"==b.valueType&&0<b.maxLength&&c.length>b.maxLength&&a.set(b,c.substring(0,b.maxLength))})},ed=function(a){Ua(a,function(b,c){void 0!==b.defaultValue&&c==b.defaultValue&&a.remove(b)})};var oc={status:"device-offline",Ba:void 0},fd={status:"rate-limited",Ba:void 0},gd={status:"sampled-out",Ba:void 0},hd={status:"sent",Ba:void 0};var id=function(a,b){this.Wb=a;this.e=b};id.prototype.send=function(a,b){var c;c=this.Wb;var d=c.pb(),e=Math.floor((d-c.ob)*c.Sb);0<e&&(c.$=Math.min(c.$+e,c.Tb),c.ob=d);1>c.$?c=!1:(c.$-=1,c=!0);return c||"item"==a||"transaction"==a?this.e.send(a,b):nc(fd)};var jd=function(){this.$=60;this.Tb=500;this.Sb=5E-4;this.pb=function(){return(new Date).getTime()};this.ob=this.pb()};var kd=function(a,b){this.k=a;this.e=b};kd.prototype.send=function(a,b){var c=b.get(vc),c=parseInt(c.split("-")[1],16),d;"timing"!=a?d=this.k.V:((d=b.get(yc))&&b.remove(yc),d||(d=this.k.V));return c<655.36*d?this.e.send(a,b):nc(gd)};var ld=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#(.*))?$/,md=L,nd=function(a,b){if(md){md=!1;var c=h.location;if(c){var d=c.href;if(d&&(d=(d=nd(3,d))?decodeURI(d):d)&&d!=c.hostname)throw md=!0,Error();}}return b.match(ld)[a]||null};var od=function(){};od.prototype.Eb=null;var qd=function(a){var b;(b=a.Eb)||(b={},pd(a)&&(b[0]=!0,b[1]=!0),b=a.Eb=b);return b};var rd,sd=function(){};u(sd,od);var td=function(a){return(a=pd(a))?new ActiveXObject(a):new XMLHttpRequest},pd=function(a){if(!a.Hb&&"undefined"==typeof XMLHttpRequest&&"undefined"!=typeof ActiveXObject){for(var b=["MSXML2.XMLHTTP.6.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],c=0;c<b.length;c++){var d=b[c];try{return new ActiveXObject(d),a.Hb=d}catch(e){}}throw Error("Could not create ActiveXObject. ActiveX might be disabled, or MSXML might not be installed");}return a.Hb};rd=new sd;var $=function(a){Q.call(this);this.headers=new x;this.fa=a||null;this.D=!1;this.ca=this.a=null;this.ba=this.Fa="";this.K=this.Ea=this.aa=this.Ha=!1;this.ea=0;this.da=null;this.cb="";this.Ga=this.Kb=!1};u($,Q);var ud=/^https?$/i,vd=["POST","PUT"],wd=[],xd=function(a,b,c){var d=new $;wd.push(d);b&&d.listen("complete",b);d.bb("ready",d.tc);d.send(a,"POST",c,void 0)};$.prototype.tc=function(){this.xa();pa(wd,this)};
$.prototype.send=function(a,b,c,d){if(this.a)throw Error("[goog.net.XhrIo] Object is active with another request="+this.Fa+"; newUri="+a);b=b?b.toUpperCase():"GET";this.Fa=a;this.ba="";this.Ha=!1;this.D=!0;this.a=this.fa?td(this.fa):td(rd);this.ca=this.fa?qd(this.fa):qd(rd);this.a.onreadystatechange=r(this.eb,this);try{this.Ea=!0,this.a.open(b,String(a),!0),this.Ea=!1}catch(e){this.ga(5,e);return}a=c||"";var f=this.headers.clone();d&&Ta(d,function(a,b){f.set(b,a)});d=oa(f.F());c=h.FormData&&a instanceof
h.FormData;!(0<=ja(vd,b))||d||c||f.set("Content-Type","application/x-www-form-urlencoded;charset=utf-8");f.forEach(function(a,b){this.a.setRequestHeader(b,a)},this);this.cb&&(this.a.responseType=this.cb);"withCredentials"in this.a&&(this.a.withCredentials=this.Kb);try{yd(this),0<this.ea&&((this.Ga=zd(this.a))?(this.a.timeout=this.ea,this.a.ontimeout=r(this.fb,this)):this.da=Yc(this.fb,this.ea,this)),this.aa=!0,this.a.send(a),this.aa=!1}catch(k){this.ga(5,k)}};
var zd=function(a){return I&&M(9)&&ea(a.timeout)&&void 0!==a.ontimeout},na=function(a){return"content-type"==a.toLowerCase()};$.prototype.fb=function(){"undefined"!=typeof aa&&this.a&&(this.ba="Timed out after "+this.ea+"ms, aborting",this.dispatchEvent("timeout"),this.abort(8))};$.prototype.ga=function(a,b){this.D=!1;this.a&&(this.K=!0,this.a.abort(),this.K=!1);this.ba=b;Ad(this);Bd(this)};var Ad=function(a){a.Ha||(a.Ha=!0,a.dispatchEvent("complete"),a.dispatchEvent("error"))};
$.prototype.abort=function(){this.a&&this.D&&(this.D=!1,this.K=!0,this.a.abort(),this.K=!1,this.dispatchEvent("complete"),this.dispatchEvent("abort"),Bd(this))};$.prototype.l=function(){this.a&&(this.D&&(this.D=!1,this.K=!0,this.a.abort(),this.K=!1),Bd(this,!0));$.L.l.call(this)};$.prototype.eb=function(){this.Y||(this.Ea||this.aa||this.K?Cd(this):this.jc())};$.prototype.jc=function(){Cd(this)};
var Cd=function(a){if(a.D&&"undefined"!=typeof aa&&(!a.ca[1]||4!=Dd(a)||2!=Ed(a)))if(a.aa&&4==Dd(a))Yc(a.eb,0,a);else if(a.dispatchEvent("readystatechange"),4==Dd(a)){a.D=!1;try{var b=Ed(a),c,d;t:switch(b){case 200:case 201:case 202:case 204:case 206:case 304:case 1223:d=!0;break t;default:d=!1}if(!(c=d)){var e;if(e=0===b){var f=nd(1,String(a.Fa));if(!f&&self.location)var k=self.location.protocol,f=k.substr(0,k.length-1);e=!ud.test(f?f.toLowerCase():"")}c=e}if(c)a.dispatchEvent("complete"),a.dispatchEvent("success");
else{var l;try{l=2<Dd(a)?a.a.statusText:""}catch(N){l=""}a.ba=l+" ["+Ed(a)+"]";Ad(a)}}finally{Bd(a)}}},Bd=function(a,b){if(a.a){yd(a);var c=a.a,d=a.ca[0]?ba:null;a.a=null;a.ca=null;b||a.dispatchEvent("ready");try{c.onreadystatechange=d}catch(e){}}},yd=function(a){a.a&&a.Ga&&(a.a.ontimeout=null);ea(a.da)&&(h.clearTimeout(a.da),a.da=null)},Dd=function(a){return a.a?a.a.readyState:0},Ed=function(a){try{return 2<Dd(a)?a.a.status:-1}catch(b){return-1}};var Fd=function(a,b,c){this.r=a||null;this.oc=!!c},Hd=function(a){if(!a.c&&(a.c=new x,a.h=0,a.r))for(var b=a.r.split("&"),c=0;c<b.length;c++){var d=b[c].indexOf("="),e=null,f=null;0<=d?(e=b[c].substring(0,d),f=b[c].substring(d+1)):e=b[c];e=decodeURIComponent(e.replace(/\+/g," "));e=Gd(a,e);a.add(e,f?decodeURIComponent(f.replace(/\+/g," ")):"")}};g=Fd.prototype;g.c=null;g.h=null;g.add=function(a,b){Hd(this);this.r=null;a=Gd(this,a);var c=this.c.get(a);c||this.c.set(a,c=[]);c.push(b);this.h++;return this};
g.remove=function(a){Hd(this);a=Gd(this,a);return this.c.Q(a)?(this.r=null,this.h-=this.c.get(a).length,this.c.remove(a)):!1};g.Q=function(a){Hd(this);a=Gd(this,a);return this.c.Q(a)};g.F=function(){Hd(this);for(var a=this.c.t(),b=this.c.F(),c=[],d=0;d<b.length;d++)for(var e=a[d],f=0;f<e.length;f++)c.push(b[d]);return c};g.t=function(a){Hd(this);var b=[];if(n(a))this.Q(a)&&(b=qa(b,this.c.get(Gd(this,a))));else{a=this.c.t();for(var c=0;c<a.length;c++)b=qa(b,a[c])}return b};
g.set=function(a,b){Hd(this);this.r=null;a=Gd(this,a);this.Q(a)&&(this.h-=this.c.get(a).length);this.c.set(a,[b]);this.h++;return this};g.get=function(a,b){var c=a?this.t(a):[];return 0<c.length?String(c[0]):b};g.toString=function(){if(this.r)return this.r;if(!this.c)return"";for(var a=[],b=this.c.F(),c=0;c<b.length;c++)for(var d=b[c],e=encodeURIComponent(String(d)),d=this.t(d),f=0;f<d.length;f++){var k=e;""!==d[f]&&(k+="="+encodeURIComponent(String(d[f])));a.push(k)}return this.r=a.join("&")};
g.clone=function(){var a=new Fd;a.r=this.r;this.c&&(a.c=this.c.clone(),a.h=this.h);return a};var Gd=function(a,b){var c=String(b);a.oc&&(c=c.toLowerCase());return c};var Id=function(a,b){this.$b=a;this.na=b};Id.prototype.send=function(a,b){if(gb&&!navigator.onLine)return pc();var c=new S,d=Jd(a,b);d.length>this.na?c.w({status:"payload-too-big",Ba:Ra("Encoded hit length == %s, but should be <= %s.",d.length,this.na)}):xd(this.$b,function(){c.G(hd)},d);return c};var Jd=function(a,b){var c=new Fd;c.add(Da.name,a);Ua(b,function(a,b){c.add(a.name,b.toString())});return c.toString()};var Kd=function(a,b,c){this.k=a;this.Qb=b;this.na=c};Kd.prototype.Sa=function(){if(!this.q){var a=this.k;if(!jc(a.ha).C)throw Error("Cannot construct shared channel prior to settings being ready.");new ad;var b=new cd(new Id(this.Qb,this.na)),c=new jd;this.q=new bd(a,new kd(a,new id(c,b)))}return this.q};var Ld=new x,Md=function(){if(!Ba){var a=new Tc("google-analytics");Ba=new X(a)}return Ba};t("goog.async.Deferred",S);t("goog.async.Deferred.prototype.addCallback",S.prototype.J);t("goog.events.EventTarget",Q);t("goog.events.EventTarget.prototype.listen",Q.prototype.listen);t("analytics.getService",function(a){var b=Ld.get(a,null);if(null===b){var b=chrome.runtime.getManifest().version,c=Md();if(!Ca){var d=Md();Ca=new Wc(d,new Kd(d,"https://www.google-analytics.com/collect",8192))}b=new Hc("ca1.5.2",a,b,c,Ca);Ld.set(a,b)}return b});t("analytics.internal.GoogleAnalyticsService",Hc);
t("analytics.internal.GoogleAnalyticsService.prototype.getTracker",Hc.prototype.Cc);t("analytics.internal.GoogleAnalyticsService.prototype.getConfig",Hc.prototype.Bc);t("analytics.internal.ServiceSettings",X);t("analytics.internal.ServiceSettings.prototype.setTrackingPermitted",X.prototype.Lc);t("analytics.internal.ServiceSettings.prototype.isTrackingPermitted",X.prototype.va);t("analytics.internal.ServiceSettings.prototype.setSampleRate",X.prototype.Kc);t("analytics.internal.ServiceTracker",W);
t("analytics.internal.ServiceTracker.prototype.send",W.prototype.send);t("analytics.internal.ServiceTracker.prototype.sendAppView",W.prototype.Gc);t("analytics.internal.ServiceTracker.prototype.sendEvent",W.prototype.Hc);t("analytics.internal.ServiceTracker.prototype.sendSocial",W.prototype.Jc);t("analytics.internal.ServiceTracker.prototype.sendException",W.prototype.Ic);t("analytics.internal.ServiceTracker.prototype.sendTiming",W.prototype.Cb);
t("analytics.internal.ServiceTracker.prototype.startTiming",W.prototype.Mc);t("analytics.internal.ServiceTracker.Timing",Gc);t("analytics.internal.ServiceTracker.Timing.prototype.send",Gc.prototype.send);t("analytics.internal.ServiceTracker.prototype.forceSessionStart",W.prototype.Ac);t("analytics.internal.ServiceTracker.prototype.addFilter",W.prototype.S);t("analytics.internal.FilterChannel.Hit",T);t("analytics.internal.FilterChannel.Hit.prototype.getHitType",T.prototype.Gb);
t("analytics.internal.FilterChannel.Hit.prototype.getParameters",T.prototype.T);t("analytics.internal.FilterChannel.Hit.prototype.cancel",T.prototype.cancel);t("analytics.ParameterMap",C);t("analytics.ParameterMap.Entry",C.Entry);t("analytics.ParameterMap.prototype.set",C.prototype.set);t("analytics.ParameterMap.prototype.get",C.prototype.get);t("analytics.ParameterMap.prototype.remove",C.prototype.remove);t("analytics.ParameterMap.prototype.toObject",C.prototype.Jb);
t("analytics.HitTypes.APPVIEW","appview");t("analytics.HitTypes.EVENT","event");t("analytics.HitTypes.SOCIAL","social");t("analytics.HitTypes.TRANSACTION","transaction");t("analytics.HitTypes.ITEM","item");t("analytics.HitTypes.TIMING","timing");t("analytics.HitTypes.EXCEPTION","exception");ua(Ka,function(a){var b=a.id.replace(/[A-Z]/,"_$&").toUpperCase();t("analytics.Parameters."+b,a)});t("analytics.filters.EventLabelerBuilder",A);
t("analytics.filters.EventLabelerBuilder.prototype.appendToExistingLabel",A.prototype.wc);t("analytics.filters.EventLabelerBuilder.prototype.stripValue",A.prototype.Nc);t("analytics.filters.EventLabelerBuilder.prototype.powersOfTwo",A.prototype.Ec);t("analytics.filters.EventLabelerBuilder.prototype.rangeBounds",A.prototype.Fc);t("analytics.filters.EventLabelerBuilder.prototype.build",A.prototype.Ca);t("analytics.filters.FilterBuilder",z);t("analytics.filters.FilterBuilder.builder",Pa);
t("analytics.filters.FilterBuilder.prototype.when",z.prototype.when);t("analytics.filters.FilterBuilder.prototype.whenHitType",z.prototype.zb);t("analytics.filters.FilterBuilder.prototype.whenValue",z.prototype.Oc);t("analytics.filters.FilterBuilder.prototype.applyFilter",z.prototype.xb);t("analytics.filters.FilterBuilder.prototype.build",z.prototype.Ca);t("analytics.EventBuilder",D);t("analytics.EventBuilder.builder",function(){return Va});t("analytics.EventBuilder.prototype.category",D.prototype.xc);
t("analytics.EventBuilder.prototype.action",D.prototype.action);t("analytics.EventBuilder.prototype.label",D.prototype.label);t("analytics.EventBuilder.prototype.value",D.prototype.value);t("analytics.EventBuilder.prototype.dimension",D.prototype.yc);t("analytics.EventBuilder.prototype.metric",D.prototype.Dc);t("analytics.EventBuilder.prototype.send",D.prototype.send); })()

View file

@ -1,12 +1,6 @@
/* Global */
.jBox-wrapper {
text-align: left;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.jBox-title,
@ -14,30 +8,28 @@
.jBox-container {
position: relative;
word-break: break-word;
box-sizing: border-box;
}
.jBox-container {
background: #fff;
border:1px solid #37a8db;
/*max-width:180px;*/
font-size:11px;
line-height:13px;
color:#525352;
}
.jBox-content {
padding: 4px 5px;
overflow: auto;
-webkit-transition: opacity .15s;
transition: opacity .15s;
padding: 8px 12px;
overflow-x: hidden;
overflow-y: auto;
transition: opacity .2s;
}
/* jBox Tooltip */
.jBox-footer {
box-sizing: border-box;
}
.jBox-Tooltip .jBox-container,
.jBox-Mouse .jBox-container {
border-radius: 3px;
box-shadow: 0 0 5px rgba(0, 0, 0, .3);
border-radius: 4px;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.25);
}
.jBox-Tooltip .jBox-title,
@ -46,316 +38,113 @@
font-weight: bold;
}
.jBox-hasTitle.jBox-Tooltip .jBox-content,
.jBox-hasTitle.jBox-Mouse .jBox-content {
.jBox-Tooltip.jBox-hasTitle .jBox-content,
.jBox-Mouse.jBox-hasTitle .jBox-content {
padding-top: 5px;
}
/* Pointer */
.jBox-Mouse {
pointer-events: none;
}
.jBox-pointer {
position: absolute;
overflow: hidden;
}
.jBox-pointer-top { top: 0; }
.jBox-pointer-bottom { bottom: 0; }
.jBox-pointer-left { left: 0; }
.jBox-pointer-right { right: 0; }
.jBox-pointer-top,
.jBox-pointer-bottom {
width: 22px;
height: 10px;
}
.jBox-pointer-left,
.jBox-pointer-right {
width: 10px;
height: 20px;
box-sizing: border-box;
}
.jBox-pointer:after {
content: '';
width: 10px;
height: 9px;
width: 20px;
height: 20px;
position: absolute;
background: #fff;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
border:1px solid #37a8db;
box-sizing: border-box;
}
.jBox-pointer-top {
top: 0;
}
.jBox-pointer-top:after {
left: 5px;
top: 6px;
box-shadow: -1px -1px 4px rgba(0, 0, 0, .2);
box-shadow: -1px -1px 2px rgba(0, 0, 0, 0.15);
}
.jBox-pointer-right {
right: 0;
}
.jBox-pointer-right:after {
top: 5px;
right: 6px;
box-shadow: 1px -1px 4px rgba(0, 0, 0, .2);
box-shadow: 1px -1px 2px rgba(0, 0, 0, 0.15);
}
.jBox-pointer-bottom:after {
left: 5px;
bottom: 6px;
box-shadow: 1px 1px 4px rgba(0, 0, 0, .2);
.jBox-pointer-left {
left: 0;
}
.jBox-pointer-left:after {
top: 5px;
left: 6px;
box-shadow: -1px 1px 4px rgba(0, 0, 0, .2);
box-shadow: -1px 1px 2px rgba(0, 0, 0, 0.15);
}
/* jBox Modal & jBox Confirm */
.jBox-Modal .jBox-container,
.jBox-Confirm .jBox-container {
border-radius: 3px;
box-shadow: 0 3px 15px rgba(0, 0, 0, .4), 0 0 5px rgba(0, 0, 0, .4);
.jBox-pointer-bottom {
bottom: 0;
}
.jBox-Modal .jBox-title,
.jBox-Confirm .jBox-title {
border-radius: 3px 3px 0 0;
padding: 10px 15px;
background: #f4f5f6;
border-bottom: 1px solid #ddd;
text-shadow: 0 1px 1px #fff;
.jBox-pointer-bottom:after {
left: 5px;
bottom: 6px;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.15);
}
.jBox-Modal.jBox-closeButton-title .jBox-title,
.jBox-Confirm.jBox-closeButton-title .jBox-title {
padding-right: 55px;
.jBox-pointer-top, .jBox-pointer-bottom {
width: 30px;
height: 12px;
}
.jBox-Modal.jBox-closeButton-box:before,
.jBox-Confirm.jBox-closeButton-box:before {
box-shadow: 0 3px 15px rgba(0, 0, 0, .4), 0 0 5px rgba(0, 0, 0, .4);
.jBox-pointer-left, .jBox-pointer-right {
width: 12px;
height: 30px;
}
/* jBox Modal */
.jBox-Modal .jBox-container {
border-radius: 4px;
}
.jBox-Modal .jBox-container, .jBox-Modal.jBox-closeButton-box:before {
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.4), 0 0 5px rgba(0, 0, 0, 0.4);
}
.jBox-Modal .jBox-content {
padding: 12px 15px;
padding: 15px 20px;
}
/* jBox Confirm */
.jBox-Confirm .jBox-content {
text-align: center;
padding: 45px 35px;
}
.jBox-Confirm-footer {
border-top: 1px solid #e2e2e2;
.jBox-Modal .jBox-title {
border-radius: 4px 4px 0 0;
padding: 15px 20px;
background: #fafafa;
border-radius: 0 0 3px 3px;
text-align: center;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.jBox-Confirm-button {
display: inline-block;
cursor: pointer;
font-size: 15px;
line-height: 30px;
height: 30px;
border-radius: 3px;
padding: 0 20px;
-webkit-transition: color .2s, background-color .2s;
transition: color .2s, background-color .2s;
.jBox-Modal.jBox-closeButton-title .jBox-title {
padding-right: 65px;
}
.jBox-Confirm-button-cancel {
text-shadow: 0 1px 1px rgba(255, 255, 255, .6);
background: #ddd;
color: #999;
margin-right: 25px;
.jBox-Modal .jBox-footer {
border-radius: 0 0 4px 4px;
}
.jBox-Confirm-button-cancel:hover {
background: #ccc;
color: #666;
}
.jBox-Confirm-button-submit {
text-shadow: 0 -1px 1px rgba(0, 0, 0, .2);
background: #5fc04c;
color: #fff;
}
.jBox-Confirm-button-submit:hover {
background: #53a642;
}
.jBox-Confirm-button-cancel:active,
.jBox-Confirm-button-submit:active {
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .26);
}
/* jBox Notice */
.jBox-Notice {
-webkit-transition: margin .2s;
transition: margin .2s;
}
.jBox-Notice .jBox-container {
border-radius: 3px;
box-shadow: 0 0 3px rgba(0, 0, 0, .2);
color: #fff;
text-shadow: 0 -1px 1px #000;
background: #333;
background-image: linear-gradient(to bottom, #444, #222);
}
.jBox-Notice .jBox-content {
border-radius: 3px;
padding: 12px 20px;
}
.jBox-Notice .jBox-title {
padding: 8px 20px 0;
font-weight: bold;
}
.jBox-hasTitle.jBox-Notice .jBox-content {
padding-top: 5px;
}
.jBox-Notice-color .jBox-container {
text-shadow: 0 -1px 1px rgba(0, 0, 0, .3);
}
.jBox-Notice-gray .jBox-container {
color: #666;
text-shadow: 0 1px 1px #fff;
background: #f4f4f4;
background-image: linear-gradient(to bottom, #fafafa, #f0f0f0);
}
.jBox-Notice-red .jBox-container {
background: #b02222;
background-image: linear-gradient(to bottom, #ee2222, #b02222);
}
.jBox-Notice-green .jBox-container {
background: #70a800;
background-image: linear-gradient(to bottom, #95cc2a, #70a800);
}
.jBox-Notice-blue .jBox-container {
background: #2b91d9;
background-image: linear-gradient(to bottom, #5abaff, #2b91d9);
}
.jBox-Notice-yellow .jBox-container {
color: #744700;
text-shadow: 0 1px 1px rgba(255, 255, 255, .6);
background: #ffb11f;
background-image: linear-gradient(to bottom, #ffd665, #ffb11f);
}
/* jBox Image */
.jBox-Image {
background: #fff;
padding: 8px 8px 45px;
border-radius: 5px;
}
.jBox-Image .jBox-content {
padding: 0;
width: 100%;
height: 100%;
}
.jBox-image-container {
border-radius: 5px;
background: #fff center center no-repeat;
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
}
.jBox-image-label {
box-sizing: border-box;
position: absolute;
background: #fff;
top: 100%;
left: 0;
width: 100%;
color: #333;
margin-top: -35px;
padding: 0 90px 5px 10px;
border-radius: 0 0 5px 5px;
-webkit-transition: opacity .3s;
transition: opacity .3s;
opacity: 0;
}
.jBox-image-label.active {
opacity: 1;
}
.jBox-image-pointer-next,
.jBox-image-pointer-prev {
position: absolute;
bottom: 0px;
width: 22px;
height: 45px;
background: no-repeat center center url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ijc0LjcgMjI0IDE4LjcgMzIiPg0KPHBhdGggZmlsbD0iIzAwMDAwMCIgZD0iTTkzLDIyNy40TDgwLjQsMjQwTDkzLDI1Mi42YzAuNCwwLjQsMC40LDEuMSwwLDEuNWwtMS42LDEuNmMtMC40LDAuNC0xLDAuNS0xLjUsMEw3NSwyNDAuN2MtMC40LTAuNC0wLjUtMSwwLTEuNWwxNC45LTE0LjljMC40LTAuNCwxLTAuNCwxLjUsMGwxLjYsMS42QzkzLjUsMjI2LjQsOTMuNCwyMjcsOTMsMjI3LjR6Ii8+DQo8L3N2Zz4=);
background-size: 11px auto;
cursor: pointer;
opacity: .6;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transition: opacity .2s;
transition: opacity .2s;
}
.jBox-image-pointer-next:hover,
.jBox-image-pointer-prev:hover {
opacity: 1;
}
.jBox-image-pointer-next {
right: 8px;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.jBox-image-pointer-prev {
right: 30px;
}
.jBox-image-open #jBox-overlay {
background-color: rgba(0, 0, 0, .86);
}
.jBox-Image.jBox-loading .jBox-container:before {
left: auto;
top: auto;
bottom: -33px;
right: 55px;
margin-top: -9px;
margin-left: -9px;
}
/* Close button */
.jBox-closeButton {
z-index: 1;
cursor: pointer;
position: absolute;
box-sizing: border-box;
}
.jBox-closeButton svg {
@ -364,68 +153,54 @@
right: 50%;
}
.jBox-closeButton path {
-webkit-transition: fill .2s;
transition: fill .2s;
}
.jBox-closeButton path {
fill: #aaa;
transition: fill .2s;
}
.jBox-closeButton:hover path {
fill: #888;
}
.jBox-closeButton:active path {
fill: #666;
}
/* Close button in overlay */
#jBox-overlay .jBox-closeButton {
.jBox-overlay .jBox-closeButton {
top: 0;
right: 0;
width: 40px;
height: 40px;
}
#jBox-overlay .jBox-closeButton svg {
.jBox-overlay .jBox-closeButton svg {
width: 20px;
height: 20px;
margin-top: -10px;
margin-right: -10px;
}
#jBox-overlay .jBox-closeButton path {
fill: #d2d4d6;
.jBox-overlay .jBox-closeButton path {
fill: #ddd;
}
#jBox-overlay .jBox-closeButton:hover path {
.jBox-overlay .jBox-closeButton:hover path {
fill: #fff;
}
#jBox-overlay .jBox-closeButton:active path {
fill: #b2b4b6;
}
/* Close button in title */
.jBox-closeButton-title .jBox-closeButton {
top: 0;
right: 0;
bottom: 0;
width: 40px;
width: 50px;
}
.jBox-closeButton-title .jBox-closeButton svg {
.jBox-closeButton-title svg {
width: 12px;
height: 12px;
margin-top: -6px;
margin-right: -6px;
}
/* Close button in box */
.jBox-closeButton-box {
box-sizing: border-box;
}
.jBox-closeButton-box .jBox-closeButton {
top: -8px;
@ -443,10 +218,6 @@
margin-right: -5px;
}
.jBox-hasTitle.jBox-Modal.jBox-closeButton-box .jBox-closeButton {
background: #f4f5f6;
}
.jBox-closeButton-box:before {
content: '';
position: absolute;
@ -455,118 +226,347 @@
width: 24px;
height: 24px;
border-radius: 50%;
box-shadow: 0 0 5px rgba(0, 0, 0, .3);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}
.jBox-pointerPosition-top.jBox-closeButton-box:before {
top: 4px;
.jBox-closeButton-box.jBox-pointerPosition-top:before {
top: 5px;
}
.jBox-pointerPosition-right.jBox-closeButton-box:before {
.jBox-closeButton-box.jBox-pointerPosition-right:before {
right: 2px;
}
/* Overlay */
.jBox-Modal.jBox-hasTitle.jBox-closeButton-box .jBox-closeButton {
background: #fafafa;
}
#jBox-overlay {
.jBox-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000;
background-color: rgba(0, 0, 0, .6);
background-color: rgba(0, 0, 0, 0.82);
}
/* Block scrolling */
.jBox-footer {
background: #fafafa;
border-top: 1px solid #eee;
padding: 8px 10px;
border-radius: 0 0 3px 3px;
}
body[class^="jBox-blockScroll-"],
body[class*=" jBox-blockScroll-"] {
overflow: hidden;
}
/* Draggable */
.jBox-draggable {
cursor: move;
}
/* Spinner */
@keyframes jBoxLoading {
to {transform: rotate(360deg);}
}
@-webkit-keyframes jBoxLoading {
to {-webkit-transform: rotate(360deg);}
to {
transform: rotate(360deg);
}
}
.jBox-loading .jBox-content {
min-height: 32px;
min-width: 38px;
opacity: .2;
}
.jBox-loading-spinner .jBox-content {
min-height: 38px !important;
min-width: 38px !important;
opacity: 0;
}
.jBox-spinner {
box-sizing: border-box;
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin-top: -10px;
margin-left: -10px;
width: 24px;
height: 24px;
margin-top: -12px;
margin-left: -12px;
}
.jBox-spinner:before {
content: 'Loading…';
display: block;
width: 20px;
height: 20px;
text-align: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.jBox-spinner:not(:required):before {
content: '';
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid rgba(0, 0, 0, .3);
border-top-color: rgba(0, 0, 0, .6);
border: 2px solid rgba(0, 0, 0, 0.2);
border-top-color: rgba(0, 0, 0, 0.8);
animation: jBoxLoading .6s linear infinite;
-webkit-animation: jBoxLoading .6s linear infinite;
}
/* IE8 fixes */
.jBox-IE8.jBox-Tooltip .jBox-container,
.jBox-IE8.jBox-Mouse .jBox-container {
border: 1px solid #aaa;
.jBox-countdown {
border-radius: 4px 4px 0 0;
z-index: 0;
background: #000;
opacity: .2;
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
overflow: hidden;
}
.jBox-IE8 .jBox-pointer:after {
display: none;
.jBox-countdown-inner {
top: 0;
right: 0;
width: 100%;
height: 3px;
position: absolute;
background: #fff;
}
.jBox-IE8 .jBox-pointer {
border: 0;
background: no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAYAAACN1PRVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNq01l0OwyAIAGAlvY+n8ZJ6Gk/EqqkNtf7ApCQ+LM34iuCmRUQzihjj6FH+kjWL8N4/Ph9GHpiTnC9SwDbhLGyvspSScc71KkOa/HpuuRhIK+psE2pjONouCQg7kBSEXUgC2tHo52mTTBpnaEATWlaYK6MrhIAaceWpOcsCrYp6FV4H/90zTWjUQ/gSevVQq0ecHqoOxWpYoO7p5O9ku2fnVtp7QAik2rsK3fnpWfjynJWpbw+1BkghurrYDjiCptg/4AxaYhJwBbEwDsiB2NgM5EIirAdKIDFGQSmU1+NaIPjJYt2I25vxT4ABAMhWvtle2YvmAAAAAElFTkSuQmCC);
[class^="jBox-animated-"],
[class*=" jBox-animated-"] {
animation-fill-mode: both;
}
.jBox-IE8 .jBox-pointer-top { background-position: center top; }
.jBox-IE8 .jBox-pointer-bottom { background-position: center bottom; }
.jBox-IE8 .jBox-pointer-left { background-position: left center; }
.jBox-IE8 .jBox-pointer-right { background-position: right center; }
.jBox-IE8.jBox-Modal .jBox-container {
border: 3px solid #aaa;
@keyframes jBox-tada {
0% {
transform: scale(1);
}
10%,
20% {
transform: scale(0.8) rotate(-4deg);
}
30%,
50%,
70%,
90% {
transform: scale(1.2) rotate(4deg);
}
40%,
60%,
80% {
transform: scale(1.2) rotate(-4deg);
}
100% {
transform: scale(1) rotate(0);
}
}
/* No SVG support fixes */
.jBox-nosvg .jBox-closeButton:before {
font-family: Verdana, sans-serif;
content: 'x';
text-align: center;
font-size: 18px;
color: #888;
.jBox-animated-tada {
animation: jBox-tada 1s;
}
@keyframes jBox-tadaSmall {
0% {
transform: scale(1);
}
10%,
20% {
transform: scale(0.9) rotate(-2deg);
}
30%,
50%,
70%,
90% {
transform: scale(1.1) rotate(2deg);
}
40%,
60%,
80% {
transform: scale(1.1) rotate(-2deg);
}
100% {
transform: scale(1) rotate(0);
}
}
.jBox-animated-tadaSmall {
animation: jBox-tadaSmall 1s;
}
@keyframes jBox-flash {
0%,
50%,
100% {
opacity: 1;
}
25%,
75% {
opacity: 0;
}
}
.jBox-animated-flash {
animation: jBox-flash .5s;
}
@keyframes jBox-shake {
0%,
100% {
transform: translateX(0);
}
20%,
60% {
transform: translateX(-6px);
}
40%,
80% {
transform: translateX(6px);
}
}
.jBox-animated-shake {
animation: jBox-shake .4s;
}
@keyframes jBox-pulseUp {
0% {
transform: scale(1);
}
50% {
transform: scale(1.15);
}
100% {
transform: scale(1);
}
}
.jBox-animated-pulseUp {
animation: jBox-pulseUp .25s;
}
@keyframes jBox-pulseDown {
0% {
transform: scale(1);
}
50% {
transform: scale(0.85);
}
100% {
transform: scale(1);
}
}
.jBox-animated-pulseDown {
animation: jBox-pulseDown .25s;
}
@keyframes jBox-popIn {
0% {
transform: scale(0);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
.jBox-animated-popIn {
animation: jBox-popIn .25s;
}
@keyframes jBox-popOut {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(0);
}
}
.jBox-animated-popOut {
animation: jBox-popOut .25s;
}
@keyframes jBox-fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.jBox-animated-fadeIn {
animation: jBox-fadeIn .2s;
}
@keyframes jBox-fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.jBox-animated-fadeOut {
animation: jBox-fadeOut .2s;
}
@keyframes jBox-slideUp {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-300px);
opacity: 0;
}
}
.jBox-animated-slideUp {
animation: jBox-slideUp .4s;
}
@keyframes jBox-slideRight {
0% {
transform: translateX(0);
}
100% {
transform: translateX(300px);
opacity: 0;
}
}
.jBox-animated-slideRight {
animation: jBox-slideRight .4s;
}
@keyframes jBox-slideDown {
0% {
transform: translateY(0);
}
100% {
transform: translateY(300px);
opacity: 0;
}
}
.jBox-animated-slideDown {
animation: jBox-slideDown .4s;
}
@keyframes jBox-slideLeft {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-300px);
opacity: 0;
}
}
.jBox-animated-slideLeft {
animation: jBox-slideLeft .4s;
}
/*# sourceMappingURL=jBox.css.map */

File diff suppressed because one or more lines are too long

View file

@ -1,46 +0,0 @@
/* Wrapper */
.jBox-ModalBorder {
border-radius: 8px;
background: rgba(0, 0, 0, .4);
padding: 8px;
box-shadow: 0 0 6px rgba(0, 0, 0, .2);
}
/* Container */
.jBox-ModalBorder .jBox-container {
border-radius: 5px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
}
/* Close button */
.jBox-ModalBorder.jBox-closeButton-box {
border-top-right-radius: 0;
}
.jBox-ModalBorder.jBox-closeButton-box:before {
display: none;
}
.jBox-ModalBorder.jBox-hasTitle.jBox-closeButton-box .jBox-closeButton,
.jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton {
background: rgba(0, 0, 0, .4);
border-radius: 0 50% 50% 0;
right: -32px;
top: -8px;
}
.jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton path {
fill: #d2d4d6;
}
.jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton:hover path {
fill: #fff;
}
.jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton:active path {
fill: #b2b4b6;
}

View file

@ -1,45 +0,0 @@
/* jBox: Notice */
.jBox-NoticeBorder .jBox-container {
border-radius: 6px;
}
.jBox-NoticeBorder .jBox-content,
.jBox-NoticeBorder .jBox-title {
padding-left: 26px;
}
.jBox-NoticeBorder.jBox-Notice-color .jBox-container {
color: #fff;
text-shadow: 0 -1px 0 #000;
background: rgba(0, 0, 0, .92);
}
.jBox-NoticeBorder.jBox-Notice-color .jBox-container:after {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 8px;
border-radius: 5px 0 0 5px;
background-image: linear-gradient(45deg, rgba(255, 255, 255, .5) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .5) 50%, rgba(255, 255, 255, .5) 75%, transparent 75%, transparent);
background-size: 18px 18px;
}
.jBox-NoticeBorder.jBox-Notice-red .jBox-container:after {
background-color: #ee0000;
}
.jBox-NoticeBorder.jBox-Notice-green .jBox-container:after {
background-color: #95cc2a;
}
.jBox-NoticeBorder.jBox-Notice-blue .jBox-container:after {
background-color: #4cb4ff;
}
.jBox-NoticeBorder.jBox-Notice-yellow .jBox-container:after {
background-color: #ffba00;
}

View file

@ -1,33 +0,0 @@
/* Container */
.jBox-TooltipBorder .jBox-container {
border-radius: 5px;
border: 2px solid #52a2cb;
}
/* Pointer */
.jBox-TooltipBorder .jBox-pointer:after {
border: 2px solid #52a2cb;
}
.jBox-TooltipBorder .jBox-pointer-top,
.jBox-TooltipBorder .jBox-pointer-bottom {
width: 34px;
height: 12px;
}
.jBox-TooltipBorder .jBox-pointer-left,
.jBox-TooltipBorder .jBox-pointer-right {
width: 12px;
height: 34px;
}
/* Close button */
.jBox-TooltipBorder.jBox-closeButton-box:before {
width: 28px;
height: 28px;
background: #52a2cb;
}

View file

@ -0,0 +1,49 @@
.jBox-NoticeFancy .jBox-content,
.jBox-NoticeFancy .jBox-title {
padding-left: 25px;
}
.jBox-NoticeFancy.jBox-Notice-color .jBox-container {
color: #fff;
background: #000;
}
.jBox-NoticeFancy.jBox-Notice-color .jBox-container:after {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 8px;
border-radius: 4px 0 0 4px;
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.4) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.4) 75%, transparent 75%, transparent);
background-size: 14px 14px;
}
.jBox-NoticeFancy.jBox-Notice-black .jBox-container:after,
.jBox-NoticeFancy.jBox-Notice-gray .jBox-container:after {
background-color: #888;
}
.jBox-NoticeFancy.jBox-Notice-red .jBox-container:after {
background-color: #e00;
}
.jBox-NoticeFancy.jBox-Notice-green .jBox-container:after {
background-color: #6c0;
}
.jBox-NoticeFancy.jBox-Notice-blue .jBox-container:after {
background-color: #49d;
}
.jBox-NoticeFancy.jBox-Notice-yellow .jBox-container:after {
background-color: #fb0;
}
.jBox-NoticeFancy .jBox-countdown {
left: 8px;
border-radius: 0 4px 0 0;
}
/*# sourceMappingURL=jBox.NoticeFancy.css.map */

View file

@ -0,0 +1 @@
.jBox-NoticeFancy .jBox-content,.jBox-NoticeFancy .jBox-title{padding-left:25px}.jBox-NoticeFancy.jBox-Notice-color .jBox-container{color:#fff;background:#000}.jBox-NoticeFancy.jBox-Notice-color .jBox-container:after{content:'';position:absolute;top:0;left:0;bottom:0;width:8px;border-radius:4px 0 0 4px;background-image:linear-gradient(45deg,rgba(255,255,255,.4) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.4) 50%,rgba(255,255,255,.4) 75%,transparent 75%,transparent);background-size:14px 14px}.jBox-NoticeFancy.jBox-Notice-black .jBox-container:after,.jBox-NoticeFancy.jBox-Notice-gray .jBox-container:after{background-color:#888}.jBox-NoticeFancy.jBox-Notice-red .jBox-container:after{background-color:#e00}.jBox-NoticeFancy.jBox-Notice-green .jBox-container:after{background-color:#6c0}.jBox-NoticeFancy.jBox-Notice-blue .jBox-container:after{background-color:#49d}.jBox-NoticeFancy.jBox-Notice-yellow .jBox-container:after{background-color:#fb0}.jBox-NoticeFancy .jBox-countdown{left:8px;border-radius:0 4px 0 0}

View file

@ -0,0 +1,39 @@
.jBox-TooltipBorder .jBox-container,
.jBox-TooltipBorder .jBox-pointer:after {
border: 2px solid #49d;
}
.jBox-TooltipBorder .jBox-pointer:after {
width: 22px;
height: 22px;
}
.jBox-TooltipBorder .jBox-pointer-top,
.jBox-TooltipBorder .jBox-pointer-bottom {
width: 34px;
height: 13px;
}
.jBox-TooltipBorder .jBox-pointer-top:after,
.jBox-TooltipBorder .jBox-pointer-bottom:after {
left: 6px;
}
.jBox-TooltipBorder .jBox-pointer-left,
.jBox-TooltipBorder .jBox-pointer-right {
width: 13px;
height: 34px;
}
.jBox-TooltipBorder .jBox-pointer-left:after,
.jBox-TooltipBorder .jBox-pointer-right:after {
top: 6px;
}
.jBox-TooltipBorder.jBox-closeButton-box:before {
width: 28px;
height: 28px;
background: #49d;
}
/*# sourceMappingURL=jBox.TooltipBorder.css.map */

View file

@ -0,0 +1 @@
.jBox-TooltipBorder .jBox-container,.jBox-TooltipBorder .jBox-pointer:after{border:2px solid #49d}.jBox-TooltipBorder .jBox-pointer:after{width:22px;height:22px}.jBox-TooltipBorder .jBox-pointer-bottom,.jBox-TooltipBorder .jBox-pointer-top{width:34px;height:13px}.jBox-TooltipBorder .jBox-pointer-bottom:after,.jBox-TooltipBorder .jBox-pointer-top:after{left:6px}.jBox-TooltipBorder .jBox-pointer-left,.jBox-TooltipBorder .jBox-pointer-right{width:13px;height:34px}.jBox-TooltipBorder .jBox-pointer-left:after,.jBox-TooltipBorder .jBox-pointer-right:after{top:6px}.jBox-TooltipBorder.jBox-closeButton-box:before{width:28px;height:28px;background:#49d}

View file

@ -0,0 +1,32 @@
.jBox-TooltipBorderThick .jBox-container {
box-shadow: none;
border-radius: 8px;
border: 4px solid #ccc;
}
.jBox-TooltipBorderThick .jBox-pointer:after {
box-shadow: none;
border: 4px solid #ccc;
width: 24px;
height: 24px;
}
.jBox-TooltipBorderThick .jBox-pointer-top,
.jBox-TooltipBorderThick .jBox-pointer-bottom {
width: 38px;
height: 13px;
}
.jBox-TooltipBorderThick .jBox-pointer-left,
.jBox-TooltipBorderThick .jBox-pointer-right {
width: 13px;
height: 38px;
}
.jBox-TooltipBorderThick.jBox-closeButton-box:before {
width: 32px;
height: 32px;
background: #ccc;
}
/*# sourceMappingURL=jBox.TooltipBorderThick.css.map */

View file

@ -0,0 +1 @@
.jBox-TooltipBorderThick .jBox-container{box-shadow:none;border-radius:8px;border:4px solid #ccc}.jBox-TooltipBorderThick .jBox-pointer:after{box-shadow:none;border:4px solid #ccc;width:24px;height:24px}.jBox-TooltipBorderThick .jBox-pointer-bottom,.jBox-TooltipBorderThick .jBox-pointer-top{width:38px;height:13px}.jBox-TooltipBorderThick .jBox-pointer-left,.jBox-TooltipBorderThick .jBox-pointer-right{width:13px;height:38px}.jBox-TooltipBorderThick.jBox-closeButton-box:before{width:32px;height:32px;background:#ccc}

View file

@ -1,31 +1,24 @@
/* Container */
.jBox-TooltipDark .jBox-container {
border-radius: 3px;
background: #222;
border-radius: 4px;
background: #000;
color: #fff;
box-shadow: 0 0 6px rgba(0, 0, 0, .4);
box-shadow: 0 0 6px rgba(0, 0, 0, 0.4);
}
/* Pointer */
.jBox-TooltipDark .jBox-pointer:after {
background: #222;
background: #000;
}
/* Close button */
.jBox-TooltipDark .jBox-closeButton {
background: #222;
background: #000;
}
.jBox-TooltipDark.jBox-closeButton-box:before {
box-shadow: 0 0 6px rgba(0, 0, 0, .4);
box-shadow: 0 0 6px rgba(0, 0, 0, 0.4);
}
.jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton path {
fill: #d2d4d6;
fill: #ddd;
}
.jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton:hover path {
@ -33,5 +26,7 @@
}
.jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton:active path {
fill: #b2b4b6;
fill: #bbb;
}
/*# sourceMappingURL=jBox.TooltipDark.css.map */

View file

@ -0,0 +1 @@
.jBox-TooltipDark .jBox-container{border-radius:4px;background:#000;color:#fff;box-shadow:0 0 6px rgba(0,0,0,.4)}.jBox-TooltipDark .jBox-pointer:after{background:#000}.jBox-TooltipDark .jBox-closeButton{background:#000}.jBox-TooltipDark.jBox-closeButton-box:before{box-shadow:0 0 6px rgba(0,0,0,.4)}.jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton path{fill:#ddd}.jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton:hover path{fill:#fff}.jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton:active path{fill:#bbb}

View file

@ -0,0 +1,54 @@
.jBox-TooltipError {
pointer-events: none;
}
.jBox-TooltipError .jBox-container {
border-radius: 2px;
background: #d00;
color: #fff;
font-weight: bold;
font-size: 13px;
}
.jBox-TooltipError .jBox-content {
padding: 0 10px;
line-height: 28px;
}
.jBox-TooltipError .jBox-pointer:after {
background: #d00;
width: 20px;
height: 20px;
}
.jBox-TooltipError .jBox-pointer-top, .jBox-TooltipError .jBox-pointer-bottom {
width: 22px;
height: 8px;
}
.jBox-TooltipError .jBox-pointer-right, .jBox-TooltipError .jBox-pointer-left {
width: 8px;
height: 22px;
}
.jBox-TooltipError .jBox-pointer-top:after {
left: 1px;
top: 6px;
}
.jBox-TooltipError .jBox-pointer-right:after {
top: 1px;
right: 6px;
}
.jBox-TooltipError .jBox-pointer-bottom:after {
left: 1px;
bottom: 6px;
}
.jBox-TooltipError .jBox-pointer-left:after {
top: 1px;
left: 6px;
}
/*# sourceMappingURL=jBox.TooltipError.css.map */

View file

@ -0,0 +1 @@
.jBox-TooltipError{pointer-events:none}.jBox-TooltipError .jBox-container{border-radius:2px;background:#d00;color:#fff;font-weight:700;font-size:13px}.jBox-TooltipError .jBox-content{padding:0 10px;line-height:28px}.jBox-TooltipError .jBox-pointer:after{background:#d00;width:20px;height:20px}.jBox-TooltipError .jBox-pointer-bottom,.jBox-TooltipError .jBox-pointer-top{width:22px;height:8px}.jBox-TooltipError .jBox-pointer-left,.jBox-TooltipError .jBox-pointer-right{width:8px;height:22px}.jBox-TooltipError .jBox-pointer-top:after{left:1px;top:6px}.jBox-TooltipError .jBox-pointer-right:after{top:1px;right:6px}.jBox-TooltipError .jBox-pointer-bottom:after{left:1px;bottom:6px}.jBox-TooltipError .jBox-pointer-left:after{top:1px;left:6px}

View file

@ -0,0 +1,49 @@
.jBox-TooltipSmall {
pointer-events: none;
}
.jBox-TooltipSmall .jBox-container {
border-radius: 2px;
}
.jBox-TooltipSmall .jBox-content {
padding: 0 10px;
line-height: 28px;
}
.jBox-TooltipSmall .jBox-pointer:after {
width: 20px;
height: 20px;
}
.jBox-TooltipSmall .jBox-pointer-top, .jBox-TooltipSmall .jBox-pointer-bottom {
width: 22px;
height: 8px;
}
.jBox-TooltipSmall .jBox-pointer-right, .jBox-TooltipSmall .jBox-pointer-left {
width: 8px;
height: 22px;
}
.jBox-TooltipSmall .jBox-pointer-top:after {
left: 1px;
top: 6px;
}
.jBox-TooltipSmall .jBox-pointer-right:after {
top: 1px;
right: 6px;
}
.jBox-TooltipSmall .jBox-pointer-bottom:after {
left: 1px;
bottom: 6px;
}
.jBox-TooltipSmall .jBox-pointer-left:after {
top: 1px;
left: 6px;
}
/*# sourceMappingURL=jBox.TooltipSmall.css.map */

View file

@ -0,0 +1 @@
.jBox-TooltipSmall{pointer-events:none}.jBox-TooltipSmall .jBox-container{border-radius:2px}.jBox-TooltipSmall .jBox-content{padding:0 10px;line-height:28px}.jBox-TooltipSmall .jBox-pointer:after{width:20px;height:20px}.jBox-TooltipSmall .jBox-pointer-bottom,.jBox-TooltipSmall .jBox-pointer-top{width:22px;height:8px}.jBox-TooltipSmall .jBox-pointer-left,.jBox-TooltipSmall .jBox-pointer-right{width:8px;height:22px}.jBox-TooltipSmall .jBox-pointer-top:after{left:1px;top:6px}.jBox-TooltipSmall .jBox-pointer-right:after{top:1px;right:6px}.jBox-TooltipSmall .jBox-pointer-bottom:after{left:1px;bottom:6px}.jBox-TooltipSmall .jBox-pointer-left:after{top:1px;left:6px}

View file

@ -0,0 +1,53 @@
.jBox-TooltipSmallGray {
pointer-events: none;
}
.jBox-TooltipSmallGray .jBox-container {
font-size: 13px;
line-height: 24px;
border-radius: 12px;
background-image: linear-gradient(to bottom, #fafafa, #f2f2f2);
}
.jBox-TooltipSmallGray .jBox-content {
padding: 0 10px;
}
.jBox-TooltipSmallGray .jBox-pointer:after {
width: 20px;
height: 20px;
}
.jBox-TooltipSmallGray .jBox-pointer-top, .jBox-TooltipSmallGray .jBox-pointer-bottom {
width: 22px;
height: 8px;
}
.jBox-TooltipSmallGray .jBox-pointer-left, .jBox-TooltipSmallGray .jBox-pointer-right {
width: 8px;
height: 22px;
}
.jBox-TooltipSmallGray .jBox-pointer-top:after {
background: #fafafa;
left: 1px;
top: 6px;
}
.jBox-TooltipSmallGray .jBox-pointer-right:after {
top: 1px;
right: 6px;
}
.jBox-TooltipSmallGray .jBox-pointer-bottom:after {
background: #f2f2f2;
left: 1px;
bottom: 6px;
}
.jBox-TooltipSmallGray .jBox-pointer-left:after {
top: 1px;
left: 6px;
}
/*# sourceMappingURL=jBox.TooltipSmallGray.css.map */

View file

@ -0,0 +1 @@
.jBox-TooltipSmallGray{pointer-events:none}.jBox-TooltipSmallGray .jBox-container{font-size:13px;line-height:24px;border-radius:12px;background-image:linear-gradient(to bottom,#fafafa,#f2f2f2)}.jBox-TooltipSmallGray .jBox-content{padding:0 10px}.jBox-TooltipSmallGray .jBox-pointer:after{width:20px;height:20px}.jBox-TooltipSmallGray .jBox-pointer-bottom,.jBox-TooltipSmallGray .jBox-pointer-top{width:22px;height:8px}.jBox-TooltipSmallGray .jBox-pointer-left,.jBox-TooltipSmallGray .jBox-pointer-right{width:8px;height:22px}.jBox-TooltipSmallGray .jBox-pointer-top:after{background:#fafafa;left:1px;top:6px}.jBox-TooltipSmallGray .jBox-pointer-right:after{top:1px;right:6px}.jBox-TooltipSmallGray .jBox-pointer-bottom:after{background:#f2f2f2;left:1px;bottom:6px}.jBox-TooltipSmallGray .jBox-pointer-left:after{top:1px;left:6px}

View file

@ -1,125 +0,0 @@
'use strict';
var nwdialog = {
_context: (typeof global === 'undefined' || typeof global.DOMDocument === 'undefined') ? document : global.DOMDocument,
setContext: function(context) {
this._context = context;
},
openFileDialog: function(filter, multiple, workdir, callback) {
var fn = callback;
var node = this._context.createElement('input');
node.type = 'file';
node.id = 'open-file-dialog';
node.style = 'display: none';
if (typeof filter === 'function') {
fn = filter;
} else if (typeof filter === 'string') {
node.setAttribute('accept', filter);
} else if (typeof filter === 'boolean' && filter === true) {
node.setAttribute('multiple', '');
} else if (this.isArray(filter)) {
node.setAttribute('accept', filter.join(','));
}
if (typeof multiple === 'function') {
fn = multiple;
} else if (typeof multiple === 'string') {
node.setAttribute('nwworkingdir', multiple);
} else if (typeof multiple === 'boolean' && multiple === true) {
node.setAttribute('multiple', '');
}
if (typeof workdir === 'function') {
fn = workdir;
} else if (typeof workdir === 'string') {
node.setAttribute('nwworkingdir', workdir);
}
this._context.body.appendChild(node);
node.addEventListener('change', function(e) {
fn(node.value);
node.remove();
});
node.click();
},
saveFileDialog: function(name, accept, directory, callback) {
var fn = callback;
var node = this._context.createElement('input');
node.type = 'file';
node.id = 'save-file-dialog';
node.style = 'display: none';
node.setAttribute('nwsaveas', '');
if (typeof name === 'function') {
fn = name;
} else if (typeof name === 'string') {
node.setAttribute('nwsaveas', name);
}
if (typeof accept === 'function') {
fn = accept;
} else if (typeof accept === 'string') {
node.setAttribute('accept', accept);
} else if (this.isArray(accept)) {
node.setAttribute('accept', accept.join(','));
}
if (typeof directory === 'function') {
fn = directory;
} else if (typeof directory === 'string') {
node.setAttribute('nwworkingdir', directory);
}
this._context.body.appendChild(node);
node.addEventListener('change', function() {
fn(node.value);
node.remove();
});
node.click();
},
folderBrowserDialog: function(workdir, callback) {
var fn = callback;
var node = this._context.createElement('input');
node.type = 'file';
node.id = 'folder-browser-dialog';
node.style = 'display: none';
node.nwdirectory= true;
if (typeof workdir === 'function') {
fn = workdir
} else if (typeof workdir === 'string') {
node.setAttribute('nwworkingdir', workdir);
}
this._context.body.appendChild(node);
node.addEventListener('change', function() {
fn(node.value);
node.remove();
});
node.click();
},
isArray: function(value) {
return Object.prototype.toString.call(value) === '[object Array]';
}
}
if (typeof nw !== 'undefined') {
if (typeof exports == 'undefined') {
nw.Dialog = nwdialog;
window.dialog = nwdialog;
} else {
module.exports = nwdialog;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

6
js/libraries/three/three.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,23 +1,189 @@
'use strict';
function localize() {
var localized = 0;
// Thanks to Betaflight :)
var translate = function(messageID) {
window.$ = window.jQuery = require('jquery');
const { app } = require('@electron/remote');
const path = require('path');
const i18next = require('i18next');
const i18nextXHRBackend = require('i18next-xhr-backend');
const Store = require('electron-store');
const store = new Store();
const availableLanguages = ['en'];
const i18n = {};
i18n.init = function (callback) {
i18next.use(i18nextXHRBackend);
i18next.init({
lng: store.get('userLanguage', app.getLocale()),
getAsync: false,
debug: true,
ns: ['messages'],
defaultNS:['messages'],
fallbackLng: 'en',
backend: {
loadPath: path.join(__dirname, "./../locale/{{lng}}/{{ns}}.json"),
parse: i18n.parseInputFile,
},
}, function(err) {
if (err !== undefined) {
console.error(`Error loading i18n: ${err}`);
} else {
console.log('i18n system loaded');
const detectedLanguage = i18n.getMessage(`language_${i18n.getValidLocale("DEFAULT")}`);
i18next.addResourceBundle('en', 'messages', {"detectedLanguage": detectedLanguage }, true, true);
i18next.on('languageChanged', function () {
i18n.localize(true);
});
}
if (callback) {
callback();
}
});
}
i18n.parseInputFile = function (data) {
// Remove the $n interpolate of Chrome $1, $2, ... -> {{1}}, {{2}}, ...
const REGEXP_CHROME = /\$([1-9])/g;
const dataChrome = data.replace(REGEXP_CHROME, '{{$1}}');
// Remove the .message of the nesting $t(xxxxx.message) -> $t(xxxxx)
const REGEXP_NESTING = /\$t\(([^\)]*).message\)/g;
const dataNesting = dataChrome.replace(REGEXP_NESTING, '$t($1)');
// Move the .message of the json object to root xxxxx.message -> xxxxx
const jsonData = JSON.parse(dataNesting);
Object.entries(jsonData).forEach(([key, value]) => {
jsonData[key] = value.message;
});
return jsonData;
}
i18n.getValidLocale = function(userLocale) {
let validUserLocale = userLocale;
if (validUserLocale === 'DEFAULT') {
validUserLocale = app.getLocale();
console.log(`Detected locale ${validUserLocale}`);
}
return validUserLocale;
}
i18n.getMessage = function(messageID, parameters) {
let parametersObject;
if (!i18next.exists(messageID)) {
return false;
}
// Option 1, no parameters or Object as parameters (i18Next type parameters)
if ((parameters === undefined) || ((parameters.constructor !== Array) && (parameters instanceof Object))) {
parametersObject = parameters;
// Option 2: parameters as $1, $2, etc.
// (deprecated, from the old Chrome i18n
} else {
// Convert the input to an array
let parametersArray = parameters;
if (parametersArray.constructor !== Array) {
parametersArray = [parameters];
}
parametersObject = {};
parametersArray.forEach(function(parameter, index) {
parametersObject[index + 1] = parameter;
});
}
return i18next.t(messageID, parametersObject);
};
i18n.getCurrentLanguage = function() {
return i18next.language;
};
i18n.getLanguages = function() {
return availableLanguages;
}
i18n.changeLanguage = function(languageSelected) {
store.set('userLanguage', languageSelected);
i18next.changeLanguage(i18n.getValidLocale(languageSelected));
//i18n.selectedLanguage = languageSelected;
};
i18n.localize = function (reTranslate = false) {
let localized = 0;
const translate = function(messageID) {
localized++;
return chrome.i18n.getMessage(messageID);
return i18n.getMessage(messageID);
};
if (reTranslate) {
$('[i18n]').each(function() {
const element = $(this);
element.html(translate(element.attr('i18n')));
});
$('[data-i18n]').each(function() {
const element = $(this);
const translated = translate(element.data('i18n'));
element.html(translated);
if (element.attr("title") !== "") {
element.attr("title", translated);
}
});
$('[i18n_title]').each(function() {
const element = $(this);
element.attr('title', translate(element.attr('i18n_title')));
});
$('[data-i18n_title]').each(function() {
const element = $(this);
element.attr('title', translate(element.data('i18n_title')));
});
$('[i18n_label]').each(function() {
const element = $(this);
element.attr('label', translate(element.attr('i18n_label')));
});
$('[data-i18n_label]').each(function() {
const element = $(this);
element.attr('label', translate(element.data('i18n_label')));
});
$('[i18n_value]').each(function() {
const element = $(this);
element.val(translate(element.attr('i18n_value')));
});
$('[i18n_placeholder]').each(function() {
const element = $(this);
element.attr('placeholder', translate(element.attr('i18n_placeholder')));
});
} else {
$('[i18n]:not(.i18n-replaced)').each(function() {
var element = $(this);
const element = $(this);
element.html(translate(element.attr('i18n')));
element.addClass('i18n-replaced');
});
$('[data-i18n]:not(.i18n-replaced)').each(function() {
var element = $(this);
const element = $(this);
const translated = translate(element.data('i18n'));
element.html(translated);
@ -28,46 +194,49 @@ function localize() {
});
$('[i18n_title]:not(.i18n_title-replaced)').each(function() {
var element = $(this);
const element = $(this);
element.attr('title', translate(element.attr('i18n_title')));
element.addClass('i18n_title-replaced');
});
$('[data-i18n_title]:not(.i18n_title-replaced)').each(function() {
var element = $(this);
const element = $(this);
element.attr('title', translate(element.data('i18n_title')));
element.addClass('i18n_title-replaced');
});
$('[i18n_label]:not(.i18n_label-replaced)').each(function() {
var element = $(this);
const element = $(this);
element.attr('label', translate(element.attr('i18n_label')));
element.addClass('i18n_label-replaced');
});
$('[data-i18n_label]:not(.i18n_label-replaced)').each(function() {
var element = $(this);
const element = $(this);
element.attr('label', translate(element.data('i18n_label')));
element.addClass('i18n_label-replaced');
});
$('[i18n_value]:not(.i18n_value-replaced)').each(function() {
var element = $(this);
const element = $(this);""
element.val(translate(element.attr('i18n_value')));
element.addClass('i18n_value-replaced');
});
$('[i18n_placeholder]:not(.i18n_placeholder-replaced)').each(function() {
var element = $(this);
const element = $(this);
element.attr('placeholder', translate(element.attr('i18n_placeholder')));
element.addClass('i18n_placeholder-replaced');
});
}
return localized;
}
module.exports = i18n;

View file

@ -1,6 +1,8 @@
/*global $,FC*/
'use strict';
const FC = require('./fc');
const { GUI } = require('./../js/gui');
let LogicCondition = function (enabled, activatorId, operation, operandAType, operandAValue, operandBType, operandBValue, flags) {
let self = {};
let $row;
@ -164,7 +166,7 @@ let LogicCondition = function (enabled, activatorId, operation, operandAType, op
/*
* Bind events
*/
$t.change(self.onOperatorTypeChange);
$t.on('change', self.onOperatorTypeChange);
}
}
@ -218,7 +220,7 @@ let LogicCondition = function (enabled, activatorId, operation, operandAType, op
if (self.getEnabled()) {
GUI.renderLogicConditionSelect(
$e,
LOGIC_CONDITIONS,
FC.LOGIC_CONDITIONS,
self.getActivatorId,
self.onActivatorChange,
true,
@ -306,7 +308,7 @@ let LogicCondition = function (enabled, activatorId, operation, operandAType, op
$t.append('</optgroup>');
$t.change(self.onOperatorChange);
$t.on('change', self.onOperatorChange);
self.renderOperand(0);
self.renderOperand(1);
@ -316,3 +318,5 @@ let LogicCondition = function (enabled, activatorId, operation, operandAType, op
return self;
};
module.exports = LogicCondition;

View file

@ -1,6 +1,6 @@
'use strict';
let LogicConditionsCollection = function () {
var LogicConditionsCollection = function () {
let self = {},
data = [],
@ -46,8 +46,6 @@ let LogicConditionsCollection = function () {
self.get()[k].render(k, $table);
}
}
GUI.switchery();
};
self.onSave = function () {
@ -68,8 +66,8 @@ let LogicConditionsCollection = function () {
self.init = function ($element) {
$container = $element;
$container.find('.logic__save').click(self.onSave);
$container.find('.logic__close').click(self.onClose);
$container.find('.logic__save').on('click', self.onSave);
$container.find('.logic__close').on('click', self.onClose);
};
@ -85,3 +83,5 @@ let LogicConditionsCollection = function () {
return self;
};
module.exports = LogicConditionsCollection;

View file

@ -23,3 +23,5 @@ let LogicConditionsStatus = function () {
return self;
};
module.exports = LogicConditionsStatus;

View file

@ -1,8 +1,6 @@
'use strict';
var helper = helper || {};
helper.ltmDecoder = (function () {
const ltmDecoder = (function () {
let TELEMETRY = {
//A frame
@ -258,3 +256,5 @@ helper.ltmDecoder = (function () {
return publicScope;
})();
module.exports = ltmDecoder;

166
js/main.js Normal file
View file

@ -0,0 +1,166 @@
const { app, BrowserWindow, ipcMain } = require('electron');
const windowStateKeeper = require('electron-window-state');
const path = require('path');
const Store = require('electron-store');
Store.initRenderer();
require('@electron/remote/main').initialize();
const usbBootloaderIds = [
{ vendorId: 1155, productId: 57105},
{ vendorId: 11836, productId: 57105}
];
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
app.quit();
}
let mainWindow = null;
let bluetoothDeviceChooser = null;
let btDeviceList = null;
let selectBluetoothCallback = null;
// In Electron the bluetooth device chooser didn't exist, so we have to build our own
function createDeviceChooser() {
bluetoothDeviceChooser = new BrowserWindow({
parent: mainWindow,
width: 400,
height: 400,
webPreferences: {
preload: path.join(__dirname, 'libraries/bluetooth-device-chooser/preload.js')
}
});
bluetoothDeviceChooser.removeMenu();
bluetoothDeviceChooser.loadFile(path.join(__dirname, 'libraries/bluetooth-device-chooser/index.html'));
bluetoothDeviceChooser.on('closed', () => {
btDeviceList = null;
if (selectBluetoothCallback) {
selectBluetoothCallback('');
selectBluetoothCallback = null;
}
bluetoothDeviceChooser = null;
});
ipcMain.on('deviceSelected', (_event, deviceID) => {
if (selectBluetoothCallback) {
selectBluetoothCallback(deviceID);
selectBluetoothCallback = null;
}
});
}
app.on('ready', () => {
let mainWindowState = windowStateKeeper({
defaultWidth: 800,
defaultHeight: 600
});
mainWindow = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
autoHideMenuBar: true,
icon: "images/inav_icon_128.png",
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
event.preventDefault();
selectBluetoothCallback = callback;
const compare = (a, b) => {
if (a.length !== b.length) {
return false;
}
a.every((element, index) => {
if (element.deviceId !== b[index].deviceId) {
return false;
}
})
return true;
}
if (!btDeviceList || !compare(btDeviceList, deviceList)) {
btDeviceList = [...deviceList];
if (!bluetoothDeviceChooser) {
createDeviceChooser();
}
bluetoothDeviceChooser.webContents.send('ble-scan', btDeviceList);
}
});
mainWindow.webContents.session.on('select-usb-device', (event, details, callback) => {
console.log(details.deviceList)
let premittedDevice = null;
if (details.deviceList) {
details.deviceList.every((device, idx) => {
if (device.productId == usbBootloaderIds[idx].productId && device.vendorId == usbBootloaderIds[idx].vendorId) {
premittedDevice = device.deviceId;
return;
}
});
}
if (premittedDevice) {
callback(premittedDevice);
} else {
callback();
}
});
mainWindow.webContents.session.setDevicePermissionHandler((details) => {
if (details.deviceType === 'usb' && details.origin === 'file://') {
return true;
}
});
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
return {
action: 'allow',
overrideBrowserWindowOptions: {
autoHideMenuBar: true
}
}
});
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors')
require("@electron/remote/main").enable(mainWindow.webContents);
mainWindow.removeMenu();
mainWindow.setMinimumSize(800, 600);
mainWindow.loadFile('./index.html');
mainWindowState.manage(mainWindow);
// Open the DevTools.
if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools();
}
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
console.log("We're closing...");
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

View file

@ -1,52 +1,63 @@
'use strict';
const SERVO_GIMBAL_PITCH = 0,
SERVO_GIMBAL_ROLL = 1,
SERVO_ELEVATOR = 1,
SERVO_ELEVON_1 = 1,
SERVO_ELEVON_2 = 2,
SERVO_FLAPPERON_1 = 2,
SERVO_FLAPPERON_2 = 3,
SERVO_RUDDER = 4,
SERVO_BICOPTER_LEFT = 4,
SERVO_BICOPTER_RIGHT = 5,
SERVO_DUALCOPTER_LEFT = 4,
SERVO_DUALCOPTER_RIGHT = 5,
SERVO_SINGLECOPTER_1 = 3,
SERVO_SINGLECOPTER_2 = 4,
SERVO_SINGLECOPTER_3 = 5,
SERVO_SINGLECOPTER_4 = 6;
const MotorMixRule = require('./motorMixRule');
const ServoMixRule = require('./servoMixRule');
const INPUT_STABILIZED_ROLL = 0,
INPUT_STABILIZED_PITCH = 1,
INPUT_STABILIZED_YAW = 2,
INPUT_STABILIZED_THROTTLE = 3,
INPUT_RC_ROLL = 4,
INPUT_RC_PITCH = 5,
INPUT_RC_YAW = 6,
INPUT_RC_THROTTLE = 7,
INPUT_RC_AUX1 = 8,
INPUT_RC_AUX2 = 9,
INPUT_RC_AUX3 = 10,
INPUT_RC_AUX4 = 11,
INPUT_GIMBAL_PITCH = 12,
INPUT_GIMBAL_ROLL = 13,
INPUT_FEATURE_FLAPS = 14;
STABILIZED_ROLL_POSITIVE = 23;
STABILIZED_ROLL_NEGATIVE = 24;
STABILIZED_PITCH_POSITIVE = 25;
STABILIZED_PITCH_NEGATIVE = 26;
STABILIZED_YAW_POSITIVE = 27;
STABILIZED_YAW_NEGATIVE = 28;
const SERVO = {
GIMBAL_PITCH: 0,
GIMBAL_ROLL: 1,
ELEVATOR: 1,
ELEVON_1: 1,
ELEVON_2: 2,
FLAPPERON_1: 2,
FLAPPERON_2: 3,
RUDDER: 4,
BICOPTER_LEFT: 4,
BICOPTER_RIGHT: 5,
DUALCOPTER_LEFT: 4,
DUALCOPTER_RIGHT: 5,
SINGLECOPTER_1: 3,
SINGLECOPTER_2: 4,
SINGLECOPTER_3: 5,
SINGLECOPTER_4: 6
}
const
PLATFORM_MULTIROTOR = 0,
PLATFORM_AIRPLANE = 1,
PLATFORM_HELICOPTER = 2,
PLATFORM_TRICOPTER = 3,
PLATFORM_ROVER = 4,
PLATFORM_BOAT = 5,
PLATFORM_OTHER = 6;
const INPUT = {
STABILIZED_ROLL: 0,
STABILIZED_PITCH: 1,
STABILIZED_YAW: 2,
STABILIZED_THROTTLE: 3,
RC_ROLL: 4,
RC_PITCH: 5,
RC_YAW: 6,
RC_THROTTLE: 7,
RC_AUX1: 8,
RC_AUX2: 9,
RC_AUX3: 10,
RC_AUX4: 11,
GIMBAL_PITCH: 12,
GIMBAL_ROLL: 13,
FEATURE_FLAPS: 14
}
const STABILIZED = {
ROLL_POSITIVE: 23,
ROLL_NEGATIVE: 24,
PITCH_POSITIVE: 25,
PITCH_NEGATIVE: 26,
YAW_POSITIVE: 27,
YAW_NEGATIVE: 28
}
const PLATFORM = {
MULTIROTOR: 0,
AIRPLANE: 1,
HELICOPTER: 2,
TRICOPTER: 3,
ROVER: 4,
BOAT: 5,
OTHER: 6
}
// generate mixer
const mixerList = [
@ -58,14 +69,14 @@ const mixerList = [
image: 'tri',
enabled: true,
legacy: true,
platform: PLATFORM_TRICOPTER,
platform: PLATFORM.TRICOPTER,
motorMixer: [
new MotorMixRule(1.0, 0.0, 1.333333, 0.0), // REAR
new MotorMixRule(1.0, -1.0, -0.666667, 0.0), // RIGHT
new MotorMixRule(1.0, 1.0, -0.666667, 0.0), // LEFT
],
servoMixer: [
new ServoMixRule(SERVO_RUDDER, INPUT_STABILIZED_YAW, 100, 0),
new ServoMixRule(SERVO.RUDDER, INPUT.STABILIZED_YAW, 100, 0),
]
}, // 1
{
@ -75,7 +86,7 @@ const mixerList = [
image: 'quad_x',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, -1.0, 1.0, -1.0), // REAR_R
new MotorMixRule(1.0, -1.0, -1.0, 1.0), // FRONT_R
@ -91,7 +102,7 @@ const mixerList = [
image: 'quad_p',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, 0.0, 1.0, -1.0), // REAR
new MotorMixRule(1.0, -1.0, 0.0, 1.0), // RIGHT
@ -107,7 +118,7 @@ const mixerList = [
image: 'bicopter',
enabled: false,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [],
servoMixer: []
}, // 4
@ -118,7 +129,7 @@ const mixerList = [
image: 'y6',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, 0.0, 1.333333, 1.0), // REAR
new MotorMixRule(1.0, -1.0, -0.666667, -1.0), // RIGHT
@ -136,7 +147,7 @@ const mixerList = [
image: 'hex_p',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, -0.866025, 0.5, 1.0), // REAR_R
new MotorMixRule(1.0, -0.866025, -0.5, -1.0), // FRONT_R
@ -154,7 +165,7 @@ const mixerList = [
image: 'y4',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, 0.0, 1.0, -1.0), // REAR_TOP CW
new MotorMixRule(1.0, -1.0, -1.0, 0.0), // FRONT_R CCW
@ -170,7 +181,7 @@ const mixerList = [
image: 'hex_x',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, -0.5, 0.866025, 1.0), // REAR_R
new MotorMixRule(1.0, -0.5, -0.866025, 1.0), // FRONT_R
@ -188,7 +199,7 @@ const mixerList = [
image: 'octo_x8',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, -1.0, 1.0, -1.0), // REAR_R
new MotorMixRule(1.0, -1.0, -1.0, 1.0), // FRONT_R
@ -208,7 +219,7 @@ const mixerList = [
image: 'octo_flat_p',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, 0.707107, -0.707107, 1.0), // FRONT_L
new MotorMixRule(1.0, -0.707107, -0.707107, 1.0), // FRONT_R
@ -228,7 +239,7 @@ const mixerList = [
image: 'octo_flat_x',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, 1.0, -0.414178, 1.0), // MIDFRONT_L
new MotorMixRule(1.0, -0.414178, -1.0, 1.0), // FRONT_R
@ -248,7 +259,7 @@ const mixerList = [
image: 'vtail_quad',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, -0.58, 0.58, 1.0), // REAR_R
new MotorMixRule(1.0, -0.46, -0.39, -0.5), // FRONT_R
@ -264,7 +275,7 @@ const mixerList = [
image: 'custom',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, -1.0, 1.0, -1.0), // REAR_R
new MotorMixRule(1.0, -1.0, -1.0, 1.0), // FRONT_R
@ -282,7 +293,7 @@ const mixerList = [
image: 'custom',
enabled: false,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [],
servoMixer: []
}, // 20
@ -293,7 +304,7 @@ const mixerList = [
image: 'custom',
enabled: false,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [],
servoMixer: []
}, // 21
@ -304,7 +315,7 @@ const mixerList = [
image: 'atail_quad',
enabled: true,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [
new MotorMixRule(1.0, 0.0, 1.0, 1.0), // REAR_R
new MotorMixRule(1.0, -1.0, -1.0, 0.0), // FRONT_R
@ -320,7 +331,7 @@ const mixerList = [
image: 'custom',
enabled: false,
legacy: true,
platform: PLATFORM_MULTIROTOR,
platform: PLATFORM.MULTIROTOR,
motorMixer: [],
servoMixer: []
}, // 23
@ -331,7 +342,7 @@ const mixerList = [
image: 'custom',
enabled: false,
legacy: true,
platform: PLATFORM_TRICOPTER,
platform: PLATFORM.TRICOPTER,
motorMixer: [],
servoMixer: []
}, // 25
@ -343,22 +354,22 @@ const mixerList = [
model: 'flying_wing',
image: 'flying_wing',
imageOutputsNumbers: [
{input: INPUT_STABILIZED_ROLL, top: 123, left: 18, colour: "#ff0000"},
{input: INPUT_STABILIZED_ROLL, top: 123, left: 134, colour: "#00e000"},
{input: INPUT_STABILIZED_THROTTLE, top:93, left:71, colour: "#000000"},
{input: INPUT.STABILIZED_ROLL, top: 123, left: 18, colour: "#ff0000"},
{input: INPUT.STABILIZED_ROLL, top: 123, left: 134, colour: "#00e000"},
{input: INPUT.STABILIZED_THROTTLE, top:93, left:71, colour: "#000000"},
],
enabled: true,
legacy: true,
platform: PLATFORM_AIRPLANE,
platform: PLATFORM.AIRPLANE,
motorStopOnLow: true,
motorMixer: [
new MotorMixRule(1.0, 0.0, 0.0, 0.0),
],
servoMixer: [
new ServoMixRule(SERVO_ELEVON_1, INPUT_STABILIZED_ROLL, 50, 0),
new ServoMixRule(SERVO_ELEVON_1, INPUT_STABILIZED_PITCH, 50, 0),
new ServoMixRule(SERVO_ELEVON_2, INPUT_STABILIZED_ROLL, -50, 0),
new ServoMixRule(SERVO_ELEVON_2, INPUT_STABILIZED_PITCH, 50, 0),
new ServoMixRule(SERVO.ELEVON_1, INPUT.STABILIZED_ROLL, 50, 0),
new ServoMixRule(SERVO.ELEVON_1, INPUT.STABILIZED_PITCH, 50, 0),
new ServoMixRule(SERVO.ELEVON_2, INPUT.STABILIZED_ROLL, -50, 0),
new ServoMixRule(SERVO.ELEVON_2, INPUT.STABILIZED_PITCH, 50, 0),
]
}, // 8
{
@ -367,23 +378,23 @@ const mixerList = [
model: 'flying_wing',
image: 'flying_wing',
imageOutputsNumbers: [
{input: INPUT_STABILIZED_ROLL, top: 123, left: 18, colour: "#ff0000"},
{input: INPUT_STABILIZED_ROLL, top: 123, left: 134, colour: "#00e000"},
{input: INPUT_STABILIZED_THROTTLE, top:93, left:71, colour: "#000000"},
{input: INPUT.STABILIZED_ROLL, top: 123, left: 18, colour: "#ff0000"},
{input: INPUT.STABILIZED_ROLL, top: 123, left: 134, colour: "#00e000"},
{input: INPUT.STABILIZED_THROTTLE, top:93, left:71, colour: "#000000"},
],
enabled: true,
legacy: false,
platform: PLATFORM_AIRPLANE,
platform: PLATFORM.AIRPLANE,
motorStopOnLow: true,
motorMixer: [
new MotorMixRule(1.0, 0.0, 0.0, 0.1),
new MotorMixRule(1.0, 0.0, 0.0, -0.1)
],
servoMixer: [
new ServoMixRule(SERVO_ELEVON_1, INPUT_STABILIZED_ROLL, 50, 0),
new ServoMixRule(SERVO_ELEVON_1, INPUT_STABILIZED_PITCH, 50, 0),
new ServoMixRule(SERVO_ELEVON_2, INPUT_STABILIZED_ROLL, -50, 0),
new ServoMixRule(SERVO_ELEVON_2, INPUT_STABILIZED_PITCH, 50, 0),
new ServoMixRule(SERVO.ELEVON_1, INPUT.STABILIZED_ROLL, 50, 0),
new ServoMixRule(SERVO.ELEVON_1, INPUT.STABILIZED_PITCH, 50, 0),
new ServoMixRule(SERVO.ELEVON_2, INPUT.STABILIZED_ROLL, -50, 0),
new ServoMixRule(SERVO.ELEVON_2, INPUT.STABILIZED_PITCH, 50, 0),
]
}, // 27
{
@ -392,27 +403,27 @@ const mixerList = [
model: 'twin_plane',
image: 'airplane',
imageOutputsNumbers: [
{input: INPUT_STABILIZED_PITCH, top: 151, left: 126, colour: "#ff7f00"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT_STABILIZED_YAW, top: 126, left: 52, colour: "#00a6ff"},
{input: INPUT_STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
{input: INPUT.STABILIZED_PITCH, top: 151, left: 126, colour: "#ff7f00"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT.STABILIZED_YAW, top: 126, left: 52, colour: "#00a6ff"},
{input: INPUT.STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
],
enabled: true,
legacy: true,
platform: PLATFORM_AIRPLANE,
platform: PLATFORM.AIRPLANE,
motorStopOnLow: true,
hasFlaps: true,
motorMixer: [
new MotorMixRule(1.0, 0.0, 0.0, 0.0),
],
servoMixer: [
new ServoMixRule(SERVO_ELEVATOR, INPUT_STABILIZED_PITCH, 100, 0),
new ServoMixRule(SERVO_FLAPPERON_1, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO_FLAPPERON_1, INPUT_FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(SERVO_FLAPPERON_2, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO_FLAPPERON_2, INPUT_FEATURE_FLAPS, -100, 0),*/
new ServoMixRule(SERVO_RUDDER, INPUT_STABILIZED_YAW, 100, 0),
new ServoMixRule(SERVO.ELEVATOR, INPUT.STABILIZED_PITCH, 100, 0),
new ServoMixRule(SERVO.FLAPPERON_1, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO.FLAPPERON_1, INPUT.FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(SERVO.FLAPPERON_2, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO.FLAPPERON_2, INPUT.FEATURE_FLAPS, -100, 0),*/
new ServoMixRule(SERVO.RUDDER, INPUT.STABILIZED_YAW, 100, 0),
]
}, // 14
{
@ -421,15 +432,15 @@ const mixerList = [
model: 'twin_plane',
image: 'airplane',
imageOutputsNumbers: [
{input: INPUT_STABILIZED_PITCH, top: 151, left: 126, colour: "#ff7f00"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT_STABILIZED_YAW, top: 126, left: 52, colour: "#00a6ff"},
{input: INPUT_STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
{input: INPUT.STABILIZED_PITCH, top: 151, left: 126, colour: "#ff7f00"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT.STABILIZED_YAW, top: 126, left: 52, colour: "#00a6ff"},
{input: INPUT.STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
],
enabled: true,
legacy: false,
platform: PLATFORM_AIRPLANE,
platform: PLATFORM.AIRPLANE,
motorStopOnLow: true,
hasFlaps: true,
motorMixer: [
@ -437,12 +448,12 @@ const mixerList = [
new MotorMixRule(1.0, 0.0, 0.0, -0.3)
],
servoMixer: [
new ServoMixRule(SERVO_ELEVATOR, INPUT_STABILIZED_PITCH, 100, 0),
new ServoMixRule(SERVO_FLAPPERON_1, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO_FLAPPERON_1, INPUT_FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(SERVO_FLAPPERON_2, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO_FLAPPERON_2, INPUT_FEATURE_FLAPS, -100, 0),*/
new ServoMixRule(SERVO_RUDDER, INPUT_STABILIZED_YAW, 100, 0),
new ServoMixRule(SERVO.ELEVATOR, INPUT.STABILIZED_PITCH, 100, 0),
new ServoMixRule(SERVO.FLAPPERON_1, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO.FLAPPERON_1, INPUT.FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(SERVO.FLAPPERON_2, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO.FLAPPERON_2, INPUT.FEATURE_FLAPS, -100, 0),*/
new ServoMixRule(SERVO.RUDDER, INPUT.STABILIZED_YAW, 100, 0),
]
}, // 26
{
@ -451,29 +462,29 @@ const mixerList = [
model: 'vtail_plane',
image: 'airplane_vtail',
imageOutputsNumbers: [
{input: INPUT_STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT_STABILIZED_PITCH, top: 154, left: 20, colour: "#ff7f00"},
{input: INPUT_STABILIZED_PITCH, top: 154, left: 132, colour: "#00a6ff"},
{input: INPUT_STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT.STABILIZED_PITCH, top: 154, left: 20, colour: "#ff7f00"},
{input: INPUT.STABILIZED_PITCH, top: 154, left: 132, colour: "#00a6ff"},
{input: INPUT.STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
],
enabled: true,
legacy: false,
platform: PLATFORM_AIRPLANE,
platform: PLATFORM.AIRPLANE,
motorStopOnLow: true,
hasFlaps: true,
motorMixer: [
new MotorMixRule(1.0, 0.0, 0.0, 0.0),
],
servoMixer: [
new ServoMixRule(1, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(1, INPUT_FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(2, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(2, INPUT_FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(3, INPUT_STABILIZED_PITCH, 50, 0),
new ServoMixRule(3, INPUT_STABILIZED_YAW, -50, 0),
new ServoMixRule(4, INPUT_STABILIZED_PITCH, -50, 0),
new ServoMixRule(4, INPUT_STABILIZED_YAW, -50, 0)
new ServoMixRule(1, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(1, INPUT.FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(2, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(2, INPUT.FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(3, INPUT.STABILIZED_PITCH, 50, 0),
new ServoMixRule(3, INPUT.STABILIZED_YAW, -50, 0),
new ServoMixRule(4, INPUT.STABILIZED_PITCH, -50, 0),
new ServoMixRule(4, INPUT.STABILIZED_YAW, -50, 0)
]
}, // 28
{
@ -482,15 +493,15 @@ const mixerList = [
model: 'vtail_plane',
image: 'airplane_vtail',
imageOutputsNumbers: [
{input: INPUT_STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT_STABILIZED_PITCH, top: 154, left: 20, colour: "#ff7f00"},
{input: INPUT_STABILIZED_PITCH, top: 154, left: 132, colour: "#00a6ff"},
{input: INPUT_STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT.STABILIZED_PITCH, top: 154, left: 20, colour: "#ff7f00"},
{input: INPUT.STABILIZED_PITCH, top: 154, left: 132, colour: "#00a6ff"},
{input: INPUT.STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
],
enabled: true,
legacy: false,
platform: PLATFORM_AIRPLANE,
platform: PLATFORM.AIRPLANE,
motorStopOnLow: true,
hasFlaps: true,
motorMixer: [
@ -498,14 +509,14 @@ const mixerList = [
new MotorMixRule(1.0, 0.0, 0.0, -0.3)
],
servoMixer: [
new ServoMixRule(1, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(1, INPUT_FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(2, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(2, INPUT_FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(3, INPUT_STABILIZED_PITCH, 50, 0),
new ServoMixRule(3, INPUT_STABILIZED_YAW, -50, 0),
new ServoMixRule(4, INPUT_STABILIZED_PITCH, -50, 0),
new ServoMixRule(4, INPUT_STABILIZED_YAW, -50, 0)
new ServoMixRule(1, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(1, INPUT.FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(2, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(2, INPUT.FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(3, INPUT.STABILIZED_PITCH, 50, 0),
new ServoMixRule(3, INPUT.STABILIZED_YAW, -50, 0),
new ServoMixRule(4, INPUT.STABILIZED_PITCH, -50, 0),
new ServoMixRule(4, INPUT.STABILIZED_YAW, -50, 0)
]
}, // 34
{
@ -514,24 +525,24 @@ const mixerList = [
model: 'vtail_single_servo_plane',
image: 'airplane_vtail_single',
imageOutputsNumbers: [
{input: INPUT_STABILIZED_ROLL, top: 96, left: 18, colour: "#ff7f00"},
{input: INPUT_STABILIZED_PITCH, top: 154, left: 20, colour: "#ff0000"},
{input: INPUT_STABILIZED_PITCH, top: 154, left: 132, colour: "#00e000"},
{input: INPUT_STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 18, colour: "#ff7f00"},
{input: INPUT.STABILIZED_PITCH, top: 154, left: 20, colour: "#ff0000"},
{input: INPUT.STABILIZED_PITCH, top: 154, left: 132, colour: "#00e000"},
{input: INPUT.STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
],
enabled: true,
legacy: false,
platform: PLATFORM_AIRPLANE,
platform: PLATFORM.AIRPLANE,
motorStopOnLow: true,
motorMixer: [
new MotorMixRule(1.0, 0.0, 0.0, 0.0),
],
servoMixer: [
new ServoMixRule(1, INPUT_STABILIZED_ROLL, 100, 0),
new ServoMixRule(2, INPUT_STABILIZED_PITCH, 50, 0),
new ServoMixRule(2, INPUT_STABILIZED_YAW, -50, 0),
new ServoMixRule(3, INPUT_STABILIZED_PITCH, -50, 0),
new ServoMixRule(3, INPUT_STABILIZED_YAW, -50, 0),
new ServoMixRule(1, INPUT.STABILIZED_ROLL, 100, 0),
new ServoMixRule(2, INPUT.STABILIZED_PITCH, 50, 0),
new ServoMixRule(2, INPUT.STABILIZED_YAW, -50, 0),
new ServoMixRule(3, INPUT.STABILIZED_PITCH, -50, 0),
new ServoMixRule(3, INPUT.STABILIZED_YAW, -50, 0),
]
}, //29
{
@ -540,25 +551,25 @@ const mixerList = [
model: 'rudderless_plane',
image: 'airplane_norudder',
imageOutputsNumbers: [
{input: INPUT_STABILIZED_PITCH, top: 151, left: 126, colour: "#ff7f00"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT_STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
{input: INPUT.STABILIZED_PITCH, top: 151, left: 126, colour: "#ff7f00"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT.STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
],
enabled: true,
legacy: false,
platform: PLATFORM_AIRPLANE,
platform: PLATFORM.AIRPLANE,
hasFlaps: true,
motorStopOnLow: true,
motorMixer: [
new MotorMixRule(1.0, 0.0, 0.0, 0.0),
],
servoMixer: [
new ServoMixRule(SERVO_ELEVATOR, INPUT_STABILIZED_PITCH, 100, 0),
new ServoMixRule(SERVO_FLAPPERON_1, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO_FLAPPERON_1, INPUT_FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(SERVO_FLAPPERON_2, INPUT_STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO_FLAPPERON_2, INPUT_FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(SERVO.ELEVATOR, INPUT.STABILIZED_PITCH, 100, 0),
new ServoMixRule(SERVO.FLAPPERON_1, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO.FLAPPERON_1, INPUT.FEATURE_FLAPS, 100, 0),*/
new ServoMixRule(SERVO.FLAPPERON_2, INPUT.STABILIZED_ROLL, 100, 0),
/*new ServoMixRule(SERVO.FLAPPERON_2, INPUT.FEATURE_FLAPS, 100, 0),*/
]
}, // 30
{
@ -567,15 +578,15 @@ const mixerList = [
model: 'twin_plane',
image: 'airplane',
imageOutputsNumbers: [
{input: INPUT_STABILIZED_PITCH, top: 151, left: 126, colour: "#ff7f00"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT_STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT_STABILIZED_YAW, top: 126, left: 52, colour: "#00a6ff"},
{input: INPUT_STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
{input: INPUT.STABILIZED_PITCH, top: 151, left: 126, colour: "#ff7f00"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 18, colour: "#ff0000"},
{input: INPUT.STABILIZED_ROLL, top: 96, left: 134, colour: "#00e000"},
{input: INPUT.STABILIZED_YAW, top: 126, left: 52, colour: "#00a6ff"},
{input: INPUT.STABILIZED_THROTTLE, top:5, left:71, colour: "#000000"},
],
enabled: false,
legacy: true,
platform: PLATFORM_AIRPLANE,
platform: PLATFORM.AIRPLANE,
motorStopOnLow: true,
motorMixer: [],
servoMixer: []
@ -589,7 +600,7 @@ const mixerList = [
image: 'custom',
enabled: false,
legacy: true,
platform: PLATFORM_HELICOPTER,
platform: PLATFORM.HELICOPTER,
motorMixer: [],
servoMixer: []
}, // 15
@ -600,7 +611,7 @@ const mixerList = [
image: 'custom',
enabled: false,
legacy: true,
platform: PLATFORM_HELICOPTER,
platform: PLATFORM.HELICOPTER,
motorMixer: [],
servoMixer: []
}, // 16
@ -613,12 +624,12 @@ const mixerList = [
image: 'custom',
enabled: true,
legacy: false,
platform: PLATFORM_ROVER,
platform: PLATFORM.ROVER,
motorMixer: [
new MotorMixRule(1.0, 0.0, 0.0, 0.0),
],
servoMixer: [
new ServoMixRule(3, INPUT_STABILIZED_YAW, 100, 0),
new ServoMixRule(3, INPUT.STABILIZED_YAW, 100, 0),
]
},
{
@ -628,12 +639,12 @@ const mixerList = [
image: 'custom',
enabled: true,
legacy: false,
platform: PLATFORM_BOAT,
platform: PLATFORM.BOAT,
motorMixer: [
new MotorMixRule(1.0, 0.0, 0.0, 0.0),
],
servoMixer: [
new ServoMixRule(3, INPUT_STABILIZED_YAW, 100, 0),
new ServoMixRule(3, INPUT.STABILIZED_YAW, 100, 0),
]
},
// ** Misc **
@ -644,12 +655,12 @@ const mixerList = [
image: 'custom',
enabled: true,
legacy: false,
platform: PLATFORM_OTHER,
platform: PLATFORM.OTHER,
motorMixer: [
new MotorMixRule(1.0, 0.0, 0.0, 0.0),
],
servoMixer: [
new ServoMixRule(3, INPUT_STABILIZED_YAW, 100, 0),
new ServoMixRule(3, INPUT.STABILIZED_YAW, 100, 0),
]
},
{
@ -659,7 +670,7 @@ const mixerList = [
image: 'custom',
enabled: false,
legacy: true,
platform: PLATFORM_OTHER,
platform: PLATFORM.OTHER,
motorMixer: [],
servoMixer: []
}, // 5
@ -670,7 +681,7 @@ const mixerList = [
image: 'custom',
enabled: false,
legacy: true,
platform: PLATFORM_OTHER,
platform: PLATFORM.OTHER,
motorMixer: [],
servoMixer: []
}, // 19
@ -721,9 +732,7 @@ const platformList = [
}
];
var helper = helper || {};
helper.mixer = (function (mixerList) {
var mixer = (function (mixerList) {
let publicScope = {},
privateScope = {};
@ -778,13 +787,13 @@ helper.mixer = (function (mixerList) {
return retVal;
};
publicScope.loadServoRules = function (mixer) {
SERVO_RULES.flush();
publicScope.loadServoRules = function (FC, mixer) {
FC.SERVO_RULES.flush();
for (const i in mixer.servoMixer) {
if (mixer.servoMixer.hasOwnProperty(i)) {
const r = mixer.servoMixer[i];
SERVO_RULES.put(
FC.SERVO_RULES.put(
new ServoMixRule(
r.getTarget(),
r.getInput(),
@ -796,13 +805,13 @@ helper.mixer = (function (mixerList) {
}
}
publicScope.loadMotorRules = function (mixer) {
MOTOR_RULES.flush();
publicScope.loadMotorRules = function (FC, mixer) {
FC.MOTOR_RULES.flush();
for (const i in mixer.motorMixer) {
if (mixer.motorMixer.hasOwnProperty(i)) {
const r = mixer.motorMixer[i];
MOTOR_RULES.put(
FC.MOTOR_RULES.put(
new MotorMixRule(
r.getThrottle(),
r.getRoll(),
@ -833,7 +842,7 @@ helper.mixer = (function (mixerList) {
return publicScope;
})(mixerList);
helper.platform = (function (platforms) {
var platform = (function (platforms) {
let publicScope = {},
privateScope = {};
@ -864,3 +873,5 @@ helper.platform = (function (platforms) {
return publicScope;
})(platformList);
module.exports = { mixer, platform, PLATFORM, SERVO, INPUT, STABILIZED };

View file

@ -1,6 +1,7 @@
/*global $,constrain*/
'use strict';
const { constrain } = require('./helpers')
var MotorMixRule = function (throttle, roll, pitch, yaw) {
var self = {};
@ -66,3 +67,5 @@ var MotorMixRule = function (throttle, roll, pitch, yaw) {
return self;
};
module.exports = MotorMixRule;

View file

@ -1,6 +1,7 @@
/*global $, MotorMixRule*/
'use strict';
const MotorMixRule = require('./motorMixRule');
var MotorMixerRuleCollection = function () {
let self = {},
@ -72,3 +73,5 @@ var MotorMixerRuleCollection = function () {
return self;
};
module.exports = MotorMixerRuleCollection;

View file

@ -1,5 +1,8 @@
'use strict';
const MSPCodes = require('./msp/MSPCodes')
const mspQueue = require('./serial_queue');
/**
*
* @constructor
@ -20,10 +23,13 @@ var MspMessageClass = function () {
return publicScope;
};
/**
* @typedef {{state: number, message_direction: number, code: number, message_length_expected: number, message_length_received: number, message_buffer: null, message_buffer_uint8_view: null, message_checksum: number, callbacks: Array, packet_error: number, unsupported: number, ledDirectionLetters: [*], ledFunctionLetters: [*], ledBaseFunctionLetters: [*], ledOverlayLetters: [*], last_received_timestamp: null, analog_last_received_timestamp: number, read: MSP.read, send_message: MSP.send_message, promise: MSP.promise, callbacks_cleanup: MSP.callbacks_cleanup, disconnect_cleanup: MSP.disconnect_cleanup}} MSP
*/
var MSP = {
SDCARD_STATE_NOT_PRESENT: 0,
SDCARD_STATE_FATAL: 1,
SDCARD_STATE_CARD_INIT: 2,
SDCARD_STATE_FS_INIT: 3,
SDCARD_STATE_READY: 4,
symbols: {
BEGIN: '$'.charCodeAt(0),
PROTO_V1: 'M'.charCodeAt(0),
@ -80,6 +86,17 @@ var MSP = {
lastFrameReceivedMs: 0,
processData: null,
init() {
mspQueue.setPutCallback(this.putCallback);
mspQueue.setremoveCallback(this.removeCallback);
},
setProcessData(cb) {
this.processData = cb;
},
read: function (readInfo) {
var data = new Uint8Array(readInfo.data);
@ -222,22 +239,22 @@ var MSP = {
/*
* Free port
*/
helper.mspQueue.freeHardLock();
mspQueue.freeHardLock();
console.log('Unknown state detected: ' + this.state);
}
}
this.last_received_timestamp = Date.now();
},
_initialize_read_buffer: function() {
_initialize_read_buffer() {
this.message_buffer = new ArrayBuffer(this.message_length_expected);
this.message_buffer_uint8_view = new Uint8Array(this.message_buffer);
},
_dispatch_message: function(expected_checksum) {
_dispatch_message(expected_checksum) {
if (this.message_checksum == expected_checksum) {
// message received, process
mspHelper.processData(this);
this.processData(this);
this.lastFrameReceivedMs = Date.now();
} else {
console.log('code: ' + this.code + ' - crc failed');
@ -248,7 +265,7 @@ var MSP = {
/*
* Free port
*/
helper.mspQueue.freeHardLock();
mspQueue.freeHardLock();
// Reset variables
this.message_length_received = 0;
@ -259,24 +276,24 @@ var MSP = {
*
* @param {MSP} mspData
*/
putCallback: function (mspData) {
putCallback(mspData) {
MSP.callbacks.push(mspData);
},
/**
* @param {number} code
*/
removeCallback: function (code) {
removeCallback(code) {
for (var i in this.callbacks) {
if (this.callbacks.hasOwnProperty(i) && this.callbacks[i].code == code) {
if (MSP.callbacks.hasOwnProperty(i) && this.callbacks[i].code == code) {
clearTimeout(this.callbacks[i].timer);
this.callbacks.splice(i, 1);
MSP.callbacks.splice(i, 1);
}
}
},
send_message: function (code, data, callback_sent, callback_msp, protocolVersion) {
send_message(code, data, callback_sent, callback_msp, protocolVersion) {
var payloadLength = data && data.length ? data.length : 0;
var length;
var buffer;
@ -301,7 +318,7 @@ var MSP = {
view[4] = code;
checksum = view[3] ^ view[4];
for (ii = 0; ii < payloadLength; ii++) {
for (let ii = 0; ii < payloadLength; ii++) {
view[ii + 5] = data[ii];
checksum ^= data[ii];
}
@ -319,11 +336,11 @@ var MSP = {
view[5] = (code & 0xFF00) >> 8; // code upper byte
view[6] = payloadLength & 0xFF; // payloadLength lower byte
view[7] = (payloadLength & 0xFF00) >> 8; // payloadLength upper byte
for (ii = 0; ii < payloadLength; ii++) {
for (let ii = 0; ii < payloadLength; ii++) {
view[8+ii] = data[ii];
}
checksum = 0;
for (ii = 3; ii < length-1; ii++) {
for (let ii = 3; ii < length-1; ii++) {
checksum = this._crc8_dvb_s2(checksum, view[ii]);
}
view[length-1] = checksum;
@ -346,11 +363,11 @@ var MSP = {
message.retryCounter = 10;
}
helper.mspQueue.put(message);
mspQueue.put(message);
return true;
},
_crc8_dvb_s2: function(crc, ch) {
_crc8_dvb_s2(crc, ch) {
crc ^= ch;
for (var ii = 0; ii < 8; ++ii) {
if (crc & 0x80) {
@ -361,7 +378,7 @@ var MSP = {
}
return crc;
},
promise: function(code, data, protocolVersion) {
promise(code, data, protocolVersion) {
var self = this;
return new Promise(function(resolve) {
self.send_message(code, data, false, function(data) {
@ -369,14 +386,14 @@ var MSP = {
}, protocolVersion);
});
},
callbacks_cleanup: function () {
callbacks_cleanup() {
for (var i = 0; i < this.callbacks.length; i++) {
clearInterval(this.callbacks[i].timer);
}
this.callbacks = [];
},
disconnect_cleanup: function () {
disconnect_cleanup() {
this.state = 0; // reset packet state for "clean" initial entry (this is only required if user hot-disconnects)
this.packet_error = 0; // reset CRC packet error counter for next session
@ -390,8 +407,4 @@ var MSP = {
}
};
MSP.SDCARD_STATE_NOT_PRESENT = 0;
MSP.SDCARD_STATE_FATAL = 1;
MSP.SDCARD_STATE_CARD_INIT = 2;
MSP.SDCARD_STATE_FS_INIT = 3;
MSP.SDCARD_STATE_READY = 4;
module.exports = MSP;

View file

@ -242,3 +242,5 @@ var MSPCodes = {
MSP2_INAV_SET_SERVO_CONFIG: 0x2201,
};
module.exports = MSPCodes;

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
/*global $*/
'use strict';
var MSPChainerClass = function () {
@ -33,3 +33,5 @@ var MSPChainerClass = function () {
return self;
};
module.exports = MSPChainerClass;

View file

@ -1,8 +1,9 @@
'use strict';
var helper = helper || {};
const mspQueue = require('./serial_queue');
const interval = require('./intervals');
helper.mspBalancedInterval = (function (mspQueue, intervalHandler) {
var mspBalancedInterval = (function (mspQueue, intervalHandler) {
var publicScope = {},
privateScope = {};
@ -59,7 +60,7 @@ helper.mspBalancedInterval = (function (mspQueue, intervalHandler) {
};
/**
* Real interval cleaning happens win helper.interval.killAll method
* Real interval cleaning happens win interval.killAll method
* both methods have to be executed
*/
publicScope.flush = function () {
@ -69,4 +70,6 @@ helper.mspBalancedInterval = (function (mspQueue, intervalHandler) {
setInterval(publicScope.balancer, Math.round(1000 / privateScope.balancingFrequency));
return publicScope;
})(helper.mspQueue, helper.interval);
})(mspQueue, interval);
module.exports = mspBalancedInterval;

36
js/mwnp.js Normal file
View file

@ -0,0 +1,36 @@
'use strict'
// MultiWii NAV Protocol
const MWNP = {};
MWNP.WPTYPE = {
WAYPOINT: 1,
POSHOLD_UNLIM: 2,
POSHOLD_TIME: 3,
RTH: 4,
SET_POI: 5,
JUMP: 6,
SET_HEAD: 7,
LAND: 8
};
MWNP.P3 = {
ALT_TYPE: 0, // Altitude (alt) : Relative (to home altitude) (0) or Absolute (AMSL) (1).
USER_ACTION_1: 1, // WP Action 1
USER_ACTION_2: 2, // WP Action 2
USER_ACTION_3: 3, // WP Action 3
USER_ACTION_4: 4, // WP Action 4
}
MWNP.WPTYPE.REV = swap(MWNP.WPTYPE);
// Reverse WayPoint type dictionary
function swap(dict) {
let rev_dict = {};
for (let key in dict) {
rev_dict[dict[key]] = key;
}
return rev_dict;
}
module.exports = MWNP;

View file

@ -1,11 +1,22 @@
/*global bit_check*/
'use strict';
let OutputMappingCollection = function () {
const BitHelper = require("./bitHelper");
var OutputMappingCollection = function () {
let self = {},
data = [],
timerOverrides = {};
const colorTable = [
"#8ecae6",
"#2a9d8f",
"#e9c46a",
"#f4a261",
"#e76f51",
"#ef476f",
"#ffc300"
];
const TIM_USE_ANY = 0;
const TIM_USE_PPM = 0;
const TIM_USE_PWM = 1;
@ -37,13 +48,13 @@ let OutputMappingCollection = function () {
}
self.getTimerColor = function (timer) {
let timerIndex = OUTPUT_MAPPING.getUsedTimerIds().indexOf(String(timer));
let timerIndex = self.getUsedTimerIds().indexOf(String(timer));
return GUI.colorTable[timerIndex % GUI.colorTable.length];
return colorTable[timerIndex % colorTable.length];
}
self.getOutputTimerColor = function (output) {
let timerId = OUTPUT_MAPPING.getTimerId(output);
let timerId = self.getTimerId(output);
return self.getTimerColor(timerId);
}
@ -68,10 +79,10 @@ let OutputMappingCollection = function () {
for (let i = 0; i < data.length; i++) {
timerMap[i] = null;
if (servosToGo > 0 && bit_check(data[i]['usageFlags'], TIM_USE_SERVO)) {
if (servosToGo > 0 && BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_SERVO)) {
servosToGo--;
timerMap[i] = OUTPUT_TYPE_SERVO;
} else if (motorsToGo > 0 && bit_check(data[i]['usageFlags'], TIM_USE_MOTOR)) {
} else if (motorsToGo > 0 && BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_MOTOR)) {
motorsToGo--;
timerMap[i] = OUTPUT_TYPE_MOTOR;
}
@ -118,8 +129,8 @@ let OutputMappingCollection = function () {
for (let i = 0; i < data.length; i++) {
if (
bit_check(data[i]['usageFlags'], TIM_USE_MOTOR) ||
bit_check(data[i]['usageFlags'], TIM_USE_SERVO)
BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_MOTOR) ||
BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_SERVO)
) {
retVal++;
};
@ -131,8 +142,8 @@ let OutputMappingCollection = function () {
function getFirstOutputOffset() {
for (let i = 0; i < data.length; i++) {
if (
bit_check(data[i]['usageFlags'], TIM_USE_MOTOR) ||
bit_check(data[i]['usageFlags'], TIM_USE_SERVO)
BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_MOTOR) ||
BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_SERVO)
) {
return i;
}
@ -151,7 +162,7 @@ let OutputMappingCollection = function () {
let lastFound = 0;
for (let i = offset; i < data.length; i++) {
if (bit_check(data[i]['usageFlags'], bit)) {
if (BitHelper.bit_check(data[i]['usageFlags'], bit)) {
if (lastFound == servoIndex) {
return i - offset + 1;
} else {
@ -177,3 +188,5 @@ let OutputMappingCollection = function () {
return self;
}
module.exports = OutputMappingCollection;

View file

@ -1,12 +1,19 @@
'use strict';
var helper = helper || {};
const { GUI } = require('./gui');
const FC = require('./fc');
const CONFIGURATOR = require('./data_storage');
const MSP = require('./msp');
const MSPCodes = require('./msp/MSPCodes');
const mspQueue = require('./serial_queue');
helper.periodicStatusUpdater = (function () {
var periodicStatusUpdater = (function () {
var publicScope = {},
privateScope = {};
var stoppped = false;
/**
*
* @param {number=} baudSpeed
@ -35,51 +42,51 @@ helper.periodicStatusUpdater = (function () {
if (FC.isModeEnabled('ARM'))
$(".armedicon").css({
'background-image': 'url("../images/icons/cf_icon_armed_active.svg")'
'background-image': 'url("./images/icons/cf_icon_armed_active.svg")'
});
else
$(".armedicon").css({
'background-image': 'url("../images/icons/cf_icon_armed_grey.svg")'
'background-image': 'url("./images/icons/cf_icon_armed_grey.svg")'
});
if (FC.isModeEnabled('FAILSAFE'))
$(".failsafeicon").css({
'background-image': 'url("../images/icons/cf_icon_failsafe_active.svg")'
'background-image': 'url("./images/icons/cf_icon_failsafe_active.svg")'
});
else
$(".failsafeicon").css({
'background-image': 'url("../images/icons/cf_icon_failsafe_grey.svg")'
'background-image': 'url("./images/icons/cf_icon_failsafe_grey.svg")'
});
if (ANALOG != undefined) {
if (FC.ANALOG != undefined) {
var nbCells;
nbCells = ANALOG.cell_count;
var min = MISC.vbatmincellvoltage * nbCells;
var max = MISC.vbatmaxcellvoltage * nbCells;
var warn = MISC.vbatwarningcellvoltage * nbCells;
nbCells = FC.ANALOG.cell_count;
var min = FC.MISC.vbatmincellvoltage * nbCells;
var max = FC.MISC.vbatmaxcellvoltage * nbCells;
var warn = FC.MISC.vbatwarningcellvoltage * nbCells;
$(".battery-status").css({
width: ANALOG.battery_percentage + "%",
width: FC.ANALOG.battery_percentage + "%",
display: 'inline-block'
});
if (active) {
$(".linkicon").css({
'background-image': 'url("../images/icons/cf_icon_link_active.svg")'
'background-image': 'url("./images/icons/cf_icon_link_active.svg")'
});
} else {
$(".linkicon").css({
'background-image': 'url("../images/icons/cf_icon_link_grey.svg")'
'background-image': 'url("./images/icons/cf_icon_link_grey.svg")'
});
}
if (((ANALOG.use_capacity_thresholds && ANALOG.battery_remaining_capacity <= MISC.battery_capacity_warning - MISC.battery_capacity_critical) || (!ANALOG.use_capacity_thresholds && ANALOG.voltage < warn)) || ANALOG.voltage < min) {
if (((FC.ANALOG.use_capacity_thresholds && FC.ANALOG.battery_remaining_capacity <= FC.MISC.battery_capacity_warning - FC.MISC.battery_capacity_critical) || (!FC.ANALOG.use_capacity_thresholds && FC.ANALOG.voltage < warn)) || FC.ANALOG.voltage < min) {
$(".battery-status").css('background-color', '#D42133');
} else {
$(".battery-status").css('background-color', '#59AA29');
}
$(".battery-legend").text(ANALOG.voltage + " V");
$(".battery-legend").text(FC.ANALOG.voltage + " V");
}
$('#quad-status_wrapper').show();
@ -95,20 +102,28 @@ helper.periodicStatusUpdater = (function () {
display: 'inline-block'
});
if (GUI.active_tab != 'cli') {
if (!stoppped && GUI.active_tab != 'cli') {
if (helper.mspQueue.shouldDropStatus()) {
if (mspQueue.shouldDropStatus()) {
return;
}
MSP.send_message(MSPCodes.MSP_SENSOR_STATUS, false, false);
MSP.send_message(MSPCodes.MSPV2_INAV_STATUS, false, false);
MSP.send_message(MSPCodes.MSP_ACTIVEBOXES, false, false);
MSP.send_message(MSPCodes.MSPV2_INAV_ANALOG, false, false);
privateScope.updateView();
}
};
publicScope.stop = function() {
stoppped = true;
}
return publicScope;
})();
module.exports = periodicStatusUpdater;

View file

@ -1,9 +1,11 @@
'use strict';
const FC = require('./fc');
// return true if user has choose a special peripheral
function isPeripheralSelected(peripheralName) {
for (var portIndex = 0; portIndex < SERIAL_CONFIG.ports.length; portIndex++) {
var serialPort = SERIAL_CONFIG.ports[portIndex];
for (var portIndex = 0; portIndex < FC.SERIAL_CONFIG.ports.length; portIndex++) {
var serialPort = FC.SERIAL_CONFIG.ports[portIndex];
if (serialPort.functions.indexOf(peripheralName) >= 0) {
return true;
}
@ -35,3 +37,5 @@ function adjustBoxNameIfPeripheralWithModeID(modeId, defaultName) {
return defaultName;
}
module.exports = adjustBoxNameIfPeripheralWithModeID ;

View file

@ -1,8 +1,7 @@
'use strict';
var classes = classes || {};
classes.PidController = function () {
var PidController = function () {
var self = {},
privateScope = {};
@ -127,3 +126,5 @@ classes.PidController = function () {
return self;
};
module.exports = PidController;

View file

@ -1,11 +1,16 @@
'use strict';
var usbDevices = {
filters: [
{'vendorId': 1155, 'productId': 57105},
{'vendorId': 11836, 'productId': 57105}
]
};
const Store = require('electron-store');
const store = new Store();
const { GUI } = require('./../js/gui');
const ConnectionSerial = require('./connection/connectionSerial');
var usbDevices = [
{ 'vendorId': 1155, 'productId': 57105},
{ 'vendorId': 11836, 'productId': 57105}
];
var PortHandler = new function () {
this.initial_ports = false;
@ -49,7 +54,7 @@ PortHandler.check = function () {
if (GUI.connected_to) {
for (var i = 0; i < removed_ports.length; i++) {
if (removed_ports[i] == GUI.connected_to) {
$('div#port-picker a.connect').click();
$('div#port-picker a.connect').trigger( "click" );
}
}
}
@ -75,35 +80,31 @@ PortHandler.check = function () {
// auto-select last used port (only during initialization)
if (!self.initial_ports) {
chrome.storage.local.get('last_used_port', function (result) {
var last_used_port = store.get('last_used_port', false);
// if last_used_port was set, we try to select it
if (result.last_used_port) {
if (result.last_used_port == "ble" || result.last_used_port == "tcp" || result.last_used_port == "udp" || result.last_used_port == "sitl" || result.last_used_port == "sitl-demo") {
$('#port').val(result.last_used_port);
if (last_used_port) {
if (last_used_port == "ble" || last_used_port == "tcp" || last_used_port == "udp" || last_used_port == "sitl" || last_used_port == "sitl-demo") {
$('#port').val(last_used_port);
} else {
current_ports.forEach(function(port) {
if (port == result.last_used_port) {
console.log('Selecting last used port: ' + result.last_used_port);
$('#port').val(result.last_used_port);
if (port == last_used_port) {
console.log('Selecting last used port: ' + last_used_port);
$('#port').val(last_used_port);
}
});
}
} else {
console.log('Last used port wasn\'t saved "yet", auto-select disabled.');
}
});
chrome.storage.local.get('last_used_bps', function (result) {
if (result['last_used_bps']) {
$('#baud').val(result['last_used_bps']);
var last_used_bps = store.get('last_used_bps', false);
if (last_used_bps) {
$('#baud').val(last_used_bps);
}
});
chrome.storage.local.get('wireless_mode_enabled', function (result) {
if (result['wireless_mode_enabled']) {
$('#wireless-mode').prop('checked', true).change();
if (store.get('wireless_mode_enabled', false)) {
$('#wireless-mode').prop('checked', true).trigger('change');
}
});
}
@ -164,23 +165,34 @@ PortHandler.check = function () {
};
PortHandler.check_usb_devices = function (callback) {
chrome.usb.getDevices(usbDevices, function (result) {
if (result.length) {
self.dfu_available = false;
navigator.usb.getDevices().then(devices => {
devices.forEach(device => {
usbDevices.forEach(usbDev => {
if (device.vendorId == usbDev.vendorId && device.productId == usbDev.productId) {
self.dfu_available = true;
return;
}
});
});
if (self.dfu_available) {
if (!$("div#port-picker #port [value='DFU']").length) {
$('div#port-picker #port').append($('<option/>', {value: "DFU", text: "DFU", data: {isDFU: true}}));
$('div#port-picker #port').val('DFU');
}
self.dfu_available = true;
} else {
if ($("div#port-picker #port [value='DFU']").length) {
$("div#port-picker #port [value='DFU']").remove();
}
self.dfu_available = false;
}
if (callback) callback(self.dfu_available);
if (callback)
callback(self.dfu_available);
});
};
}
PortHandler.update_port_select = function (ports) {
$('div#port-picker #port').html(''); // drop previous one
@ -284,3 +296,5 @@ PortHandler.flush_callbacks = function () {
return killed;
};
module.exports = { usbDevices, PortHandler };

View file

@ -1,6 +1,8 @@
/*global $,FC*/
'use strict';
const FC = require('./fc');
const { GUI } = require('./gui');
let ProgrammingPid = function (enabled, setpointType, setpointValue, measurementType, measurementValue, gainP, gainI, gainD, gainFF) {
let self = {};
let $row;
@ -140,16 +142,16 @@ let ProgrammingPid = function (enabled, setpointType, setpointValue, measurement
self.renderOperand(1);
$row.find(".pid_cell__p").html('<input type="number" class="pid_cell__p-gain" step="1" min="0" max="32767" value="0">');
$row.find(".pid_cell__p-gain").val(self.getGainP()).change(self.onGainPChange);
$row.find(".pid_cell__p-gain").val(self.getGainP()).on('change', self.onGainPChange);
$row.find(".pid_cell__i").html('<input type="number" class="pid_cell__i-gain" step="1" min="0" max="32767" value="0">');
$row.find(".pid_cell__i-gain").val(self.getGainI()).change(self.onGainIChange);
$row.find(".pid_cell__i-gain").val(self.getGainI()).on('change', self.onGainIChange);
$row.find(".pid_cell__d").html('<input type="number" class="pid_cell__d-gain" step="1" min="0" max="32767" value="0">');
$row.find(".pid_cell__d-gain").val(self.getGainD()).change(self.onGainDChange);
$row.find(".pid_cell__d-gain").val(self.getGainD()).on('change', self.onGainDChange);
$row.find(".pid_cell__ff").html('<input type="number" class="pid_cell__ff-gain" step="1" min="0" max="32767" value="0">');
$row.find(".pid_cell__ff-gain").val(self.getGainFF()).change(self.onGainFFChange);
$row.find(".pid_cell__ff-gain").val(self.getGainFF()).on('change', self.onGainFFChange);
}
@ -208,7 +210,7 @@ let ProgrammingPid = function (enabled, setpointType, setpointValue, measurement
/*
* Bind events
*/
$t.change(self.onOperatorTypeChange);
$t.on('change', self.onOperatorTypeChange);
}
@ -222,3 +224,5 @@ let ProgrammingPid = function (enabled, setpointType, setpointValue, measurement
return self;
};
module.exports = ProgrammingPid;

View file

@ -1,6 +1,6 @@
'use strict';
let ProgrammingPidCollection = function () {
var ProgrammingPidCollection = function () {
let self = {},
data = [],
@ -40,8 +40,6 @@ let ProgrammingPidCollection = function () {
self.get()[k].render(k, $table);
}
}
GUI.switchery();
};
self.update = function(statuses) {
@ -56,3 +54,5 @@ let ProgrammingPidCollection = function () {
return self;
};
module.exports = ProgrammingPidCollection;

View file

@ -1,6 +1,6 @@
'use strict';
let ProgrammingPidStatus = function () {
var ProgrammingPidStatus = function () {
let self = {},
data = [];
@ -31,3 +31,5 @@ let ProgrammingPidStatus = function () {
return self;
};
module.exports = ProgrammingPidStatus;

View file

@ -7,6 +7,14 @@
*/
'use strict';
const CONFIGURATOR = require('./../data_storage');
const { GUI } = require('./../gui');
const interval = require('./../intervals');
const { usbDevices, PortHandler } = require('./../port_handler');
const ConnectionSerial = require('./../connection/connectionSerial');
const STM32DFU = require('./stm32usbdfu');
const i18n = require('./../localization');
var STM32_protocol = function () {
this.baud;
this.options = {};
@ -81,7 +89,7 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback)
self.initialize();
} else {
GUI.log(chrome.i18n.getMessage('failedToOpenSerialPort'));
GUI.log(i18n.getMessage('failedToOpenSerialPort'));
}
});
} else {
@ -108,7 +116,7 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback)
retries++;
if (retries > maxRetries) {
clearInterval(interval);
GUI.log(chrome.i18n.getMessage('failedToFlash') + port);
GUI.log(i18n.getMessage('failedToFlash') + port);
}
}
// Check for DFU devices
@ -145,7 +153,7 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback)
});
});
} else {
GUI.log(chrome.i18n.getMessage('failedToOpenSerialPort'));
GUI.log(i18n.getMessage('failedToOpenSerialPort'));
}
});
}
@ -170,11 +178,11 @@ STM32_protocol.prototype.initialize = function () {
// lock some UI elements TODO needs rework
$('select[name="release"]').prop('disabled', true);
CONFIGURATOR.connection.onReceive.addListener(function (info) {
CONFIGURATOR.connection.addOnReceiveCallback(function (info) {
self.read(info);
});
helper.interval.add('STM32_timeout', function () {
interval.add('STM32_timeout', function () {
if (self.upload_process_alive) { // process is running
self.upload_process_alive = false;
} else {
@ -183,10 +191,9 @@ STM32_protocol.prototype.initialize = function () {
$('span.progressLabel').text('STM32 - timed out, programming: FAILED');
self.progress_bar_e.addClass('invalid');
googleAnalytics.sendEvent('Flashing', 'Programming', 'timeout');
// protocol got stuck, clear timer and disconnect
helper.interval.remove('STM32_timeout');
interval.remove('STM32_timeout');
// exit
self.upload_procedure(99);
@ -381,10 +388,10 @@ STM32_protocol.prototype.upload_procedure = function (step) {
$('span.progressLabel').text('Contacting bootloader ...');
var send_counter = 0;
helper.interval.add('stm32_initialize_mcu', function () { // 200 ms interval (just in case mcu was already initialized), we need to break the 2 bytes command requirement
interval.add('stm32_initialize_mcu', function () { // 200 ms interval (just in case mcu was already initialized), we need to break the 2 bytes command requirement
self.send([0x7F], 1, function (reply) {
if (reply[0] == 0x7F || reply[0] == self.status.ACK || reply[0] == self.status.NACK) {
helper.interval.remove('stm32_initialize_mcu');
interval.remove('stm32_initialize_mcu');
console.log('STM32 - Serial interface initialized on the MCU side');
// proceed to next step
@ -393,7 +400,7 @@ STM32_protocol.prototype.upload_procedure = function (step) {
$('span.progressLabel').text('Communication with bootloader failed');
self.progress_bar_e.addClass('invalid');
helper.interval.remove('stm32_initialize_mcu');
interval.remove('stm32_initialize_mcu');
// disconnect
self.upload_procedure(99);
@ -407,8 +414,8 @@ STM32_protocol.prototype.upload_procedure = function (step) {
$('span.progressLabel').text('No response from the bootloader, programming: FAILED');
self.progress_bar_e.addClass('invalid');
helper.interval.remove('stm32_initialize_mcu');
helper.interval.remove('STM32_timeout');
interval.remove('stm32_initialize_mcu');
interval.remove('STM32_timeout');
// exit
self.upload_procedure(99);
@ -722,7 +729,6 @@ STM32_protocol.prototype.upload_procedure = function (step) {
if (verify) {
console.log('Programming: SUCCESSFUL');
$('span.progressLabel').text('Programming: SUCCESSFUL');
googleAnalytics.sendEvent('Flashing', 'Programming', 'success');
// update progress bar
self.progress_bar_e.addClass('valid');
@ -732,7 +738,6 @@ STM32_protocol.prototype.upload_procedure = function (step) {
} else {
console.log('Programming: FAILED');
$('span.progressLabel').text('Programming: FAILED');
googleAnalytics.sendEvent('Flashing', 'Programming', 'fail');
// update progress bar
self.progress_bar_e.addClass('invalid');
@ -769,7 +774,7 @@ STM32_protocol.prototype.upload_procedure = function (step) {
break;
case 99:
// disconnect
helper.interval.remove('STM32_timeout'); // stop STM32 timeout timer (everything is finished now)
interval.remove('STM32_timeout'); // stop STM32 timeout timer (everything is finished now)
// close connection
CONFIGURATOR.connection.disconnect(function (result) {
@ -793,3 +798,5 @@ STM32_protocol.prototype.upload_procedure = function (step) {
// initialize object
var STM32 = new STM32_protocol();
module.exports = STM32;

View file

@ -12,12 +12,15 @@
*/
'use strict';
const { GUI, TABS } = require('./../gui');
const i18n = require('./../localization');
var STM32DFU_protocol = function () {
this.callback = null;
this.hex = null;
this.verify_hex = [];
this.handle = null; // connection handle
this.usbDevice = null; // USB Device object
this.request = {
DETACH: 0x00, // OUT, Requests the device to leave DFU mode and enter the application.
@ -67,7 +70,7 @@ var STM32DFU_protocol = function () {
this.transferSize = 2048; // Default USB DFU transfer size for F3,F4 and F7
};
STM32DFU_protocol.prototype.connect = function (device, hex, options, callback) {
STM32DFU_protocol.prototype.connect = function (usbDevices, hex, options, callback) {
var self = this;
self.hex = hex;
self.callback = callback;
@ -90,67 +93,62 @@ STM32DFU_protocol.prototype.connect = function (device, hex, options, callback)
// reset progress bar to initial state
TABS.firmware_flasher.flashingMessage(null, TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL).flashProgress(0);
chrome.usb.getDevices(device, function (result) {
if (result.length) {
console.log('USB DFU detected with ID: ' + result[0].device);
navigator.usb.getDevices().then(devices => {
var dev = null;
devices.forEach(device => {
usbDevices.forEach(usbDev => {
if (device.vendorId == usbDev.vendorId && device.productId == usbDev.productId) {
self.usbDevice = device;
return;
}
});
});
self.openDevice(result[0]);
if (self.usbDevice ) {
console.log('USB DFU detected');
self.openDevice();
} else {
console.log('USB DFU not found');
GUI.log(chrome.i18n.getMessage('stm32UsbDfuNotFound'));
GUI.log(i18n.getMessage('stm32UsbDfuNotFound'));
}
});
};
STM32DFU_protocol.prototype.openDevice = function (device) {
STM32DFU_protocol.prototype.openDevice = function () {
var self = this;
chrome.usb.openDevice(device, function (handle) {
if (checkChromeRuntimeError()) {
console.log('Failed to open USB device!');
GUI.log(chrome.i18n.getMessage('usbDeviceOpenFail'));
if(GUI.operating_system === 'Linux') {
GUI.log(chrome.i18n.getMessage('usbDeviceUdevNotice'));
}
return;
}
self.handle = handle;
GUI.log(chrome.i18n.getMessage('usbDeviceOpened', handle.handle.toString()));
console.log('Device opened with Handle ID: ' + handle.handle);
self.usbDevice.open().then( () => {
GUI.log(i18n.getMessage('usbDeviceOpened'));
console.log('USB-Device opened');
self.claimInterface(0);
}).catch(error => {
console.log('Failed to open USB device: ' + error);
GUI.log(i18n.getMessage('usbDeviceOpenFail'));
if(GUI.operating_system === 'Linux') {
GUI.log(i18n.getMessage('usbDeviceUdevNotice'));
}
});
};
STM32DFU_protocol.prototype.closeDevice = function () {
var self = this;
chrome.usb.closeDevice(this.handle, function closed() {
if (checkChromeRuntimeError()) {
self.usbDevice.close().then(() => {
GUI.log(i18n.getMessage('usbDeviceClosed'));
console.log('USB-Device closed');
}).catch(error => {
console.log('Failed to close USB device!');
GUI.log(chrome.i18n.getMessage('usbDeviceCloseFail'));
}
GUI.log(chrome.i18n.getMessage('usbDeviceClosed'));
console.log('Device closed with Handle ID: ' + self.handle.handle);
self.handle = null;
GUI.log(i18n.getMessage('usbDeviceCloseFail'));
});
self.usbDevice = null;
};
STM32DFU_protocol.prototype.claimInterface = function (interfaceNumber) {
var self = this;
chrome.usb.claimInterface(this.handle, interfaceNumber, function claimed() {
// Don't perform the error check on MacOS at this time as there seems to be a bug
// where it always reports the Chrome error "Error claiming interface." even though
// the interface is in fact successfully claimed.
if (checkChromeRuntimeError() && (GUI.operating_system !== "MacOS")) {
console.log('Failed to claim USB device!');
self.cleanup();
}
self.usbDevice.claimInterface(interfaceNumber).then( () => {
console.log('Claimed interface: ' + interfaceNumber);
if (self.options.exitDfu) {
@ -158,22 +156,25 @@ STM32DFU_protocol.prototype.claimInterface = function (interfaceNumber) {
} else {
self.upload_procedure(0);
}
}).catch(error => {
console.log('Failed to claim USB device: ' + error);
self.cleanup();
});
};
STM32DFU_protocol.prototype.releaseInterface = function (interfaceNumber) {
var self = this;
chrome.usb.releaseInterface(this.handle, interfaceNumber, function released() {
self.usbDevice.releaseInterface(interfaceNumber).then(() => {
console.log('Released interface: ' + interfaceNumber);
self.closeDevice();
});
};
STM32DFU_protocol.prototype.resetDevice = function (callback) {
chrome.usb.resetDevice(this.handle, function (result) {
console.log('Reset Device: ' + result);
var self = this;
self.usbDevice.reset().then( () => {
console.log('Reset USB-Device');
if (callback) callback();
});
@ -181,46 +182,50 @@ STM32DFU_protocol.prototype.resetDevice = function (callback) {
STM32DFU_protocol.prototype.getString = function (index, callback) {
var self = this;
chrome.usb.controlTransfer(self.handle, {
'direction': 'in',
'recipient': 'device',
'requestType': 'standard',
'request': 6,
'value': 0x300 | index,
'index': 0, // specifies language
'length': 255 // max length to retreive
}, function (result) {
if (checkChromeRuntimeError()) {
console.log('USB getString failed! ' + result.resultCode);
callback("", result.resultCode);
return;
}
var view = new DataView(result.data);
var length = view.getUint8(0);
var setup = {
recipient: 'device',
requestType: 'standard',
request: 6,
value: 0x300 | index,
index: 0 // specifies language
};
self.usbDevice.controlTransferIn(setup, 255).then(result => {
if (result.status == 'ok') {;
var length = result.data.getUint8(0);
var descriptor = "";
for (var i = 2; i < length; i += 2) {
var charCode = view.getUint16(i, true);
var charCode = result.data.getUint16(i, true);
descriptor += String.fromCharCode(charCode);
}
callback(descriptor, result.resultCode);
callback(descriptor, 0);
} else {
throw new Error(result.status);
}
}).catch(error => {
console.log('USB getString failed: ' + error);
callback("", 1);
});
};
STM32DFU_protocol.prototype.getInterfaceDescriptors = function (interfaceNum, callback) {
var self = this;
chrome.usb.getConfiguration( this.handle, function (config) {
if (checkChromeRuntimeError()) {
console.log('USB getConfiguration failed!');
callback([], -200);
return;
}
var interfaceID = 0;
var descriptorStringArray = [];
var interfacesCount = self.usbDevice.configuration.interfaces.length;
var descriptorCount = 0;
if (interfacesCount == 0) {
callback(0, 1); //Error
} else if (interfacesCount == 1) {
descriptorCount = self.usbDevice.configuration.interfaces[0].alternates.length
} else if (interfacesCount > 1) {
descriptorCount = interfacesCount;
}
var getDescriptorString = function () {
if(interfaceID < config.interfaces.length) {
if (interfaceID < descriptorCount) {
self.getInterfaceDescriptor(interfaceID, function (descriptor, resultCode) {
if (resultCode) {
callback([], resultCode);
@ -245,28 +250,21 @@ STM32DFU_protocol.prototype.getInterfaceDescriptors = function (interfaceNum, ca
}
};
getDescriptorString();
});
};
STM32DFU_protocol.prototype.getInterfaceDescriptor = function (_interface, callback) {
var self = this;
chrome.usb.controlTransfer(this.handle, {
'direction': 'in',
'recipient': 'device',
'requestType': 'standard',
'request': 6,
'value': 0x200,
'index': 0,
'length': 18 + _interface * 9
}, function (result) {
if (checkChromeRuntimeError()) {
console.log('USB getInterfaceDescriptor failed! ' + result.resultCode);
callback({}, result.resultCode);
return;
var setup = {
recipient: 'device',
requestType: 'standard',
request: 6,
value: 0x200,
index: 0
}
var buf = new Uint8Array(result.data, 9 + _interface * 9);
self.usbDevice.controlTransferIn(setup, 18 + _interface * 9).then(result => {
if (result.status == 'ok') {
var buf = new Uint8Array(result.data.buffer, 9 + _interface * 9);
var descriptor = {
'bLength': buf[0],
'bDescriptorType': buf[1],
@ -278,30 +276,29 @@ STM32DFU_protocol.prototype.getInterfaceDescriptor = function (_interface, callb
'bInterfaceProtocol': buf[7],
'iInterface': buf[8]
};
callback(descriptor, result.resultCode);
callback(descriptor, 0);
} else {
throw new Error(result.status)
}
}).catch(error => {
console.log('USB getInterfaceDescriptor failed: ' + error);
callback({}, 1);
return;
});
};
STM32DFU_protocol.prototype.getFunctionalDescriptor = function (_interface, callback) {
var self = this;
chrome.usb.controlTransfer(this.handle, {
'direction': 'in',
'recipient': 'interface',
'requestType': 'standard',
'request': 6,
'value': 0x2100,
'index': 0,
'length': 255
}, function (result) {
if (checkChromeRuntimeError()) {
console.log('USB getFunctionalDescriptor failed! ' + result.resultCode);
callback({}, result.resultCode);
return;
}
var buf = new Uint8Array(result.data);
var setup = {
recipient: 'interface',
requestType: 'standard',
request: 6,
value: 0x2100,
index: 0
};
self.usbDevice.controlTransferIn(setup, 255).then(result => {
if (result.status == 'ok') {
var buf = new Uint8Array(result.data.buffer);
var descriptor = {
'bLength': buf[0],
'bDescriptorType': buf[1],
@ -310,8 +307,13 @@ STM32DFU_protocol.prototype.getFunctionalDescriptor = function (_interface, call
'wTransferSize': (buf[6] << 8)|buf[5],
'bcdDFUVersion': buf[7]
};
callback(descriptor, result.resultCode);
callback(descriptor, 0);
} else {
throw new Error(result.status);
}
}).catch(error => {
console.log('USB getFunctionalDescriptor failed! ' + error);
callback({}, 1);
});
};
@ -424,60 +426,49 @@ STM32DFU_protocol.prototype.getChipInfo = function (_interface, callback) {
STM32DFU_protocol.prototype.controlTransfer = function (direction, request, value, _interface, length, data, callback, _timeout) {
var self = this;
// timeout support was added in chrome v43
var timeout;
if (typeof _timeout === "undefined") {
timeout = 0; // default is 0 (according to chrome.usb API)
} else {
timeout = _timeout;
}
if (direction == 'in') {
// data is ignored
chrome.usb.controlTransfer(this.handle, {
'direction': 'in',
'recipient': 'interface',
'requestType': 'class',
'request': request,
'value': value,
'index': _interface,
'length': length,
'timeout': timeout
}, function (result) {
if (checkChromeRuntimeError()) {
console.log('USB controlTransfer IN failed for request ' + request + '!');
var setup = {
recipient: 'interface',
requestType: 'class',
request: request,
value: value,
index: _interface
}
if (result.resultCode) console.log('USB transfer result code: ' + result.resultCode);
var buf = new Uint8Array(result.data);
self.usbDevice.controlTransferIn(setup, length).then( result => {
if (result.status == 'ok') {
var buf = new Uint8Array(result.data.buffer);
callback(buf, result.resultCode);
} else {
throw new Error(result.status);
}
}).catch(() => {
console.log('USB controlTransfer IN failed for request ' + request + '!');
});
} else {
// length is ignored
var dataIn;
if (data) {
var arrayBuf = new ArrayBuffer(data.length);
var arrayBufView = new Uint8Array(arrayBuf);
arrayBufView.set(data);
dataIn = new Uint8Array(data);;
} else {
var arrayBuf = new ArrayBuffer(0);
dataIn = new Uint8Array(0);
}
chrome.usb.controlTransfer(this.handle, {
'direction': 'out',
'recipient': 'interface',
'requestType': 'class',
'request': request,
'value': value,
'index': _interface,
'data': arrayBuf,
'timeout': timeout
}, function (result) {
if (checkChromeRuntimeError()) {
console.log('USB controlTransfer OUT failed for request ' + request + '!');
var setup = {
recipient: 'interface',
requestType: 'class',
request: request,
value: value,
index: _interface
}
if (result.resultCode) console.log('USB transfer result code: ' + result.resultCode);
self.usbDevice.controlTransferOut(setup, dataIn).then(result => {
if (result.status == 'ok') {
callback(result);
} else {
throw new Error(result.status);
}
}).catch(() => {
console.log('USB controlTransfer OUT failed for request ' + request + '!');
});
}
};
@ -568,10 +559,10 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
self.flash_layout = chipInfo.internal_flash;
self.available_flash_size = self.flash_layout.total_size - (self.hex.start_linear_address - self.flash_layout.start_address);
GUI.log(chrome.i18n.getMessage('dfu_device_flash_info', (self.flash_layout.total_size / 1024).toString()));
GUI.log(i18n.getMessage('dfu_device_flash_info', (self.flash_layout.total_size / 1024).toString()));
if (self.hex.bytes_total > self.available_flash_size) {
GUI.log(chrome.i18n.getMessage('dfu_error_image_size',
GUI.log(i18n.getMessage('dfu_error_image_size',
[(self.hex.bytes_total / 1024.0).toFixed(1),
(self.available_flash_size / 1024.0).toFixed(1)]));
self.cleanup();
@ -595,10 +586,10 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
self.available_flash_size = firmware_partition_size;
GUI.log(chrome.i18n.getMessage('dfu_device_flash_info', (self.flash_layout.total_size / 1024).toString()));
GUI.log(i18n.getMessage('dfu_device_flash_info', (self.flash_layout.total_size / 1024).toString()));
if (self.hex.bytes_total > self.available_flash_size) {
GUI.log(chrome.i18n.getMessage('dfu_error_image_size',
GUI.log(i18n.getMessage('dfu_error_image_size',
[(self.hex.bytes_total / 1024.0).toFixed(1),
(self.available_flash_size / 1024.0).toFixed(1)]));
self.cleanup();
@ -631,7 +622,7 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
var unprotect = function() {
console.log('Initiate read unprotect');
let messageReadProtected = chrome.i18n.getMessage('stm32ReadProtected');
let messageReadProtected = i18n.getMessage('stm32ReadProtected');
GUI.log(messageReadProtected);
TABS.firmware_flasher.flashingMessage(messageReadProtected, TABS.firmware_flasher.FLASH_MESSAGE_TYPES.ACTION);
@ -654,9 +645,9 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
self.controlTransfer('in', self.request.GETSTATUS, 0, 0, 6, 0, function (data, error) { // should stall/disconnect
if(error) { // we encounter an error, but this is expected. should be a stall.
console.log('Unprotect memory command ran successfully. Unplug flight controller. Connect again in DFU mode and try flashing again.');
GUI.log(chrome.i18n.getMessage('stm32UnprotectSuccessful'));
GUI.log(i18n.getMessage('stm32UnprotectSuccessful'));
let messageUnprotectUnplug = chrome.i18n.getMessage('stm32UnprotectUnplug');
let messageUnprotectUnplug = i18n.getMessage('stm32UnprotectUnplug');
GUI.log(messageUnprotectUnplug);
TABS.firmware_flasher.flashingMessage(messageUnprotectUnplug, TABS.firmware_flasher.FLASH_MESSAGE_TYPES.ACTION)
@ -665,8 +656,8 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
} else { // unprotecting the flight controller did not work. It did not reboot.
console.log('Failed to execute unprotect memory command');
GUI.log(chrome.i18n.getMessage('stm32UnprotectFailed'));
TABS.firmware_flasher.flashingMessage(chrome.i18n.getMessage('stm32UnprotectFailed'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
GUI.log(i18n.getMessage('stm32UnprotectFailed'));
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32UnprotectFailed'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
console.log(data);
self.cleanup();
}
@ -674,7 +665,7 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
}, incr);
} else {
console.log('Failed to initiate unprotect memory command');
let messageUnprotectInitFailed = chrome.i18n.getMessage('stm32UnprotectInitFailed');
let messageUnprotectInitFailed = i18n.getMessage('stm32UnprotectInitFailed');
GUI.log(messageUnprotectInitFailed);
TABS.firmware_flasher.flashingMessage(messageUnprotectInitFailed, TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
self.cleanup();
@ -695,7 +686,7 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
if (data[4] == self.state.dfuUPLOAD_IDLE && ob_data.length == self.chipInfo.option_bytes.total_size) {
console.log('Option bytes read successfully');
console.log('Chip does not appear read protected');
GUI.log(chrome.i18n.getMessage('stm32NotReadProtected'));
GUI.log(i18n.getMessage('stm32NotReadProtected'));
// it is pretty safe to continue to erase flash
self.clearStatus(function() {
self.upload_procedure(2);
@ -744,14 +735,14 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
// if address load fails with this specific error though, it is very likely bc of read protection
if(loadAddressResponse[4] == self.state.dfuERROR && loadAddressResponse[0] == self.status.errVENDOR) {
// read protected
GUI.log(chrome.i18n.getMessage('stm32AddressLoadFailed'));
GUI.log(i18n.getMessage('stm32AddressLoadFailed'));
self.clearStatus(unprotect);
return;
} else if(loadAddressResponse[4] == self.state.dfuDNLOAD_IDLE) {
console.log('Address load for option bytes sector succeeded.');
self.clearStatus(tryReadOB);
} else {
GUI.log(chrome.i18n.getMessage('stm32AddressLoadUnknown'));
GUI.log(i18n.getMessage('stm32AddressLoadUnknown'));
self.cleanup();
}
};
@ -793,13 +784,13 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
if (erase_pages.length === 0) {
console.log('Aborting, No flash pages to erase');
TABS.firmware_flasher.flashingMessage(chrome.i18n.getMessage('stm32InvalidHex'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32InvalidHex'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
self.cleanup();
break;
}
TABS.firmware_flasher.flashingMessage(chrome.i18n.getMessage('stm32Erase'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32Erase'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
console.log('Executing local chip erase', erase_pages);
var page = 0;
@ -811,7 +802,7 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
if(page == erase_pages.length) {
console.log("Erase: complete");
GUI.log(chrome.i18n.getMessage('dfu_erased_kilobytes', (total_erased / 1024).toString()));
GUI.log(i18n.getMessage('dfu_erased_kilobytes', (total_erased / 1024).toString()));
self.upload_procedure(4);
} else {
erase_page();
@ -881,7 +872,7 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
// upload
// we dont need to clear the state as we are already using DFU_DNLOAD
console.log('Writing data ...');
TABS.firmware_flasher.flashingMessage(chrome.i18n.getMessage('stm32Flashing'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32Flashing'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
var blocks = self.hex.data.length - 1;
var flashing_block = 0;
@ -953,7 +944,7 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
case 5:
// verify
console.log('Verifying data ...');
TABS.firmware_flasher.flashingMessage(chrome.i18n.getMessage('stm32Verifying'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32Verifying'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
var blocks = self.hex.data.length - 1;
var reading_block = 0;
@ -1021,14 +1012,14 @@ STM32DFU_protocol.prototype.upload_procedure = function (step) {
if (verify) {
console.log('Programming: SUCCESSFUL');
// update progress bar
TABS.firmware_flasher.flashingMessage(chrome.i18n.getMessage('stm32ProgrammingSuccessful'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.VALID);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32ProgrammingSuccessful'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.VALID);
// proceed to next step
self.leave();
} else {
console.log('Programming: FAILED');
// update progress bar
TABS.firmware_flasher.flashingMessage(chrome.i18n.getMessage('stm32ProgrammingFailed'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32ProgrammingFailed'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
// disconnect
self.cleanup();
@ -1083,3 +1074,4 @@ STM32DFU_protocol.prototype.cleanup = function () {
// initialize object
var STM32DFU = new STM32DFU_protocol();
module.exports = STM32DFU;

View file

@ -1,8 +1,6 @@
/*global $*/
'use strict';
let Safehome = function (number, enabled, lat, lon) {
var Safehome = function (number, enabled, lat, lon) {
var self = {};
@ -59,3 +57,5 @@ let Safehome = function (number, enabled, lat, lon) {
return self;
};
module.exports = Safehome;

View file

@ -1,6 +1,8 @@
'use strict';
let SafehomeCollection = function () {
const BitHelper = require('./bitHelper');
var SafehomeCollection = function () {
let self = {},
data = [],
@ -67,14 +69,14 @@ let SafehomeCollection = function () {
if (safehomeId < self.safehomeCount()) {
buffer.push(safehome.getNumber()); // sbufReadU8(src); // number
buffer.push(1);
buffer.push(specificByte(safehome.getLat(), 0)); // sbufReadU32(src); // lat
buffer.push(specificByte(safehome.getLat(), 1));
buffer.push(specificByte(safehome.getLat(), 2));
buffer.push(specificByte(safehome.getLat(), 3));
buffer.push(specificByte(safehome.getLon(), 0)); // sbufReadU32(src); // lon
buffer.push(specificByte(safehome.getLon(), 1));
buffer.push(specificByte(safehome.getLon(), 2));
buffer.push(specificByte(safehome.getLon(), 3));
buffer.push(BitHelper.specificByte(safehome.getLat(), 0)); // sbufReadU32(src); // lat
buffer.push(BitHelper.specificByte(safehome.getLat(), 1));
buffer.push(BitHelper.specificByte(safehome.getLat(), 2));
buffer.push(BitHelper.specificByte(safehome.getLat(), 3));
buffer.push(BitHelper.specificByte(safehome.getLon(), 0)); // sbufReadU32(src); // lon
buffer.push(BitHelper.specificByte(safehome.getLon(), 1));
buffer.push(BitHelper.specificByte(safehome.getLon(), 2));
buffer.push(BitHelper.specificByte(safehome.getLon(), 3));
} else {
buffer = Array(10).fill(0);
buffer[0] = safehomeId;
@ -98,3 +100,5 @@ let SafehomeCollection = function () {
return self;
};
module.exports = SafehomeCollection;

View file

@ -1,8 +1,10 @@
'use strict';
var helper = helper || {};
const FC = require('./fc');
const i18n = require('./localization');
const bitHelper = require('./bitHelper');
helper.serialPortHelper = (function () {
const serialPortHelper = (function () {
let publicScope = {},
privateScope = {};
@ -209,7 +211,7 @@ helper.serialPortHelper = (function () {
}
for (var i = 0; i < privateScope.rules.length; i++) {
privateScope.rules[i].displayName = chrome.i18n.getMessage('portsFunction_' + privateScope.rules[i].name);
privateScope.rules[i].displayName = i18n.getMessage('portsFunction_' + privateScope.rules[i].name);
}
privateScope.namesGenerated = true;
@ -242,7 +244,7 @@ helper.serialPortHelper = (function () {
let key = functions[index];
let bitIndex = privateScope.functionIDs[key];
if (bitIndex >= 0) {
mask = bit_set(mask, bitIndex);
mask = bitHelper.bit_set(mask, bitIndex);
}
}
return mask;
@ -260,7 +262,7 @@ helper.serialPortHelper = (function () {
for (let index = 0; index < keys.length; index++) {
let key = keys[index];
let bit = privateScope.functionIDs[key];
if (bit_check(mask, bit)) {
if (bitHelper.bit_check(mask, bit)) {
functions.push(key);
}
}
@ -274,8 +276,8 @@ helper.serialPortHelper = (function () {
publicScope.getPortIdentifiersForFunction = function (functionName) {
let identifiers = [];
for (let index = 0; index < SERIAL_CONFIG.ports.length; index++) {
let config = SERIAL_CONFIG.ports[index];
for (let index = 0; index < FC.SERIAL_CONFIG.ports.length; index++) {
let config = FC.SERIAL_CONFIG.ports[index];
if (config.functions.indexOf(functionName) != -1) {
identifiers.push(config.identifier);
}
@ -288,8 +290,8 @@ helper.serialPortHelper = (function () {
let list = [];
for (let index = 0; index < SERIAL_CONFIG.ports.length; index++) {
let config = SERIAL_CONFIG.ports[index];
for (let index = 0; index < FC.SERIAL_CONFIG.ports.length; index++) {
let config = FC.SERIAL_CONFIG.ports[index];
//exclude USB VCP port
if (config.identifier == 20) {
@ -310,8 +312,8 @@ helper.serialPortHelper = (function () {
};
publicScope.getPortByIdentifier = function (identifier) {
for (let index = 0; index < SERIAL_CONFIG.ports.length; index++) {
let config = SERIAL_CONFIG.ports[index];
for (let index = 0; index < FC.SERIAL_CONFIG.ports.length; index++) {
let config = FC.SERIAL_CONFIG.ports[index];
if (config.identifier == identifier) {
return config;
}
@ -320,8 +322,8 @@ helper.serialPortHelper = (function () {
};
publicScope.clearByFunction = function (functionName) {
for (let index = 0; index < SERIAL_CONFIG.ports.length; index++) {
let config = SERIAL_CONFIG.ports[index];
for (let index = 0; index < FC.SERIAL_CONFIG.ports.length; index++) {
let config = FC.SERIAL_CONFIG.ports[index];
if (config.functions.indexOf(functionName) != -1) {
config.functions = [];
}
@ -349,3 +351,5 @@ helper.serialPortHelper = (function () {
return publicScope;
})();
module.exports = serialPortHelper;

View file

@ -1,23 +1,57 @@
/*global chrome, chrome.i18n*/
'use strict';
$(document).ready(function () {
const semver = require('semver');
const Store = require('electron-store');
const store = new Store();
var $port = $('#port'),
$baud = $('#baud'),
$portOverride = $('#port-override'),
isDemoRunning = false;
const { GUI, TABS } = require('./gui');
const MSP = require('./msp');
const FC = require('./fc');
const MSPCodes = require('./msp/MSPCodes');
const mspHelper = require('./msp/MSPHelper');
const { ConnectionType, Connection } = require('./connection/connection');
const connectionFactory = require('./connection/connectionFactory');
const CONFIGURATOR = require('./data_storage');
const { PortHandler } = require('./port_handler');
const i18n = require('./../js/localization');
const interval = require('./intervals');
const periodicStatusUpdater = require('./periodicStatusUpdater');
const mspQueue = require('./serial_queue');
const timeout = require('./timeouts');
const mspBalancedInterval = require('./msp_balanced_interval');
const defaultsDialog = require('./defaults_dialog');
const { SITLProcess } = require('./sitl');
const update = require('./globalUpdates');
const BitHelper = require('./bitHelper');
const BOARD = require('./boards');
const jBox = require('./libraries/jBox/jBox.min');
const groundstation = require('./groundstation');
const ltmDecoder = require('./ltmDecoder');
var SerialBackend = (function () {
var publicScope = {},
privateScope = {};
privateScope.isDemoRunning = false;
/*
* Handle "Wireless" mode with strict queueing of messages
*/
$('#wireless-mode').change(function () {
publicScope.init = function() {
privateScope.$port = $('#port'),
privateScope.$baud = $('#baud'),
publicScope.$portOverride = $('#port-override'),
mspHelper.setSensorStatusEx(privateScope.sensor_status_ex);
$('#wireless-mode').on('change', function () {
var $this = $(this);
if ($this.is(':checked')) {
helper.mspQueue.setLockMethod('hard');
mspQueue.setLockMethod('hard');
} else {
helper.mspQueue.setLockMethod('soft');
mspQueue.setLockMethod('soft');
}
});
@ -25,11 +59,11 @@ $(document).ready(function () {
let modal;
if (BOARD.hasVcp(CONFIG.boardIdentifier)) { // VCP-based flight controls may crash old drivers, we catch and reconnect
if (BOARD.hasVcp(FC.CONFIG.boardIdentifier)) { // VCP-based flight controls may crash old drivers, we catch and reconnect
modal = new jBox('Modal', {
width: 400,
height: 100,
height: 120,
animation: false,
closeOnClick: false,
closeOnEsc: false,
@ -40,7 +74,7 @@ $(document).ready(function () {
Disconnect
*/
setTimeout(function () {
$('a.connect').click();
$('a.connect').trigger( "click" );
}, 100);
/*
@ -48,24 +82,24 @@ $(document).ready(function () {
*/
setTimeout(function start_connection() {
modal.close();
$('a.connect').click();
$('a.connect').trigger( "click" );
/*
Open configuration tab
*/
if ($tabElement != null) {
setTimeout(function () {
$tabElement.click();
$tabElement.trigger( "click" );
}, 500);
}
}, 7000);
} else {
helper.timeout.add('waiting_for_bootup', function waiting_for_bootup() {
timeout.add('waiting_for_bootup', function waiting_for_bootup() {
MSP.send_message(MSPCodes.MSPV2_INAV_STATUS, false, false, function () {
//noinspection JSUnresolvedVariable
GUI.log(chrome.i18n.getMessage('deviceReady'));
GUI.log(i18n.getMessage('deviceReady'));
//noinspection JSValidateTypes
TABS.configuration.initialize(false, $('#content').scrollTop());
});
@ -76,7 +110,7 @@ $(document).ready(function () {
GUI.updateManualPortVisibility = function(){
var selected_port = $port.find('option:selected');
var selected_port = privateScope.$port.find('option:selected');
if (selected_port.data().isManual || selected_port.data().isTcp || selected_port.data().isUdp) {
$('#port-override-option').show();
}
@ -91,10 +125,10 @@ $(document).ready(function () {
}
if (selected_port.data().isDFU || selected_port.data().isBle || selected_port.data().isTcp || selected_port.data().isUdp || selected_port.data().isSitl) {
$baud.hide();
privateScope.$baud.hide();
}
else {
$baud.show();
privateScope.$baud.show();
}
if (selected_port.data().isBle || selected_port.data().isTcp || selected_port.data().isUdp || selected_port.data().isSitl) {
@ -110,40 +144,39 @@ $(document).ready(function () {
} else if (selected_port.data().isUdp) {
type = ConnectionType.UDP;
}
CONFIGURATOR.connection = Connection.create(type);
CONFIGURATOR.connection = connectionFactory(type, CONFIGURATOR.connection);
};
GUI.updateManualPortVisibility();
$portOverride.change(function () {
chrome.storage.local.set({'portOverride': $portOverride.val()});
publicScope.$portOverride.on('change', function () {
store.set('portOverride', publicScope.$portOverride.val());
});
chrome.storage.local.get('portOverride', function (data) {
$portOverride.val(data.portOverride);
});
publicScope.$portOverride.val(store.get('portOverride', ''));
$port.change(function (target) {
privateScope.$port.on('change', function (target) {
GUI.updateManualPortVisibility();
});
$('div.connect_controls a.connect').click(function () {
if (helper.groundstation.isActivated()) {
helper.groundstation.deactivate();
if (groundstation.isActivated()) {
groundstation.deactivate();
}
if (GUI.connect_lock != true) { // GUI control overrides the user control
var clicks = $(this).data('clicks');
var selected_baud = parseInt($baud.val());
var selected_port = $port.find('option:selected').data().isManual ?
$portOverride.val() :
String($port.val());
var selected_baud = parseInt(privateScope.$baud.val());
var selected_port = privateScope.$port.find('option:selected').data().isManual ?
publicScope.$portOverride.val() :
String(privateScope.$port.val());
if (selected_port === 'DFU') {
GUI.log(chrome.i18n.getMessage('dfu_connect_message'));
GUI.log(i18n.getMessage('dfu_connect_message'));
}
else if (selected_port != '0') {
if (!clicks) {
@ -152,21 +185,25 @@ $(document).ready(function () {
// lock port select & baud while we are connecting / connected
$('#port, #baud, #delay').prop('disabled', true);
$('div.connect_controls a.connect_state').text(chrome.i18n.getMessage('connecting'));
$('div.connect_controls a.connect_state').text(i18n.getMessage('connecting'));
if (selected_port == 'tcp' || selected_port == 'udp') {
CONFIGURATOR.connection.connect($portOverride.val(), {}, onOpen);
CONFIGURATOR.connection.connect(publicScope.$portOverride.val(), {}, privateScope.onOpen);
} else if (selected_port == 'sitl') {
CONFIGURATOR.connection.connect("127.0.0.1:5760", {}, onOpen);
CONFIGURATOR.connection.connect("127.0.0.1:5760", {}, privateScope.onOpen);
} else if (selected_port == 'sitl-demo') {
if (SITLProcess.isRunning) {
SITLProcess.stop();
}
SITLProcess.start("demo.bin");
SITLProcess.start("demo.bin"), 1000;
this.isDemoRunning = true;
CONFIGURATOR.connection.connect("127.0.0.1:5760", {}, onOpen);
// Wait 1 sec until SITL is ready
setTimeout(() => {
CONFIGURATOR.connection.connect("127.0.0.1:5760", {}, privateScope.onOpen);
}, 1000);
} else {
CONFIGURATOR.connection.connect(selected_port, {bitrate: selected_baud}, onOpen);
CONFIGURATOR.connection.connect(selected_port, {bitrate: selected_baud}, privateScope.onOpen);
}
} else {
if (this.isDemoRunning) {
@ -176,9 +213,9 @@ $(document).ready(function () {
var wasConnected = CONFIGURATOR.connectionValid;
helper.timeout.killAll();
helper.interval.killAll(['global_data_refresh', 'msp-load-update']);
helper.mspBalancedInterval.flush();
timeout.killAll();
interval.killAll(['global_data_refresh', 'msp-load-update']);
mspBalancedInterval.flush();
if (CONFIGURATOR.cliActive) {
GUI.tab_switch_cleanup(finishDisconnect);
@ -197,11 +234,11 @@ $(document).ready(function () {
/*
* Flush
*/
helper.mspQueue.flush();
helper.mspQueue.freeHardLock();
helper.mspQueue.freeSoftLock();
mspQueue.flush();
mspQueue.freeHardLock();
mspQueue.freeSoftLock();
CONFIGURATOR.connection.disconnect(onClosed);
CONFIGURATOR.connection.disconnect(privateScope.onClosed);
MSP.disconnect_cleanup();
// Reset various UI elements
@ -210,22 +247,22 @@ $(document).ready(function () {
$('span.cpu-load').text('');
// unlock port select & baud
$port.prop('disabled', false);
$baud.prop('disabled', false);
privateScope.$port.prop('disabled', false);
privateScope.$baud.prop('disabled', false);
// reset connect / disconnect button
$('div.connect_controls a.connect').removeClass('active');
$('div.connect_controls a.connect_state').text(chrome.i18n.getMessage('connect'));
$('div.connect_controls a.connect_state').text(i18n.getMessage('connect'));
// reset active sensor indicators
sensor_status(0);
privateScope.sensor_status(0);
if (wasConnected) {
// detach listeners and remove element data
$('#content').empty();
}
$('#tabs .tab_landing a').click();
$('#tabs .tab_landing a').trigger( "click" );
}
}
@ -235,69 +272,67 @@ $(document).ready(function () {
});
PortHandler.initialize();
});
}
function onValidFirmware()
{
privateScope.onValidFirmware = function ()
{
MSP.send_message(MSPCodes.MSP_BUILD_INFO, false, false, function () {
googleAnalytics.sendEvent('Firmware', 'Using', CONFIG.buildInfo);
GUI.log(chrome.i18n.getMessage('buildInfoReceived', [CONFIG.buildInfo]));
GUI.log(i18n.getMessage('buildInfoReceived', [FC.CONFIG.buildInfo]));
MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, function () {
googleAnalytics.sendEvent('Board', 'Using', CONFIG.boardIdentifier + ',' + CONFIG.boardVersion);
GUI.log(chrome.i18n.getMessage('boardInfoReceived', [CONFIG.boardIdentifier, CONFIG.boardVersion]));
GUI.log(i18n.getMessage('boardInfoReceived', [FC.CONFIG.boardIdentifier, FC.CONFIG.boardVersion]));
MSP.send_message(MSPCodes.MSP_UID, false, false, function () {
GUI.log(chrome.i18n.getMessage('uniqueDeviceIdReceived', [CONFIG.uid[0].toString(16) + CONFIG.uid[1].toString(16) + CONFIG.uid[2].toString(16)]));
GUI.log(i18n.getMessage('uniqueDeviceIdReceived', [FC.CONFIG.uid[0].toString(16) + FC.CONFIG.uid[1].toString(16) + FC.CONFIG.uid[2].toString(16)]));
// continue as usually
CONFIGURATOR.connectionValid = true;
GUI.allowedTabs = GUI.defaultAllowedTabsWhenConnected.slice();
onConnect();
privateScope.onConnect();
helper.defaultsDialog.init();
defaultsDialog.init();
$('#tabs ul.mode-connected .tab_setup a').click();
$('#tabs ul.mode-connected .tab_setup a').trigger( "click" );
updateEzTuneTabVisibility(true);
updateFirmwareVersion();
GUI.updateEzTuneTabVisibility(true);
update.firmwareVersion();
});
});
});
}
function onInvalidFirmwareVariant()
{
GUI.log(chrome.i18n.getMessage('firmwareVariantNotSupported'));
privateScope.onInvalidFirmwareVariant = function ()
{
GUI.log(i18n.getMessage('firmwareVariantNotSupported'));
CONFIGURATOR.connectionValid = true; // making it possible to open the CLI tab
GUI.allowedTabs = ['cli'];
onConnect();
$('#tabs .tab_cli a').click();
}
privateScope.onConnect();
$('#tabs .tab_cli a').trigger( "click" );
}
function onInvalidFirmwareVersion()
{
GUI.log(chrome.i18n.getMessage('firmwareVersionNotSupported', [CONFIGURATOR.minfirmwareVersionAccepted, CONFIGURATOR.maxFirmwareVersionAccepted]));
privateScope.onInvalidFirmwareVersion = function ()
{
GUI.log(i18n.getMessage('firmwareVersionNotSupported', [CONFIGURATOR.minfirmwareVersionAccepted, CONFIGURATOR.maxFirmwareVersionAccepted]));
CONFIGURATOR.connectionValid = true; // making it possible to open the CLI tab
GUI.allowedTabs = ['cli'];
onConnect();
$('#tabs .tab_cli a').click();
}
privateScope.onConnect();
$('#tabs .tab_cli a').trigger( "click" );
}
function onBleNotSupported() {
GUI.log(chrome.i18n.getMessage('connectionBleNotSupported'));
privateScope.onBleNotSupported = function () {
GUI.log(i18n.getMessage('connectionBleNotSupported'));
CONFIGURATOR.connection.abort();
}
}
function onOpen(openInfo) {
privateScope.onOpen = function (openInfo) {
if (FC.restartRequired) {
GUI_control.prototype.log("<span style='color: red; font-weight: bolder'><strong>" + chrome.i18n.getMessage("illegalStateRestartRequired") + "</strong></span>");
$('div.connect_controls a').click(); // disconnect
GUI_control.prototype.log("<span style='color: red; font-weight: bolder'><strong>" + i18n.getMessage("illegalStateRestartRequired") + "</strong></span>");
$('div.connect_controls a').trigger( "click" ); // disconnect
return;
}
@ -308,37 +343,37 @@ function onOpen(openInfo) {
// reset connecting_to
GUI.connecting_to = false;
GUI.log(chrome.i18n.getMessage('serialPortOpened', [openInfo.connectionId]));
GUI.log(i18n.getMessage('serialPortOpened', [openInfo.connectionId]));
// save selected port with chrome.storage if the port differs
chrome.storage.local.get('last_used_port', function (result) {
if (result.last_used_port) {
if (result.last_used_port != GUI.connected_to) {
// save selected port if the port differs
var last_used_port = store.get('last_used_port', false);
if (last_used_port) {
if (last_used_port != GUI.connected_to) {
// last used port doesn't match the one found in local db, we will store the new one
chrome.storage.local.set({'last_used_port': GUI.connected_to});
store.set('last_used_port', GUI.connected_to);
}
} else {
// variable isn't stored yet, saving
chrome.storage.local.set({'last_used_port': GUI.connected_to});
store.set('last_used_port', GUI.connected_to);
}
});
chrome.storage.local.set({last_used_bps: CONFIGURATOR.connection.bitrate});
chrome.storage.local.set({wireless_mode_enabled: $('#wireless-mode').is(":checked")});
CONFIGURATOR.connection.addOnReceiveListener(read_serial);
CONFIGURATOR.connection.addOnReceiveListener(helper.ltmDecoder.read);
store.set('last_used_bps', CONFIGURATOR.connection.bitrate);
store.set('wireless_mode_enabled', $('#wireless-mode').is(":checked"));
CONFIGURATOR.connection.addOnReceiveListener(publicScope.read_serial);
CONFIGURATOR.connection.addOnReceiveListener(ltmDecoder.read);
// disconnect after 10 seconds with error if we don't get IDENT data
helper.timeout.add('connecting', function () {
timeout.add('connecting', function () {
//As we add LTM listener, we need to invalidate connection only when both protocols are not listening!
if (!CONFIGURATOR.connectionValid && !helper.ltmDecoder.isReceiving()) {
GUI.log(chrome.i18n.getMessage('noConfigurationReceived'));
if (!CONFIGURATOR.connectionValid && !ltmDecoder.isReceiving()) {
GUI.log(i18n.getMessage('noConfigurationReceived'));
helper.mspQueue.flush();
helper.mspQueue.freeHardLock();
helper.mspQueue.freeSoftLock();
mspQueue.flush();
mspQueue.freeHardLock();
mspQueue.freeSoftLock();
CONFIGURATOR.connection.emptyOutputBuffer();
$('div.connect_controls a').click(); // disconnect
@ -346,9 +381,9 @@ function onOpen(openInfo) {
}, 10000);
//Add a timer that every 1s will check if LTM stream is receiving data and display alert if so
helper.interval.add('ltm-connection-check', function () {
if (helper.ltmDecoder.isReceiving()) {
helper.groundstation.activate($('#main-wrapper'));
interval.add('ltm-connection-check', function () {
if (ltmDecoder.isReceiving()) {
groundstation.activate($('#main-wrapper'));
}
}, 1000);
@ -359,46 +394,46 @@ function onOpen(openInfo) {
MSP.protocolVersion = MSP.constants.PROTOCOL_V2;
MSP.send_message(MSPCodes.MSP_API_VERSION, false, false, function () {
if (CONFIG.apiVersion === "0.0.0") {
GUI_control.prototype.log("<span style='color: red; font-weight: bolder'><strong>" + chrome.i18n.getMessage("illegalStateRestartRequired") + "</strong></span>");
if (FC.CONFIG.apiVersion === "0.0.0") {
GUI_control.prototype.log("<span style='color: red; font-weight: bolder'><strong>" + i18n.getMessage("illegalStateRestartRequired") + "</strong></span>");
FC.restartRequired = true;
return;
}
GUI.log(chrome.i18n.getMessage('apiVersionReceived', [CONFIG.apiVersion]));
GUI.log(i18n.getMessage('apiVersionReceived', [FC.CONFIG.apiVersion]));
MSP.send_message(MSPCodes.MSP_FC_VARIANT, false, false, function () {
if (CONFIG.flightControllerIdentifier == 'INAV') {
if (FC.CONFIG.flightControllerIdentifier == 'INAV') {
MSP.send_message(MSPCodes.MSP_FC_VERSION, false, false, function () {
googleAnalytics.sendEvent('Firmware', 'Variant', CONFIG.flightControllerIdentifier + ',' + CONFIG.flightControllerVersion);
GUI.log(chrome.i18n.getMessage('fcInfoReceived', [CONFIG.flightControllerIdentifier, CONFIG.flightControllerVersion]));
if (semver.gte(CONFIG.flightControllerVersion, CONFIGURATOR.minfirmwareVersionAccepted) && semver.lt(CONFIG.flightControllerVersion, CONFIGURATOR.maxFirmwareVersionAccepted)) {
if (CONFIGURATOR.connection.type == ConnectionType.BLE && semver.lt(CONFIG.flightControllerVersion, "5.0.0")) {
onBleNotSupported();
GUI.log(i18n.getMessage('fcInfoReceived', [FC.CONFIG.flightControllerIdentifier, FC.CONFIG.flightControllerVersion]));
if (semver.gte(FC.CONFIG.flightControllerVersion, CONFIGURATOR.minfirmwareVersionAccepted) && semver.lt(FC.CONFIG.flightControllerVersion, CONFIGURATOR.maxFirmwareVersionAccepted)) {
if (CONFIGURATOR.connection.type == ConnectionType.BLE && semver.lt(FC.CONFIG.flightControllerVersion, "5.0.0")) {
privateScope.onBleNotSupported();
} else {
mspHelper.getCraftName(function(name) {
if (name) {
CONFIG.name = name;
FC.CONFIG.name = name;
}
onValidFirmware();
privateScope.onValidFirmware();
});
}
} else {
onInvalidFirmwareVersion();
privateScope.onInvalidFirmwareVersion();
}
});
} else {
onInvalidFirmwareVariant();
privateScope.onInvalidFirmwareVariant();
}
});
});
} else {
console.log('Failed to open serial port');
GUI.log(chrome.i18n.getMessage('serialPortOpenFail'));
GUI.log(i18n.getMessage('serialPortOpenFail'));
var $connectButton = $('#connectbutton');
$connectButton.find('.connect_state').text(chrome.i18n.getMessage('connect'));
$connectButton.find('.connect_state').text(i18n.getMessage('connect'));
$connectButton.find('.connect').removeClass('active');
// unlock port select & baud
@ -407,11 +442,11 @@ function onOpen(openInfo) {
// reset data
$connectButton.find('.connect').data("clicks", false);
}
}
}
function onConnect() {
helper.timeout.remove('connecting'); // kill connecting timer
$('#connectbutton a.connect_state').text(chrome.i18n.getMessage('disconnect')).addClass('active');
privateScope.onConnect = function () {
timeout.remove('connecting'); // kill connecting timer
$('#connectbutton a.connect_state').text(i18n.getMessage('disconnect')).addClass('active');
$('#connectbutton a.connect').addClass('active');
$('.mode-disconnected').hide();
$('.mode-connected').show();
@ -433,25 +468,25 @@ function onConnect() {
let pidCount = 11;
for (let i = 0; i < pidCount; i++) {
PIDs.push(new Array(4));
FC.PIDs.push(new Array(4));
}
helper.interval.add('msp-load-update', function () {
interval.add('msp-load-update', function () {
$('#msp-version').text("MSP version: " + MSP.protocolVersion.toFixed(0));
$('#msp-load').text("MSP load: " + helper.mspQueue.getLoad().toFixed(1));
$('#msp-roundtrip').text("MSP round trip: " + helper.mspQueue.getRoundtrip().toFixed(0));
$('#hardware-roundtrip').text("HW round trip: " + helper.mspQueue.getHardwareRoundtrip().toFixed(0));
$('#drop-rate').text("Drop ratio: " + helper.mspQueue.getDropRatio().toFixed(0) + "%");
$('#msp-load').text("MSP load: " + mspQueue.getLoad().toFixed(1));
$('#msp-roundtrip').text("MSP round trip: " + mspQueue.getRoundtrip().toFixed(0));
$('#hardware-roundtrip').text("HW round trip: " + mspQueue.getHardwareRoundtrip().toFixed(0));
$('#drop-rate').text("Drop ratio: " + mspQueue.getDropRatio().toFixed(0) + "%");
}, 100);
helper.interval.add('global_data_refresh', helper.periodicStatusUpdater.run, helper.periodicStatusUpdater.getUpdateInterval(CONFIGURATOR.connection.bitrate), false);
}
interval.add('global_data_refresh', periodicStatusUpdater.run, periodicStatusUpdater.getUpdateInterval(CONFIGURATOR.connection.bitrate), false);
}
function onClosed(result) {
privateScope.onClosed = function (result) {
if (result) { // All went as expected
GUI.log(chrome.i18n.getMessage('serialPortClosedOk'));
GUI.log(i18n.getMessage('serialPortClosedOk'));
} else { // Something went wrong
GUI.log(chrome.i18n.getMessage('serialPortClosedFail'));
GUI.log(i18n.getMessage('serialPortClosedFail'));
}
$('.mode-connected').hide();
@ -462,43 +497,43 @@ function onClosed(result) {
$('#dataflash_wrapper_global').hide();
$('#quad-status_wrapper').hide();
updateFirmwareVersion();
}
//updateFirmwareVersion();
}
function read_serial(info) {
publicScope.read_serial = function (info) {
if (!CONFIGURATOR.cliActive) {
MSP.read(info);
} else if (CONFIGURATOR.cliActive) {
TABS.cli.read(info);
}
}
}
/**
/**
* Sensor handler used in INAV >= 1.5
* @param hw_status
*/
function sensor_status_ex(hw_status)
{
var statusHash = sensor_status_hash(hw_status);
privateScope.sensor_status_ex = function (hw_status)
{
var statusHash = privateScope.sensor_status_hash(hw_status);
if (sensor_status_ex.previousHash == statusHash) {
if (privateScope.sensor_status_ex.previousHash == statusHash) {
return;
}
sensor_status_ex.previousHash = statusHash;
privateScope.sensor_status_ex.previousHash = statusHash;
sensor_status_update_icon('.gyro', '.gyroicon', hw_status.gyroHwStatus);
sensor_status_update_icon('.accel', '.accicon', hw_status.accHwStatus);
sensor_status_update_icon('.mag', '.magicon', hw_status.magHwStatus);
sensor_status_update_icon('.baro', '.baroicon', hw_status.baroHwStatus);
sensor_status_update_icon('.gps', '.gpsicon', hw_status.gpsHwStatus);
sensor_status_update_icon('.sonar', '.sonaricon', hw_status.rangeHwStatus);
sensor_status_update_icon('.airspeed', '.airspeedicon', hw_status.speedHwStatus);
sensor_status_update_icon('.opflow', '.opflowicon', hw_status.flowHwStatus);
}
privateScope.sensor_status_update_icon('.gyro', '.gyroicon', hw_status.gyroHwStatus);
privateScope.sensor_status_update_icon('.accel', '.accicon', hw_status.accHwStatus);
privateScope.sensor_status_update_icon('.mag', '.magicon', hw_status.magHwStatus);
privateScope.sensor_status_update_icon('.baro', '.baroicon', hw_status.baroHwStatus);
privateScope.sensor_status_update_icon('.gps', '.gpsicon', hw_status.gpsHwStatus);
privateScope.sensor_status_update_icon('.sonar', '.sonaricon', hw_status.rangeHwStatus);
privateScope.sensor_status_update_icon('.airspeed', '.airspeedicon', hw_status.speedHwStatus);
privateScope.sensor_status_update_icon('.opflow', '.opflowicon', hw_status.flowHwStatus);
}
function sensor_status_update_icon(sensId, sensIconId, status)
{
privateScope.sensor_status_update_icon = function (sensId, sensIconId, status)
{
var e_sensor_status = $('#sensor-status');
if (status == 0) {
@ -516,10 +551,10 @@ function sensor_status_update_icon(sensId, sensIconId, status)
$(sensIconId, e_sensor_status).removeClass('active');
$(sensIconId, e_sensor_status).addClass('error');
}
}
}
function sensor_status_hash(hw_status)
{
privateScope.sensor_status_hash = function (hw_status)
{
return "S" +
hw_status.isHardwareHealthy +
hw_status.gyroHwStatus +
@ -530,115 +565,55 @@ function sensor_status_hash(hw_status)
hw_status.rangeHwStatus +
hw_status.speedHwStatus +
hw_status.flowHwStatus;
}
}
/**
/**
* Legacy sensor handler used in INAV < 1.5 versions
* @param sensors_detected
* @deprecated
*/
function sensor_status(sensors_detected) {
privateScope.sensor_status = function (sensors_detected) {
if (typeof SENSOR_STATUS === 'undefined') {
return;
}
SENSOR_STATUS.isHardwareHealthy = 1;
SENSOR_STATUS.gyroHwStatus = have_sensor(sensors_detected, 'gyro') ? 1 : 0;
SENSOR_STATUS.accHwStatus = have_sensor(sensors_detected, 'acc') ? 1 : 0;
SENSOR_STATUS.magHwStatus = have_sensor(sensors_detected, 'mag') ? 1 : 0;
SENSOR_STATUS.baroHwStatus = have_sensor(sensors_detected, 'baro') ? 1 : 0;
SENSOR_STATUS.gpsHwStatus = have_sensor(sensors_detected, 'gps') ? 1 : 0;
SENSOR_STATUS.rangeHwStatus = have_sensor(sensors_detected, 'sonar') ? 1 : 0;
SENSOR_STATUS.speedHwStatus = have_sensor(sensors_detected, 'airspeed') ? 1 : 0;
SENSOR_STATUS.flowHwStatus = have_sensor(sensors_detected, 'opflow') ? 1 : 0;
sensor_status_ex(SENSOR_STATUS);
}
SENSOR_STATUS.gyroHwStatus = publicScope.have_sensor(sensors_detected, 'gyro') ? 1 : 0;
SENSOR_STATUS.accHwStatus = publicScope.have_sensor(sensors_detected, 'acc') ? 1 : 0;
SENSOR_STATUS.magHwStatus = publicScope.have_sensor(sensors_detected, 'mag') ? 1 : 0;
SENSOR_STATUS.baroHwStatus = publicScope.have_sensor(sensors_detected, 'baro') ? 1 : 0;
SENSOR_STATUS.gpsHwStatus = publicScope.have_sensor(sensors_detected, 'gps') ? 1 : 0;
SENSOR_STATUS.rangeHwStatus = publicScope.have_sensor(sensors_detected, 'sonar') ? 1 : 0;
SENSOR_STATUS.speedHwStatus = publicScope.have_sensor(sensors_detected, 'airspeed') ? 1 : 0;
SENSOR_STATUS.flowHwStatus = publicScope.have_sensor(sensors_detected, 'opflow') ? 1 : 0;
privateScope.sensor_status_ex(SENSOR_STATUS);
}
function have_sensor(sensors_detected, sensor_code) {
publicScope.have_sensor = function (sensors_detected, sensor_code) {
switch(sensor_code) {
case 'acc':
case 'gyro':
return bit_check(sensors_detected, 0);
return BitHelper.bit_check(sensors_detected, 0);
case 'baro':
return bit_check(sensors_detected, 1);
return BitHelper.bit_check(sensors_detected, 1);
case 'mag':
return bit_check(sensors_detected, 2);
return BitHelper.bit_check(sensors_detected, 2);
case 'gps':
return bit_check(sensors_detected, 3);
return BitHelper.bit_check(sensors_detected, 3);
case 'sonar':
return bit_check(sensors_detected, 4);
return BitHelper.bit_check(sensors_detected, 4);
case 'opflow':
return bit_check(sensors_detected, 5);
return BitHelper.bit_check(sensors_detected, 5);
case 'airspeed':
return bit_check(sensors_detected, 6);
return BitHelper.bit_check(sensors_detected, 6);
}
return false;
}
function highByte(num) {
return num >> 8;
}
function lowByte(num) {
return 0x00FF & num;
}
function specificByte(num, pos) {
return 0x000000FF & (num >> (8 * pos));
}
function bit_check(num, bit) {
return ((num >> bit) % 2 != 0);
}
function bit_set(num, bit) {
return num | 1 << bit;
}
function bit_clear(num, bit) {
return num & ~(1 << bit);
}
function update_dataflash_global() {
function formatFilesize(bytes) {
if (bytes < 1024) {
return bytes + "B";
}
var kilobytes = bytes / 1024;
if (kilobytes < 1024) {
return Math.round(kilobytes) + "kB";
}
var megabytes = kilobytes / 1024;
return megabytes.toFixed(1) + "MB";
}
return publicScope;
var supportsDataflash = DATAFLASH.totalSize > 0;
})();
if (supportsDataflash){
$(".noflash_global").css({
display: 'none'
});
$(".dataflash-contents_global").css({
display: 'block'
});
$(".dataflash-free_global").css({
width: (100-(DATAFLASH.totalSize - DATAFLASH.usedSize) / DATAFLASH.totalSize * 100) + "%",
display: 'block'
});
$(".dataflash-free_global div").text('Dataflash: free ' + formatFilesize(DATAFLASH.totalSize - DATAFLASH.usedSize));
} else {
$(".noflash_global").css({
display: 'block'
});
$(".dataflash-contents_global").css({
display: 'none'
});
}
}
module.exports = SerialBackend;

Some files were not shown because too many files have changed in this diff Show more