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:
commit
82ede9c7fc
170 changed files with 18075 additions and 13033 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -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
21
.vscode/launch.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
82
README.md
82
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
theme: jekyll-theme-slate
|
|
@ -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
|
BIN
assets/windows/background.jpg
Normal file
BIN
assets/windows/background.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
assets/windows/banner.jpg
Normal file
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 |
|
@ -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
162
assets/windows/wix.xml
Normal 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>
|
117
eventPage.js
117
eventPage.js
|
@ -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
113
forge.config.js
Normal 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/",
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
754
gulpfile.js
754
gulpfile.js
|
@ -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
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
12
main.html → index.html
Executable file → Normal 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>
|
|
@ -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;
|
||||
|
|
|
@ -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
34
js/bitHelper.js
Normal 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;
|
|
@ -59,3 +59,6 @@ BOARD.findDefinition = function (identifier) {
|
|||
}
|
||||
return DEFAULT_BOARD_DEFINITION;
|
||||
};
|
||||
|
||||
module.exports = BOARD;
|
||||
|
||||
|
|
622
js/configurator_main.js
Normal file
622
js/configurator_main.js
Normal 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) + "}";
|
||||
});
|
||||
};
|
||||
|
|
@ -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};
|
|
@ -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;
|
||||
|
|
32
js/connection/connectionFactory.js
Normal file
32
js/connection/connectionFactory.js
Normal 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;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -10,3 +10,5 @@ var CONFIGURATOR = {
|
|||
'cliValid': false,
|
||||
'connection': false
|
||||
};
|
||||
|
||||
module.exports = CONFIGURATOR;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
306
js/fc.js
|
@ -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;
|
|
@ -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;
|
|
@ -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 };
|
||||
|
|
|
@ -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
26
js/globalSettings.js
Normal 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
40
js/globalUpdates.js
Normal 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;
|
||||
|
|
@ -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;
|
|
@ -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
101
js/gui.js
|
@ -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 };
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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;
|
18
js/libraries/bluetooth-device-chooser/index.html
Normal file
18
js/libraries/bluetooth-device-chooser/index.html
Normal 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>
|
6
js/libraries/bluetooth-device-chooser/preload.js
Normal file
6
js/libraries/bluetooth-device-chooser/preload.js
Normal 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)
|
||||
});
|
23
js/libraries/bluetooth-device-chooser/renderer.js
Normal file
23
js/libraries/bluetooth-device-chooser/renderer.js
Normal 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();
|
||||
});
|
||||
});
|
38
js/libraries/bluetooth-device-chooser/style.css
Normal file
38
js/libraries/bluetooth-device-chooser/style.css
Normal 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;
|
||||
}
|
|
@ -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++;
|
||||
|
|
|
@ -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); })()
|
|
@ -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 */
|
||||
|
|
3
js/libraries/jbox/jBox.min.js
vendored
3
js/libraries/jbox/jBox.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
49
js/libraries/jbox/themes/jBox.NoticeFancy.css
Normal file
49
js/libraries/jbox/themes/jBox.NoticeFancy.css
Normal 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 */
|
1
js/libraries/jbox/themes/jBox.NoticeFancy.min.css
vendored
Normal file
1
js/libraries/jbox/themes/jBox.NoticeFancy.min.css
vendored
Normal 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}
|
39
js/libraries/jbox/themes/jBox.TooltipBorder.css
Normal file
39
js/libraries/jbox/themes/jBox.TooltipBorder.css
Normal 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 */
|
1
js/libraries/jbox/themes/jBox.TooltipBorder.min.css
vendored
Normal file
1
js/libraries/jbox/themes/jBox.TooltipBorder.min.css
vendored
Normal 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}
|
32
js/libraries/jbox/themes/jBox.TooltipBorderThick.css
Normal file
32
js/libraries/jbox/themes/jBox.TooltipBorderThick.css
Normal 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 */
|
1
js/libraries/jbox/themes/jBox.TooltipBorderThick.min.css
vendored
Normal file
1
js/libraries/jbox/themes/jBox.TooltipBorderThick.min.css
vendored
Normal 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}
|
25
js/libraries/jbox/themes/TooltipDark.css → js/libraries/jbox/themes/jBox.TooltipDark.css
Executable file → Normal file
25
js/libraries/jbox/themes/TooltipDark.css → js/libraries/jbox/themes/jBox.TooltipDark.css
Executable file → Normal 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 */
|
1
js/libraries/jbox/themes/jBox.TooltipDark.min.css
vendored
Normal file
1
js/libraries/jbox/themes/jBox.TooltipDark.min.css
vendored
Normal 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}
|
54
js/libraries/jbox/themes/jBox.TooltipError.css
Normal file
54
js/libraries/jbox/themes/jBox.TooltipError.css
Normal 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 */
|
1
js/libraries/jbox/themes/jBox.TooltipError.min.css
vendored
Normal file
1
js/libraries/jbox/themes/jBox.TooltipError.min.css
vendored
Normal 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}
|
49
js/libraries/jbox/themes/jBox.TooltipSmall.css
Normal file
49
js/libraries/jbox/themes/jBox.TooltipSmall.css
Normal 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 */
|
1
js/libraries/jbox/themes/jBox.TooltipSmall.min.css
vendored
Normal file
1
js/libraries/jbox/themes/jBox.TooltipSmall.min.css
vendored
Normal 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}
|
53
js/libraries/jbox/themes/jBox.TooltipSmallGray.css
Normal file
53
js/libraries/jbox/themes/jBox.TooltipSmallGray.css
Normal 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 */
|
1
js/libraries/jbox/themes/jBox.TooltipSmallGray.min.css
vendored
Normal file
1
js/libraries/jbox/themes/jBox.TooltipSmallGray.min.css
vendored
Normal 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}
|
|
@ -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;
|
||||
}
|
||||
}
|
2
js/libraries/plotly-latest.min.js
vendored
2
js/libraries/plotly-latest.min.js
vendored
File diff suppressed because one or more lines are too long
4001
js/libraries/three/GLTFLoader.js
Normal file
4001
js/libraries/three/GLTFLoader.js
Normal file
File diff suppressed because it is too large
Load diff
1079
js/libraries/three/OrbitControls.js
Normal file
1079
js/libraries/three/OrbitControls.js
Normal file
File diff suppressed because it is too large
Load diff
6
js/libraries/three/three.min.js
vendored
Normal file
6
js/libraries/three/three.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -23,3 +23,5 @@ let LogicConditionsStatus = function () {
|
|||
|
||||
return self;
|
||||
};
|
||||
|
||||
module.exports = LogicConditionsStatus;
|
|
@ -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
166
js/main.js
Normal 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();
|
||||
}
|
||||
});
|
367
js/model.js
367
js/model.js
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
65
js/msp.js
65
js/msp.js
|
@ -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;
|
||||
|
|
|
@ -242,3 +242,5 @@ var MSPCodes = {
|
|||
MSP2_INAV_SET_SERVO_CONFIG: 0x2201,
|
||||
|
||||
};
|
||||
|
||||
module.exports = MSPCodes;
|
1913
js/msp/MSPHelper.js
1913
js/msp/MSPHelper.js
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
/*global $*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var MSPChainerClass = function () {
|
||||
|
@ -33,3 +33,5 @@ var MSPChainerClass = function () {
|
|||
|
||||
return self;
|
||||
};
|
||||
|
||||
module.exports = MSPChainerClass;
|
|
@ -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
36
js/mwnp.js
Normal 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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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 ;
|
|
@ -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;
|
|
@ -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 };
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue