mirror of
https://github.com/betaflight/betaflight-configurator.git
synced 2025-07-16 12:55:14 +03:00
Presets TAB
This commit is contained in:
parent
41747c65b6
commit
e63a5fe642
39 changed files with 3375 additions and 19 deletions
|
@ -31,6 +31,9 @@
|
||||||
"noticeTitle": {
|
"noticeTitle": {
|
||||||
"message": "Notice"
|
"message": "Notice"
|
||||||
},
|
},
|
||||||
|
"dontShowAgain": {
|
||||||
|
"message": "Don't show again"
|
||||||
|
},
|
||||||
"operationNotSupported": {
|
"operationNotSupported": {
|
||||||
"message": "This operation is not supported by your hardware."
|
"message": "This operation is not supported by your hardware."
|
||||||
},
|
},
|
||||||
|
@ -68,6 +71,9 @@
|
||||||
"close": {
|
"close": {
|
||||||
"message": "Close"
|
"message": "Close"
|
||||||
},
|
},
|
||||||
|
"OK": {
|
||||||
|
"message": "OK"
|
||||||
|
},
|
||||||
"cancel": {
|
"cancel": {
|
||||||
"message": "Cancel"
|
"message": "Cancel"
|
||||||
},
|
},
|
||||||
|
@ -86,6 +92,9 @@
|
||||||
"permanentExpertMode": {
|
"permanentExpertMode": {
|
||||||
"message": "Permanently enable Expert Mode"
|
"message": "Permanently enable Expert Mode"
|
||||||
},
|
},
|
||||||
|
"warningSettings": {
|
||||||
|
"message": "Show warnings"
|
||||||
|
},
|
||||||
"rememberLastTab": {
|
"rememberLastTab": {
|
||||||
"message": "Reopen last tab on connect"
|
"message": "Reopen last tab on connect"
|
||||||
},
|
},
|
||||||
|
@ -6205,5 +6214,205 @@
|
||||||
},
|
},
|
||||||
"cordovaExitAppMessage": {
|
"cordovaExitAppMessage": {
|
||||||
"message": "Do you really want to close the configurator?"
|
"message": "Do you really want to close the configurator?"
|
||||||
|
},
|
||||||
|
"dropDownSelectAll": {
|
||||||
|
"message": "[Select all]",
|
||||||
|
"description": "Select all item in the drop down/multiple select"
|
||||||
|
},
|
||||||
|
"dropDownAll": {
|
||||||
|
"message": "All",
|
||||||
|
"description": "Text indicating everything is selected in the drop down/multiple select"
|
||||||
|
},
|
||||||
|
"tabPresets": {
|
||||||
|
"message": "Presets",
|
||||||
|
"description": "Presets tab title"
|
||||||
|
},
|
||||||
|
"presetsReload": {
|
||||||
|
"message": "Reload",
|
||||||
|
"description": "Text on the reload button that appears when there in an error loading presets index"
|
||||||
|
},
|
||||||
|
"presetsAuthor": {
|
||||||
|
"message": "Author:",
|
||||||
|
"description": "Hint text in the presets detailed dialog"
|
||||||
|
},
|
||||||
|
"presetsKeywords": {
|
||||||
|
"message": "Keywords:",
|
||||||
|
"description": "Hint text in the presets detailed dialog"
|
||||||
|
},
|
||||||
|
"presetsVersions": {
|
||||||
|
"message": "Firmware:",
|
||||||
|
"description": "Hint text in the presets detailed dialog"
|
||||||
|
},
|
||||||
|
"presetsOfficial": {
|
||||||
|
"message": "Official",
|
||||||
|
"description": "Hint text in the presets detailed dialog indication preset is official"
|
||||||
|
},
|
||||||
|
"presetsCommunity": {
|
||||||
|
"message": "Community",
|
||||||
|
"description": "Hint text in the presets detailed dialog indication preset is not official but community"
|
||||||
|
},
|
||||||
|
"presetsExperimental": {
|
||||||
|
"message": "Experimental",
|
||||||
|
"description": "Hint text in the presets detailed dialog indication preset is not official but experimental"
|
||||||
|
},
|
||||||
|
"presetsApply": {
|
||||||
|
"message": "Pick",
|
||||||
|
"description": "Button to pick a preset"
|
||||||
|
},
|
||||||
|
"presetsViewOnline": {
|
||||||
|
"message": "View online…",
|
||||||
|
"description": "Link text for opening preset file online"
|
||||||
|
},
|
||||||
|
"presetsOpenDiscussion": {
|
||||||
|
"message": "Discussion…",
|
||||||
|
"description": "Link text for opening preset discussion"
|
||||||
|
},
|
||||||
|
"presetsShowCli": {
|
||||||
|
"message": "Show CLI",
|
||||||
|
"description": "Button to show CLI code of a preset"
|
||||||
|
},
|
||||||
|
"presetsHideCli": {
|
||||||
|
"message": "Hide CLI",
|
||||||
|
"description": "Button to hide CLI code of a preset"
|
||||||
|
},
|
||||||
|
"presetsOptions": {
|
||||||
|
"message": "Options",
|
||||||
|
"description": "Text label for Options drop down select"
|
||||||
|
},
|
||||||
|
"presetsFilterCategory": {
|
||||||
|
"message": "Categories",
|
||||||
|
"description": "UI filter name"
|
||||||
|
},
|
||||||
|
"presetsFilterKeyword": {
|
||||||
|
"message": "Keywords",
|
||||||
|
"description": "UI filter name"
|
||||||
|
},
|
||||||
|
"presetsFilterAuthor": {
|
||||||
|
"message": "Authors",
|
||||||
|
"description": "UI filter name"
|
||||||
|
},
|
||||||
|
"presetsFilterFirmware": {
|
||||||
|
"message": "Firmwares",
|
||||||
|
"description": "UI filter name"
|
||||||
|
},
|
||||||
|
"presetsFilterStatus": {
|
||||||
|
"message": "Status",
|
||||||
|
"description": "UI filter name - official/community/experimental"
|
||||||
|
},
|
||||||
|
"presetsLoadError": {
|
||||||
|
"message": "Error loading presets from the internet",
|
||||||
|
"description": "Error report when failed to load presets index or a specific preset"
|
||||||
|
},
|
||||||
|
"presetsButtonSave": {
|
||||||
|
"message": "Save and Reboot",
|
||||||
|
"description": "A button that saves all appied presets - analog to 'save' command in CLI"
|
||||||
|
},
|
||||||
|
"presetsButtonCancel": {
|
||||||
|
"message": "Cancel",
|
||||||
|
"description": "A button that restarts FC without saving appied presets - analog to 'exit' command in CLI"
|
||||||
|
},
|
||||||
|
"presetsApplyingPresets": {
|
||||||
|
"message": "Applying configuration...",
|
||||||
|
"description": "First label in the progress dialog when applying configuration (presets or user config)"
|
||||||
|
},
|
||||||
|
"presetsPleaseWait": {
|
||||||
|
"message": "Please wait.",
|
||||||
|
"description": "Second label in the progress dialog when applying presets"
|
||||||
|
},
|
||||||
|
"presetsCliErrorsWaring": {
|
||||||
|
"message": "<span class='message-negative'>WARNING!</span><br/>Configuration has been applied with CLI errors.",
|
||||||
|
"description": "Text to show when there are CLI errors after applying presets or user configuration"
|
||||||
|
},
|
||||||
|
"presetsSaveAnyway": {
|
||||||
|
"message": "Save anyway",
|
||||||
|
"description": "Save anyway button on the CLI errors presets dialog"
|
||||||
|
},
|
||||||
|
"presetsWarningDialogTitle": {
|
||||||
|
"message": "WARNING!",
|
||||||
|
"description": "Warning title in the warning dialog in the presets"
|
||||||
|
},
|
||||||
|
"presetsWarningDialogYesButton": {
|
||||||
|
"message": "Agree",
|
||||||
|
"description": "Agree button in the presets warning dialog"
|
||||||
|
},
|
||||||
|
"presetsWarningDialogNoButton": {
|
||||||
|
"message": "Cancel",
|
||||||
|
"description": "Cancel button in the presets warning dialog"
|
||||||
|
},
|
||||||
|
"presetsWiki": {
|
||||||
|
"message": "Presets Wiki",
|
||||||
|
"description": "Button to open Presets Wiki link"
|
||||||
|
},
|
||||||
|
"presetsBackupSave": {
|
||||||
|
"message": "Save backup",
|
||||||
|
"description": "Button to backup current configuration to file"
|
||||||
|
},
|
||||||
|
"presetsBackupLoad": {
|
||||||
|
"message": "Load backup",
|
||||||
|
"description": "Button to load backup from the file"
|
||||||
|
},
|
||||||
|
"presetsLoadingDumpAll": {
|
||||||
|
"message": "Loading current configuration from the flight controller",
|
||||||
|
"description": "Title for the waiting dialog when loading dump all into a file"
|
||||||
|
},
|
||||||
|
"dumpAllNotSavedWarning": {
|
||||||
|
"message": "Error occured while saving current configuration",
|
||||||
|
"description": "Message appears on presets tab when saving current diff all into a file has failed"
|
||||||
|
},
|
||||||
|
"presetSources": {
|
||||||
|
"message": "Preset sources...",
|
||||||
|
"description": "A button to show preset sources dialog"
|
||||||
|
},
|
||||||
|
"presetsSourcesDialogTitle": {
|
||||||
|
"message": "Preset sources",
|
||||||
|
"description": "A button to show preset sources dialog"
|
||||||
|
},
|
||||||
|
"presetsSourcesDialogAddNew": {
|
||||||
|
"message": "Add new source",
|
||||||
|
"description": "A button to show preset sources dialog"
|
||||||
|
},
|
||||||
|
"presetsSourcesDialogDefaultSourceName": {
|
||||||
|
"message": "New Custom Preset Source",
|
||||||
|
"description": "A default preset source (repo) name"
|
||||||
|
},
|
||||||
|
"presetsSourcesDialogSaveSource": {
|
||||||
|
"message": "Save",
|
||||||
|
"description": "Presets tab, sources dialog, button to save new or editable source"
|
||||||
|
},
|
||||||
|
"presetsSourcesDialogResetSource": {
|
||||||
|
"message": "Reset",
|
||||||
|
"description": "Presets tab, sources dialog, button to reset source after modifications"
|
||||||
|
},
|
||||||
|
"presetsSourcesDialogMakeSourceActive": {
|
||||||
|
"message": "Make Active",
|
||||||
|
"description": "Presets tab, sources dialog, button to make selected source active"
|
||||||
|
},
|
||||||
|
"presetsSourcesDialogDeleteSource": {
|
||||||
|
"message": "Delete",
|
||||||
|
"description": "Presets tab, sources dialog, button to delete selected source"
|
||||||
|
},
|
||||||
|
"presetsWarningNotOfficialSource": {
|
||||||
|
"message": "<span class=\"message-negative\">WARNING!</span> A third party preset source is selected.",
|
||||||
|
"description": "Warning message that shows up when a third party preset source is selected"
|
||||||
|
},
|
||||||
|
"presetsWarningBackup": {
|
||||||
|
"message": "Please make sure you backup your current configuration ('$t(presetsBackupSave.message)' button or via CLI if the button is disabled) <strong>before</strong> picking and applying presets. Otherwise there is no way to return to previous configuration after applying presets.",
|
||||||
|
"description": "Warning message that shows up at the top of the presets tab"
|
||||||
|
},
|
||||||
|
"presets_sources_dialog_warning": {
|
||||||
|
"message": "<span class=\"message-negative\">WARNING!</span> Using third party preset sources could be dangerous.<br/>Make sure you add and use only trusted sources. Malicious or bad preset sources will break your drone configuration and can potentially harm your devices.",
|
||||||
|
"description": "Warning message that shows up at the top of the preset sources dialog"
|
||||||
|
},
|
||||||
|
"presetsWarningWrongVersionConfirmation": {
|
||||||
|
"message": "Picked preset requires firmware version $1<br/> Current firmware version is $2",
|
||||||
|
"description": "Warning message that shows up at the top of the preset sources dialog"
|
||||||
|
},
|
||||||
|
"presetsNoPresetsFound": {
|
||||||
|
"message": "No presets found for the given search paramteres",
|
||||||
|
"description": "Message that apprears on presets tab if no presets were found"
|
||||||
|
},
|
||||||
|
"presetsTooManyPresetsFound": {
|
||||||
|
"message": "Reached the maximum limit of the shown presets number",
|
||||||
|
"description": "Message that apprears on presets tab if too many presets found"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
"jquery-ui-npm": "^1.12.0",
|
"jquery-ui-npm": "^1.12.0",
|
||||||
"lru_map": "^0.3.3",
|
"lru_map": "^0.3.3",
|
||||||
"marked": "^0.8.0",
|
"marked": "^0.8.0",
|
||||||
|
"multiple-select": "^1.5.2",
|
||||||
"nw-vue-devtools-prebuilt": "^0.0.10",
|
"nw-vue-devtools-prebuilt": "^0.0.10",
|
||||||
"object-hash": "^2.0.3",
|
"object-hash": "^2.0.3",
|
||||||
"select2": "^4.0.13",
|
"select2": "^4.0.13",
|
||||||
|
@ -125,7 +126,7 @@
|
||||||
"temp": "^0.9.1",
|
"temp": "^0.9.1",
|
||||||
"vinyl-source-stream": "^2.0.0",
|
"vinyl-source-stream": "^2.0.0",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"vue-template-compiler": "^2.6.12",
|
||||||
"yarn": "^1.22.0"
|
"yarn": "^1.22.17"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"gulp-appdmg": "^1.0.3"
|
"gulp-appdmg": "^1.0.3"
|
||||||
|
|
|
@ -111,6 +111,11 @@ dialog {
|
||||||
border: 1px solid #ffbb2a;
|
border: 1px solid #ffbb2a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.standard_input {
|
||||||
|
background: var(--boxBackground);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
#quad-status_wrapper {
|
#quad-status_wrapper {
|
||||||
color: #393b3a;
|
color: #393b3a;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,18 @@ a.disabled {
|
||||||
background-position: center;
|
background-position: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.standard_input {
|
||||||
|
padding-left: 3px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
border: 1px solid var(--subtleAccent);
|
||||||
|
background: var(--boxBackground);
|
||||||
|
}
|
||||||
|
|
||||||
/* Help-Icon */
|
/* Help-Icon */
|
||||||
.helpicon {
|
.helpicon {
|
||||||
float: right;
|
float: right;
|
||||||
|
@ -740,8 +752,19 @@ input[type="number"]::-webkit-inner-spin-button {
|
||||||
background-image: url(../images/icons/cf_icon_link_active.svg);
|
background-image: url(../images/icons/cf_icon_link_active.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Header (not phones) **/
|
||||||
|
@media not all and (max-width: 575px) {
|
||||||
|
.visible-on-phone-only {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Header (phones) **/
|
/** Header (phones) **/
|
||||||
@media all and (max-width: 575px) {
|
@media all and (max-width: 575px) {
|
||||||
|
.visible-on-desktop-only {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.headerbar {
|
.headerbar {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
background: rgba(0, 0, 0, 0.15);
|
background: rgba(0, 0, 0, 0.15);
|
||||||
|
@ -1541,10 +1564,67 @@ dialog .dialog_toolbar .btn a.disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialogYesNo .dialogYesNoContent {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-top: 12px;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogYesNo .dialogYesNo-yesButton, .dialogYesNo .dialogYesNo-noButton {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogYesNo .dialogYesNo-yesButton {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogYesNo {
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogWait {
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 500px;
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogWait .data-loading {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogWait .dialogWaitTitle {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogInformation .dialogInformationContent {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-top: 12px;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogInformation .dialogInformation-confirmButton {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogInformation {
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (max-width: 575px) {
|
@media all and (max-width: 575px) {
|
||||||
dialog {
|
dialog {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: calc(100% - 2em) !important;
|
width: calc(100% - 2em - 2px) !important; /* 2px - border */
|
||||||
|
max-width: unset;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
top: 56px;
|
top: 56px;
|
||||||
|
@ -2348,7 +2428,6 @@ input {
|
||||||
.noUi-connect {
|
.noUi-connect {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Responsive grid **/
|
/** Responsive grid **/
|
||||||
@media all and (max-width: 575px) {
|
@media all and (max-width: 575px) {
|
||||||
.sm, .md, .lg, .xl {
|
.sm, .md, .lg, .xl {
|
||||||
|
|
|
@ -200,7 +200,7 @@
|
||||||
cursor: default;
|
cursor: default;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #AFAFAF;
|
background-color: #AFAFAF;
|
||||||
border: none;
|
border: 1px solid #AFAFAF;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
}
|
}
|
||||||
.tab-options .margin-bottom {
|
.tab-options .margin-bottom {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: fit-content(300px) 1fr;
|
||||||
}
|
}
|
||||||
.tab-options select {
|
.tab-options select {
|
||||||
background: var(--boxBackground);
|
background: var(--boxBackground);
|
||||||
|
|
6
src/images/icons/cf_icon_check_orange.svg
Normal file
6
src/images/icons/cf_icon_check_orange.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M 498.25 249.524 C 498.25 386.831 386.941 498.14 249.634 498.14 C 112.327 498.14 1.018 386.831 1.018 249.524 C 1.018 112.217 112.327 0.908 249.634 0.908 C 322.597 0.908 388.219 32.339 433.7 82.399 C 334.451 142.436 269.55 216.455 213.055 312.593 L 127.757 151.63 L 54.042 218.642 L 216.387 447.914 C 305.243 286.114 350.541 188.665 450.938 103.594 C 480.699 144.577 498.25 195.001 498.25 249.524 Z" style="fill: rgb(255, 187, 0); fill-rule: nonzero; paint-order: fill;"/>
|
||||||
|
<path d="M 498.75 249.524 C 498.75 318.28 470.839 380.622 425.786 425.676 C 380.732 470.729 318.39 498.64 249.634 498.64 C 180.878 498.64 118.536 470.729 73.482 425.676 C 28.429 380.622 0.518 318.28 0.518 249.524 C 0.518 180.768 28.429 118.426 73.482 73.372 C 118.536 28.319 180.878 0.408 249.634 0.408 C 318.39 0.408 380.732 28.319 425.786 73.372 C 470.839 118.426 498.75 180.768 498.75 249.524 Z M 425.079 74.079 C 380.151 29.152 318.185 1.408 249.634 1.408 C 181.083 1.408 119.117 29.152 74.189 74.079 C 29.262 119.007 1.518 180.973 1.518 249.524 C 1.518 318.075 29.262 380.041 74.189 424.969 C 119.117 469.896 181.083 497.64 249.634 497.64 C 318.185 497.64 380.151 469.896 425.079 424.969 C 470.006 380.041 497.75 318.075 497.75 249.524 C 497.75 180.973 470.006 119.007 425.079 74.079 Z" style="fill: none;"/>
|
||||||
|
<path d="M 498.75 249.524 C 498.75 318.28 470.839 380.622 425.786 425.676 C 380.732 470.729 318.39 498.64 249.634 498.64 C 180.878 498.64 118.536 470.729 73.482 425.676 C 28.429 380.622 0.518 318.28 0.518 249.524 C 0.518 180.768 28.429 118.426 73.482 73.372 C 118.536 28.319 180.878 0.408 249.634 0.408 C 318.39 0.408 380.732 28.319 425.786 73.372 C 470.839 118.426 498.75 180.768 498.75 249.524 Z M 425.079 74.079 C 380.151 29.152 318.185 1.408 249.634 1.408 C 181.083 1.408 119.117 29.152 74.189 74.079 C 29.262 119.007 1.518 180.973 1.518 249.524 C 1.518 318.075 29.262 380.041 74.189 424.969 C 119.117 469.896 181.083 497.64 249.634 497.64 C 318.185 497.64 380.151 469.896 425.079 424.969 C 470.006 380.041 497.75 318.075 497.75 249.524 C 497.75 180.973 470.006 119.007 425.079 74.079 Z" style="fill: none;"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
5
src/images/icons/cf_icon_search_orange.svg
Normal file
5
src/images/icons/cf_icon_search_orange.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M 499.204 185.669 C 499.204 287.561 416.604 370.161 314.712 370.161 C 212.82 370.161 130.22 287.561 130.22 185.669 C 130.22 83.777 212.82 1.177 314.712 1.177 C 416.604 1.177 499.204 83.777 499.204 185.669 Z M 314.712 48.304 C 238.662 48.304 177.012 109.954 177.012 186.004 C 177.012 262.054 238.662 323.704 314.712 323.704 C 390.762 323.704 452.412 262.054 452.412 186.004 C 452.412 109.954 390.762 48.304 314.712 48.304 Z" style="stroke-width: 50px; fill: rgb(255, 187, 0);"/>
|
||||||
|
<path d="M 4.046 435.582 L 146.371 297.269 C 161.05 321.35 181.091 341.803 204.835 356.971 L 63.129 494.665 L 4.046 435.582 Z" style="fill: rgb(255, 187, 0);"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 760 B |
22
src/images/icons/cf_icon_welcome_orange.svg
Normal file
22
src/images/icons/cf_icon_welcome_orange.svg
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="-70 0 141.7 141.7" style="enable-background:new -70 0 141.7 141.7;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFBB00;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M8.4,34.1c0,0,16.2,7.3,21.5,13.4c0,0-5.9-0.2-8.1,3.3c-2.6,3.8-1.3,11.4,6.8,17.1s17.7,6.3,22.5,6.6
|
||||||
|
c4.8,0.2,9.6,1.3,10.6,3.5C61.4,78.1,62.9,51.6,8.4,34.1z M38.7,61.1c-1-4,2.2-5.5,6.6-3.8c4,1.8,10.6,12.1,10.6,12.1
|
||||||
|
C50.6,68.7,39.9,65.2,38.7,61.1z"/>
|
||||||
|
<path class="st0" d="M-68.6,10.4l9,19.7l-8.6-3l2.6,4.8c0,0,3.5,6.8,11.9,21.5c6.6,11.4,18.2,17.1,33.8,16.4c0.5,0,1.3,0,2,0
|
||||||
|
c9.6-0.5,31.6-3,32.6-3h0.2l10.3-4L-68.6,10.4z M-20.1,65.5c-5.5,0.2-10.6-0.5-14.9-2c-6.3-2.2-11.4-6.3-14.9-12.3
|
||||||
|
c-4.3-7.6-7.3-12.9-9-16.4l7.9,2.8L-59,20.8l74.2,40.9l-1.3,0.5c-0.5,0-1.8,0.2-3.8,0.5l-50-20.2L-2.2,64
|
||||||
|
C-9,64.4-16.4,65.2-20.1,65.5z"/>
|
||||||
|
<path class="st0" d="M24.8,59.4c0,0-31.6-8.3-26.8,16.4c3.1,16.2,21.3,6.8,27.9,0.7C32.4,70.5,27,60.2,24.8,59.4z"/>
|
||||||
|
<path class="st0" d="M41.5,96.8c-14.2-6.1-22.8-16-33.6-15.2C1.3,82.1-5.7,87.6-8.5,95c4,3.3,10.1,5.5,19.7,2c0,0-6.6,8.1-20.4,7.3
|
||||||
|
c0.2,1.8,1,3.3,1.8,5c1.3,2,3,4,5,5.5c5.9-0.7,13.9-3.5,21.7-12.1c0,0-0.7,9-7.9,17.1c3.8,0.5,7.9,0.5,12.1,0.2
|
||||||
|
c3.3-1.8,7.9-5,7.9-10.9c0,0,1.5,4.6-0.7,9.9c6.6-1.3,13.4-3.8,19.9-7.6C65.2,103,70,93.2,70,93.2S55.9,102.5,41.5,96.8z
|
||||||
|
M51.8,107.3c-0.7-2-1-6.1-1-6.1c4.6,0.5,10.1-1.5,10.1-1.5C57.2,104.6,51.8,107.3,51.8,107.3z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -5,30 +5,31 @@
|
||||||
const ConfigStorage = {
|
const ConfigStorage = {
|
||||||
// key can be one string, or array of strings
|
// key can be one string, or array of strings
|
||||||
get: function(key, callback) {
|
get: function(key, callback) {
|
||||||
|
let result = {};
|
||||||
if (Array.isArray(key)) {
|
if (Array.isArray(key)) {
|
||||||
let obj = {};
|
|
||||||
key.forEach(function (element) {
|
key.forEach(function (element) {
|
||||||
try {
|
try {
|
||||||
obj = {...obj, ...JSON.parse(window.localStorage.getItem(element))};
|
result = {...result, ...JSON.parse(window.localStorage.getItem(element))};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// is okay
|
// is okay
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
callback(obj);
|
callback?.(result);
|
||||||
} else {
|
} else {
|
||||||
const keyValue = window.localStorage.getItem(key);
|
const keyValue = window.localStorage.getItem(key);
|
||||||
if (keyValue) {
|
if (keyValue) {
|
||||||
let obj = {};
|
|
||||||
try {
|
try {
|
||||||
obj = JSON.parse(keyValue);
|
result = JSON.parse(keyValue);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// It's fine if we fail that parse
|
// It's fine if we fail that parse
|
||||||
}
|
}
|
||||||
callback(obj);
|
callback?.(result);
|
||||||
} else {
|
} else {
|
||||||
callback({});
|
callback?.(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
},
|
},
|
||||||
// set takes an object like {'userLanguageSelect':'DEFAULT'}
|
// set takes an object like {'userLanguageSelect':'DEFAULT'}
|
||||||
set: function(input) {
|
set: function(input) {
|
||||||
|
|
|
@ -30,6 +30,9 @@ const CONFIGURATOR = {
|
||||||
cliActive: false,
|
cliActive: false,
|
||||||
cliValid: false,
|
cliValid: false,
|
||||||
productName: 'Betaflight Configurator',
|
productName: 'Betaflight Configurator',
|
||||||
|
cliEngineActive: false,
|
||||||
|
cliEngineValid: false,
|
||||||
|
gitChangesetId: 'unknown',
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
gitRevision: 'norevision',
|
gitRevision: 'norevision',
|
||||||
latestVersion: '0.0.1',
|
latestVersion: '0.0.1',
|
||||||
|
|
163
src/js/gui.js
163
src/js/gui.js
|
@ -19,6 +19,7 @@ const GuiControl = function () {
|
||||||
this.operating_system = null;
|
this.operating_system = null;
|
||||||
this.interval_array = [];
|
this.interval_array = [];
|
||||||
this.timeout_array = [];
|
this.timeout_array = [];
|
||||||
|
this.buttonDisabledClass = "disabled";
|
||||||
|
|
||||||
this.defaultAllowedTabsWhenDisconnected = [
|
this.defaultAllowedTabsWhenDisconnected = [
|
||||||
'landing',
|
'landing',
|
||||||
|
@ -36,6 +37,7 @@ const GuiControl = function () {
|
||||||
'power',
|
'power',
|
||||||
'adjustments',
|
'adjustments',
|
||||||
'auxiliary',
|
'auxiliary',
|
||||||
|
'presets',
|
||||||
'cli',
|
'cli',
|
||||||
'configuration',
|
'configuration',
|
||||||
'gps',
|
'gps',
|
||||||
|
@ -407,5 +409,166 @@ GuiControl.prototype.isOther = function () {
|
||||||
return this.Mode === GUI_MODES.Other;
|
return this.Mode === GUI_MODES.Other;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
GuiControl.prototype.showYesNoDialog = function(yesNoDialogSettings) {
|
||||||
|
// yesNoDialogSettings:
|
||||||
|
// title, text, buttonYesText, buttonNoText, buttonYesCallback, buttonNoCallback
|
||||||
|
const dialog = $(".dialogYesNo");
|
||||||
|
const title = dialog.find(".dialogYesNoTitle");
|
||||||
|
const content = dialog.find(".dialogYesNoContent");
|
||||||
|
const buttonYes = dialog.find(".dialogYesNo-yesButton");
|
||||||
|
const buttonNo = dialog.find(".dialogYesNo-noButton");
|
||||||
|
|
||||||
|
title.html(yesNoDialogSettings.title);
|
||||||
|
content.html(yesNoDialogSettings.text);
|
||||||
|
buttonYes.html(yesNoDialogSettings.buttonYesText);
|
||||||
|
buttonNo.html(yesNoDialogSettings.buttonNoText);
|
||||||
|
|
||||||
|
buttonYes.off("click");
|
||||||
|
buttonNo.off("click");
|
||||||
|
|
||||||
|
buttonYes.on("click", () => {
|
||||||
|
dialog[0].close();
|
||||||
|
yesNoDialogSettings.buttonYesCallback?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonNo.on("click", () => {
|
||||||
|
dialog[0].close();
|
||||||
|
yesNoDialogSettings.buttonNoCallback?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog[0].showModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
GuiControl.prototype.showWaitDialog = function(waitDialogSettings) {
|
||||||
|
// waitDialogSettings:
|
||||||
|
// title, buttonCancelCallback
|
||||||
|
const dialog = $(".dialogWait")[0];
|
||||||
|
const title = $(".dialogWaitTitle");
|
||||||
|
const buttonCancel = $(".dialogWait-cancelButton");
|
||||||
|
|
||||||
|
title.html(waitDialogSettings.title);
|
||||||
|
buttonCancel.toggle(!!waitDialogSettings.buttonCancelCallback);
|
||||||
|
|
||||||
|
buttonCancel.off("click");
|
||||||
|
|
||||||
|
buttonCancel.on("click", () => {
|
||||||
|
dialog.close();
|
||||||
|
waitDialogSettings.buttonCancelCallback?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.showModal();
|
||||||
|
return dialog;
|
||||||
|
};
|
||||||
|
|
||||||
|
GuiControl.prototype.showInformationDialog = function(informationDialogSettings) {
|
||||||
|
// informationDialogSettings:
|
||||||
|
// title, text, buttonConfirmText
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const dialog = $(".dialogInformation");
|
||||||
|
const title = dialog.find(".dialogInformationTitle");
|
||||||
|
const content = dialog.find(".dialogInformationContent");
|
||||||
|
const buttonConfirm = dialog.find(".dialogInformation-confirmButton");
|
||||||
|
|
||||||
|
title.html(informationDialogSettings.title);
|
||||||
|
content.html(informationDialogSettings.text);
|
||||||
|
buttonConfirm.html(informationDialogSettings.buttonConfirmText);
|
||||||
|
|
||||||
|
buttonConfirm.off("click");
|
||||||
|
|
||||||
|
buttonConfirm.on("click", () => {
|
||||||
|
dialog[0].close();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog[0].showModal();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
GuiControl.prototype.saveToTextFileDialog = function(textToSave, suggestedFileName, extension) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const accepts = [{ description: extension.toUpperCase() + ' files', extensions: [extension] }];
|
||||||
|
|
||||||
|
chrome.fileSystem.chooseEntry(
|
||||||
|
{
|
||||||
|
type: 'saveFile',
|
||||||
|
suggestedName: suggestedFileName,
|
||||||
|
accepts: accepts,
|
||||||
|
},
|
||||||
|
entry => this._saveToTextFileDialogFileSelected(entry, textToSave, resolve, reject),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
GuiControl.prototype._saveToTextFileDialogFileSelected = function(entry, textToSave, resolve, reject) {
|
||||||
|
checkChromeRuntimeError();
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
console.log('No file selected for saving');
|
||||||
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.createWriter(writer => {
|
||||||
|
writer.onerror = () => {
|
||||||
|
reject();
|
||||||
|
console.error('Failed to write file');
|
||||||
|
};
|
||||||
|
|
||||||
|
writer.onwriteend = () => {
|
||||||
|
if (textToSave.length > 0 && writer.length === 0) {
|
||||||
|
writer.write(new Blob([textToSave], {type: 'text/plain'}));
|
||||||
|
} else {
|
||||||
|
resolve(true);
|
||||||
|
console.log('File write complete');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
writer.truncate(0);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
reject();
|
||||||
|
console.error('Failed to get file writer');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
GuiControl.prototype.readTextFileDialog = function(extension) {
|
||||||
|
const accepts = [{ description: extension.toUpperCase() + ' files', extensions: [extension] }];
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
chrome.fileSystem.chooseEntry({type: 'openFile', accepts: accepts}, function(entry) {
|
||||||
|
checkChromeRuntimeError();
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
console.log('No file selected for loading');
|
||||||
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.file((file) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => resolve(reader.result);
|
||||||
|
reader.onerror = () => {
|
||||||
|
console.error(reader.error);
|
||||||
|
reject();
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
GuiControl.prototype.escapeHtml = function(unsafe) {
|
||||||
|
return unsafe
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
};
|
||||||
|
|
||||||
// initialize object into GUI variable
|
// initialize object into GUI variable
|
||||||
window.GUI = new GuiControl();
|
window.GUI = new GuiControl();
|
||||||
|
|
|
@ -401,6 +401,9 @@ function startProcess() {
|
||||||
case 'cli':
|
case 'cli':
|
||||||
TABS.cli.initialize(content_ready, GUI.nwGui);
|
TABS.cli.initialize(content_ready, GUI.nwGui);
|
||||||
break;
|
break;
|
||||||
|
case 'presets':
|
||||||
|
TABS.presets.initialize(content_ready, GUI.nwGui);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log(`Tab not found: ${tab}`);
|
console.log(`Tab not found: ${tab}`);
|
||||||
|
|
|
@ -583,13 +583,17 @@ function onClosed(result) {
|
||||||
CONFIGURATOR.connectionValid = false;
|
CONFIGURATOR.connectionValid = false;
|
||||||
CONFIGURATOR.cliValid = false;
|
CONFIGURATOR.cliValid = false;
|
||||||
CONFIGURATOR.cliActive = false;
|
CONFIGURATOR.cliActive = false;
|
||||||
|
CONFIGURATOR.cliEngineValid = false;
|
||||||
|
CONFIGURATOR.cliEngineActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function read_serial(info) {
|
function read_serial(info) {
|
||||||
if (!CONFIGURATOR.cliActive) {
|
if (CONFIGURATOR.cliActive) {
|
||||||
MSP.read(info);
|
|
||||||
} else if (CONFIGURATOR.cliActive) {
|
|
||||||
TABS.cli.read(info);
|
TABS.cli.read(info);
|
||||||
|
} else if (CONFIGURATOR.cliEngineActive) {
|
||||||
|
TABS.presets.read(info);
|
||||||
|
} else {
|
||||||
|
MSP.read(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +698,8 @@ function update_live_status() {
|
||||||
display: 'inline-block'
|
display: 'inline-block'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (GUI.active_tab != 'cli') {
|
if (GUI.active_tab !== 'cli' && GUI.active_tab !== 'presets') {
|
||||||
|
MSP.send_message(MSPCodes.MSP_BOXNAMES, false, false);
|
||||||
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_32)) {
|
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_32)) {
|
||||||
MSP.send_message(MSPCodes.MSP_STATUS_EX, false, false);
|
MSP.send_message(MSPCodes.MSP_STATUS_EX, false, false);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,6 +18,8 @@ options.initialize = function (callback) {
|
||||||
TABS.options.initCordovaForceComputerUI();
|
TABS.options.initCordovaForceComputerUI();
|
||||||
TABS.options.initDarkTheme();
|
TABS.options.initDarkTheme();
|
||||||
|
|
||||||
|
TABS.options.initShowWarnings();
|
||||||
|
|
||||||
GUI.content_ready(callback);
|
GUI.content_ready(callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -28,6 +30,19 @@ options.cleanup = function (callback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
options.initShowWarnings = function () {
|
||||||
|
ConfigStorage.get('showPresetsWarningBackup', function (result) {
|
||||||
|
if (result.showPresetsWarningBackup) {
|
||||||
|
$('div.presetsWarningBackup input').prop('checked', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('div.presetsWarningBackup input').change(function () {
|
||||||
|
const checked = $(this).is(':checked');
|
||||||
|
ConfigStorage.set({'showPresetsWarningBackup': checked});
|
||||||
|
}).change();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
options.initPermanentExpertMode = function () {
|
options.initPermanentExpertMode = function () {
|
||||||
ConfigStorage.get('permanentExpertMode', function (result) {
|
ConfigStorage.get('permanentExpertMode', function (result) {
|
||||||
if (result.permanentExpertMode) {
|
if (result.permanentExpertMode) {
|
||||||
|
|
|
@ -22,6 +22,11 @@
|
||||||
<link type="text/css" rel="stylesheet" href="./css/tabs/led_strip.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./css/tabs/led_strip.css" media="all"/>
|
||||||
<link type="text/css" rel="stylesheet" href="./css/tabs/sensors.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./css/tabs/sensors.css" media="all"/>
|
||||||
<link type="text/css" rel="stylesheet" href="./css/tabs/cli.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./css/tabs/cli.css" media="all"/>
|
||||||
|
<link type="text/css" rel="stylesheet" href="./tabs/presets/presets.css" media="all"/>
|
||||||
|
<link type="text/css" rel="stylesheet" href="./tabs/presets/TitlePanel/PresetTitlePanel.css" media="all"/>
|
||||||
|
<link type="text/css" rel="stylesheet" href="./tabs/presets/DetailedDialog/PresetsDetailedDialog.css" media="all"/>
|
||||||
|
<link type="text/css" rel="stylesheet" href="./tabs/presets/SourcesDialog/SourcesDialog.css" media="all"/>
|
||||||
|
<link type="text/css" rel="stylesheet" href="./tabs/presets/SourcesDialog/SourcePanel.css" media="all"/>
|
||||||
<link type="text/css" rel="stylesheet" href="./css/tabs/logging.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./css/tabs/logging.css" media="all"/>
|
||||||
<link type="text/css" rel="stylesheet" href="./css/tabs/onboard_logging.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./css/tabs/onboard_logging.css" media="all"/>
|
||||||
<link type="text/css" rel="stylesheet" href="./css/tabs/firmware_flasher.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./css/tabs/firmware_flasher.css" media="all"/>
|
||||||
|
@ -42,6 +47,7 @@
|
||||||
<link type="text/css" rel="stylesheet" href="./components/MotorOutputReordering/Styles.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./components/MotorOutputReordering/Styles.css" media="all"/>
|
||||||
<link type="text/css" rel="stylesheet" href="./css/select2_custom.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./css/select2_custom.css" media="all"/>
|
||||||
<link type="text/css" rel="stylesheet" href="./node_modules/select2/dist/css/select2.min.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./node_modules/select2/dist/css/select2.min.css" media="all"/>
|
||||||
|
<link type="text/css" rel="stylesheet" href="./node_modules/multiple-select/dist/multiple-select.min.css" media="all"/>
|
||||||
<link type="text/css" rel="stylesheet" href="./components/EscDshotDirection/Styles.css" media="all"/>
|
<link type="text/css" rel="stylesheet" href="./components/EscDshotDirection/Styles.css" media="all"/>
|
||||||
|
|
||||||
<link type="text/css" rel="stylesheet" href="./css/dark-theme.css" media="all" disabled/>
|
<link type="text/css" rel="stylesheet" href="./css/dark-theme.css" media="all" disabled/>
|
||||||
|
@ -126,6 +132,18 @@
|
||||||
<script type="text/javascript" src="./js/tabs/cli.js"></script>
|
<script type="text/javascript" src="./js/tabs/cli.js"></script>
|
||||||
<!-- TODO: might be removed when everythign is in modules -->
|
<!-- TODO: might be removed when everythign is in modules -->
|
||||||
<script type="module" src="./js/tabs/logging.js"></script>
|
<script type="module" src="./js/tabs/logging.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/presets.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/CliEngine.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/PickedPreset.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/DetailedDialog/PresetsDetailedDialog.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/TitlePanel/PresetTitlePanel.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/PresetsRepoIndexed/PresetsRepoIndexed.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/PresetsRepoIndexed/PresetsGithubRepo.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/PresetsRepoIndexed/PresetsWebsiteRepo.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/SourcesDialog/SourcesDialog.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/SourcesDialog/SourcePanel.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/presets/SourcesDialog/PresetSource.js"></script>
|
||||||
|
<script type="text/javascript" src="./js/tabs/logging.js"></script>
|
||||||
<script type="text/javascript" src="./js/tabs/onboard_logging.js"></script>
|
<script type="text/javascript" src="./js/tabs/onboard_logging.js"></script>
|
||||||
<script type="text/javascript" src="./js/FirmwareCache.js"></script>
|
<script type="text/javascript" src="./js/FirmwareCache.js"></script>
|
||||||
<script type="text/javascript" src="./js/tabs/failsafe.js"></script>
|
<script type="text/javascript" src="./js/tabs/failsafe.js"></script>
|
||||||
|
@ -145,6 +163,7 @@
|
||||||
<script type="text/javascript" src="./components/MotorOutputReordering/MotorOutputReorderingCanvas.js"></script>
|
<script type="text/javascript" src="./components/MotorOutputReordering/MotorOutputReorderingCanvas.js"></script>
|
||||||
<script type="text/javascript" src="./components/MotorOutputReordering/MotorOutputReorderingConfig.js"></script>
|
<script type="text/javascript" src="./components/MotorOutputReordering/MotorOutputReorderingConfig.js"></script>
|
||||||
<script type="text/javascript" src="./node_modules/select2/dist/js/select2.min.js"></script>
|
<script type="text/javascript" src="./node_modules/select2/dist/js/select2.min.js"></script>
|
||||||
|
<script type="text/javascript" src="./node_modules/multiple-select/dist/multiple-select.min.js"></script>
|
||||||
<script type="text/javascript" src="./js/utils/EscProtocols.js"></script>
|
<script type="text/javascript" src="./js/utils/EscProtocols.js"></script>
|
||||||
<script type="text/javascript" src="./js/utils/DshotCommand.js"></script>
|
<script type="text/javascript" src="./js/utils/DshotCommand.js"></script>
|
||||||
<script type="text/javascript" src="./components/EscDshotDirection/EscDshotDirectionComponent.js"></script>
|
<script type="text/javascript" src="./components/EscDshotDirection/EscDshotDirectionComponent.js"></script>
|
||||||
|
@ -326,6 +345,9 @@
|
||||||
i18n_title="tabPower"></a></li>
|
i18n_title="tabPower"></a></li>
|
||||||
<li class="tab_failsafe"><a href="#" i18n="tabFailsafe" class="tabicon ic_failsafe"
|
<li class="tab_failsafe"><a href="#" i18n="tabFailsafe" class="tabicon ic_failsafe"
|
||||||
i18n_title="tabFailsafe"></a></li>
|
i18n_title="tabFailsafe"></a></li>
|
||||||
|
<li class="tab_presets">
|
||||||
|
<a href="#" i18n="tabPresets" class="tabicon ic_wizzard" i18n_title="tabPresets"></a>
|
||||||
|
</li>
|
||||||
<li class="tab_pid_tuning"><a href="#" i18n="tabPidTuning" class="tabicon ic_pid"
|
<li class="tab_pid_tuning"><a href="#" i18n="tabPidTuning" class="tabicon ic_pid"
|
||||||
i18n_title="tabPidTuning"></a></li>
|
i18n_title="tabPidTuning"></a></li>
|
||||||
<li class="tab_receiver"><a href="#" i18n="tabReceiver" class="tabicon ic_rx" i18n_title="tabReceiver"></a></li>
|
<li class="tab_receiver"><a href="#" i18n="tabReceiver" class="tabicon ic_rx" i18n_title="tabReceiver"></a></li>
|
||||||
|
@ -462,6 +484,30 @@
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog class="dialogYesNo">
|
||||||
|
<h3 class="dialogYesNoTitle"></h3>
|
||||||
|
<div class="dialogYesNoContent"></div>
|
||||||
|
<div class="buttons">
|
||||||
|
<a href="#" class="dialogYesNo-yesButton regular-button"></a>
|
||||||
|
<a href="#" class="dialogYesNo-noButton regular-button"></a>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog class="dialogWait">
|
||||||
|
<div class="data-loading"></div>
|
||||||
|
<h3 class="dialogWaitTitle"></h3>
|
||||||
|
<div class="buttons">
|
||||||
|
<a href="#" class="dialogWait-cancelButton regular-button" i18n="cancel"></a>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog class="dialogInformation">
|
||||||
|
<h3 class="dialogInformationTitle"></h3>
|
||||||
|
<div class="dialogInformationContent"></div>
|
||||||
|
<div class="buttons">
|
||||||
|
<a href="#" class="dialogInformation-confirmButton regular-button"></a>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
<!-- CORDOVA_INCLUDE cordova.js -->
|
<!-- CORDOVA_INCLUDE cordova.js -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -62,5 +62,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="gui_box">
|
||||||
|
<div class="gui_box_titlebar">
|
||||||
|
<div class="spacer_box_title" i18n="warningSettings"></div>
|
||||||
|
</div>
|
||||||
|
<div class="spacer">
|
||||||
|
<div class="presetsWarningBackup margin-bottom">
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" class="toggle" />
|
||||||
|
</div>
|
||||||
|
<span class="freelabel" i18n="presetsWarningBackup"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
275
src/tabs/presets/CliEngine.js
Normal file
275
src/tabs/presets/CliEngine.js
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class CliEngine
|
||||||
|
{
|
||||||
|
constructor(currentTab) {
|
||||||
|
this._currentTab = currentTab;
|
||||||
|
this._lineDelayMs = 15;
|
||||||
|
this._profileSwitchDelayMs = 100;
|
||||||
|
this._cliBuffer = "";
|
||||||
|
this._window = null;
|
||||||
|
this._windowWrapper = null;
|
||||||
|
this._cliErrorsCount = 0;
|
||||||
|
this._sendCommandsProgress = 0;
|
||||||
|
this._onSendCommandsProgressChange = undefined;
|
||||||
|
this._responseCallback = undefined;
|
||||||
|
this._onRowCameCallback = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
setUi(window, windowWrapper, textarea) {
|
||||||
|
this._window = window;
|
||||||
|
this._windowWrapper = windowWrapper;
|
||||||
|
this._setTextareaListen(textarea);
|
||||||
|
}
|
||||||
|
|
||||||
|
get errorsCount() { return this._cliErrorsCount; }
|
||||||
|
|
||||||
|
setProgressCallback(sendCommandsProgressCallBack) {
|
||||||
|
this._onSendCommandsProgressChange = sendCommandsProgressCallBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
_reportSendCommandsProgress(value) {
|
||||||
|
this._sendCommandsProgress = value;
|
||||||
|
this._onSendCommandsProgressChange?.(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
enterCliMode() {
|
||||||
|
const bufferOut = new ArrayBuffer(1);
|
||||||
|
const bufView = new Uint8Array(bufferOut);
|
||||||
|
this.cliBuffer = "";
|
||||||
|
|
||||||
|
bufView[0] = 0x23;
|
||||||
|
|
||||||
|
serial.send(bufferOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setTextareaListen(textarea) {
|
||||||
|
// Tab key detection must be on keydown,
|
||||||
|
// `keypress`/`keyup` happens too late, as `textarea` will have already lost focus.
|
||||||
|
textarea.keydown(event => {
|
||||||
|
if (event.which === CliEngine.s_tabCode) {
|
||||||
|
// prevent default tabbing behaviour
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
textarea.keypress(event => {
|
||||||
|
if (event.which === CliEngine.s_enterKeyCode) {
|
||||||
|
event.preventDefault(); // prevent the adding of new line
|
||||||
|
const outString = textarea.val();
|
||||||
|
this.executeCommands(outString);
|
||||||
|
textarea.val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// give input element user focus
|
||||||
|
textarea.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
close(callback) {
|
||||||
|
this.send(this.getCliCommand('exit\r', ""), function () { //this.cliBuffer
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
executeCommands(outString) {
|
||||||
|
const outputArray = outString.split("\n");
|
||||||
|
return this.executeCommandsArray(outputArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
executeCommandsArray(strings) {
|
||||||
|
this._reportSendCommandsProgress(0);
|
||||||
|
const totalCommandsCount = strings.length;
|
||||||
|
|
||||||
|
return Promise.reduce(strings, (delay, line, index) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
GUI.timeout_add('CLI_send_slowly', () => {
|
||||||
|
let processingDelay = this.lineDelayMs;
|
||||||
|
line = line.trim();
|
||||||
|
|
||||||
|
if (line.toLowerCase().startsWith('profile')) {
|
||||||
|
processingDelay = this.profileSwitchDelayMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLastCommand = totalCommandsCount === index + 1;
|
||||||
|
|
||||||
|
if (isLastCommand && this.cliBuffer) {
|
||||||
|
line = this.getCliCommand(line, this.cliBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendLine(line, ()=>{ /* empty on-send callback */ }, () => {
|
||||||
|
resolve(processingDelay);
|
||||||
|
this._reportSendCommandsProgress(100.0 * index / totalCommandsCount);
|
||||||
|
});
|
||||||
|
}, delay);
|
||||||
|
});
|
||||||
|
}, 0).then(() => {
|
||||||
|
this._reportSendCommandsProgress(100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
removePromptHash(promptText) {
|
||||||
|
return promptText.replace(/^# /, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
cliBufferCharsToDelete(command, buffer) {
|
||||||
|
let commonChars = 0;
|
||||||
|
for (let i = 0; i < buffer.length; i++) {
|
||||||
|
if (command[i] === buffer[i]) {
|
||||||
|
commonChars++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer.length - commonChars;
|
||||||
|
}
|
||||||
|
|
||||||
|
commandWithBackSpaces(command, buffer, noOfCharsToDelete) {
|
||||||
|
const backspace = String.fromCharCode(127);
|
||||||
|
return backspace.repeat(noOfCharsToDelete) + command.substring(buffer.length - noOfCharsToDelete, command.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCliCommand(command, cliBuffer) {
|
||||||
|
const buffer = this.removePromptHash(cliBuffer);
|
||||||
|
const bufferRegex = new RegExp('^' + buffer, 'g');
|
||||||
|
|
||||||
|
if (command.match(bufferRegex)) {
|
||||||
|
return command.replace(bufferRegex, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
const noOfCharsToDelete = this.cliBufferCharsToDelete(command, buffer);
|
||||||
|
return this.commandWithBackSpaces(command, buffer, noOfCharsToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeToOutput(text) {
|
||||||
|
this._windowWrapper.append(text);
|
||||||
|
this._window.scrollTop(this._windowWrapper.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
writeLineToOutput(text) {
|
||||||
|
if (text.startsWith("###ERROR")) {
|
||||||
|
this.writeToOutput(`<span class="error_message">${text}</span><br>`);
|
||||||
|
this._cliErrorsCount++;
|
||||||
|
} else {
|
||||||
|
this.writeToOutput(text + "<br>");
|
||||||
|
}
|
||||||
|
this._responseCallback?.();
|
||||||
|
this._onRowCameCallback?.(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeOnRowCame(callback) {
|
||||||
|
this._onRowCameCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsubscribeOnRowCame() {
|
||||||
|
this._onRowCameCallback = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
readSerial(readInfo) {
|
||||||
|
/* Some info about handling line feeds and carriage return
|
||||||
|
|
||||||
|
line feed = LF = \n = 0x0A = 10
|
||||||
|
carriage return = CR = \r = 0x0D = 13
|
||||||
|
|
||||||
|
MAC only understands CR
|
||||||
|
Linux and Unix only understand LF
|
||||||
|
Windows understands (both) CRLF
|
||||||
|
Chrome OS currently unknown
|
||||||
|
*/
|
||||||
|
const data = new Uint8Array(readInfo.data);
|
||||||
|
let validateText = "";
|
||||||
|
let sequenceCharsToSkip = 0;
|
||||||
|
for (const charCode of data) {
|
||||||
|
const currentChar = String.fromCharCode(charCode);
|
||||||
|
|
||||||
|
if (!CONFIGURATOR.cliEngineValid) {
|
||||||
|
// try to catch part of valid CLI enter message
|
||||||
|
validateText += currentChar;
|
||||||
|
this.writeToOutput(currentChar);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const escapeSequenceCode = 27;
|
||||||
|
const escapeSequenceCharLength = 3;
|
||||||
|
if (charCode === escapeSequenceCode && !sequenceCharsToSkip) { // ESC + other
|
||||||
|
sequenceCharsToSkip = escapeSequenceCharLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sequenceCharsToSkip) {
|
||||||
|
sequenceCharsToSkip--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._adjustCliBuffer(charCode);
|
||||||
|
|
||||||
|
if (this.cliBuffer === 'Rebooting' && CliEngine.s_backspaceCode !== charCode) {
|
||||||
|
CONFIGURATOR.cliEngineActive = false;
|
||||||
|
CONFIGURATOR.cliEngineValid = false;
|
||||||
|
GUI.log(i18n.getMessage('cliReboot'));
|
||||||
|
reinitialiseConnection(this._currentTab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CONFIGURATOR.cliEngineValid && validateText.indexOf('CLI') !== -1) {
|
||||||
|
GUI.log(i18n.getMessage('cliEnter'));
|
||||||
|
CONFIGURATOR.cliEngineValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendLine(line, callback, responseCallback) {
|
||||||
|
this.send(line + '\n', callback, responseCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
send(line, callback, responseCallback) {
|
||||||
|
this._responseCallback = responseCallback;
|
||||||
|
const bufferOut = new ArrayBuffer(line.length);
|
||||||
|
const bufView = new Uint8Array(bufferOut);
|
||||||
|
|
||||||
|
for (let cKey = 0; cKey < line.length; cKey++) {
|
||||||
|
bufView[cKey] = line.charCodeAt(cKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
serial.send(bufferOut, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
_adjustCliBuffer(newCharacterCode) {
|
||||||
|
const currentChar = String.fromCharCode(newCharacterCode);
|
||||||
|
switch (newCharacterCode) {
|
||||||
|
case CliEngine.s_lineFeedCode:
|
||||||
|
if (GUI.operating_system === "Windows") {
|
||||||
|
this.writeLineToOutput(this.cliBuffer);
|
||||||
|
this.cliBuffer = "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CliEngine.s_carriageReturnCode:
|
||||||
|
if (GUI.operating_system !== "Windows") {
|
||||||
|
this.writeLineToOutput(this.cliBuffer);
|
||||||
|
this.cliBuffer = "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
this.cliBuffer += '<';
|
||||||
|
break;
|
||||||
|
case 62:
|
||||||
|
this.cliBuffer += '>';
|
||||||
|
break;
|
||||||
|
case CliEngine.s_backspaceCode:
|
||||||
|
this.cliBuffer = this.cliBuffer.slice(0, -1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.cliBuffer += currentChar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CliEngine.s_backspaceCode = 8;
|
||||||
|
CliEngine.s_lineFeedCode = 10;
|
||||||
|
CliEngine.s_carriageReturnCode = 13;
|
||||||
|
CliEngine.s_tabCode = 9;
|
||||||
|
CliEngine.s_enterKeyCode = 13;
|
||||||
|
CliEngine.s_commandDiffAll = "diff all";
|
||||||
|
CliEngine.s_commandDefaultsNoSave = "defaults nosave";
|
||||||
|
CliEngine.s_commandSave = "save";
|
||||||
|
CliEngine.s_commandExit = "exit";
|
112
src/tabs/presets/DetailedDialog/PresetsDetailedDialog.css
Normal file
112
src/tabs/presets/DetailedDialog/PresetsDetailedDialog.css
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
.preset_detailed_dialog_title {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog_content_wrapper .preset_title_panel_title {
|
||||||
|
padding-bottom: .5ex;
|
||||||
|
border-bottom: 1px solid var(--accent);
|
||||||
|
margin-bottom: 2ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets-detailed-dialog-property-table {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets-detailed-dialog-property-table td {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets-detailed-dialog-property-table-first-col {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_detailed_dialog_text {
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
margin-top: 12px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 270px;
|
||||||
|
font-size: 110%;
|
||||||
|
white-space: pre-line;
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog_properties {
|
||||||
|
height: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog_content_wrapper .left-panel {
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog_loading {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_options_select {
|
||||||
|
width: 296px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_options_panel {
|
||||||
|
height: 26px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_options_panel > span{
|
||||||
|
line-height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preset_options_label {
|
||||||
|
width : 100px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 575px) {
|
||||||
|
.presets_detailed_dialog_text {
|
||||||
|
height: unset;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog .content_toolbar {
|
||||||
|
position: fixed;
|
||||||
|
height: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_options_panel {
|
||||||
|
height: 26px;
|
||||||
|
margin-top: 6px;
|
||||||
|
grid-template-columns: 100px 1fr;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_options_select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog .btn {
|
||||||
|
margin-left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog .btn .left-panel {
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog .btn .left-panel .regular-button {
|
||||||
|
float: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog .btn .regular-button:not(#presets_detailed_dialog .btn .left-panel .regular-button) {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
}
|
28
src/tabs/presets/DetailedDialog/PresetsDetailedDialog.html
Normal file
28
src/tabs/presets/DetailedDialog/PresetsDetailedDialog.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<div id="presets_detailed_dialog_content_wrapper">
|
||||||
|
<div id="presets_detailed_dialog_content">
|
||||||
|
<div id="presets_detailed_dialog_properties">
|
||||||
|
<div class="preset_detailed_dialog_title_panel"></div>
|
||||||
|
<div id="presets_options_panel">
|
||||||
|
<span id="preset_options_label" i18n="presetsOptions"></span>
|
||||||
|
<select multiple="multiple" id="presets_options_select"></select>
|
||||||
|
</div>
|
||||||
|
<div class="presets_detailed_dialog_text" id="presets_detailed_dialog_text_description"></div>
|
||||||
|
<div class="presets_detailed_dialog_text" id="presets_detailed_dialog_text_cli"></div>
|
||||||
|
</div>
|
||||||
|
<div id = "presets_detailed_dialog_loading" class="data-loading"></div>
|
||||||
|
<div id = "presets_detailed_dialog_error"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content_toolbar">
|
||||||
|
<div class="btn">
|
||||||
|
<div class="left-panel">
|
||||||
|
<a id="presets_cli_show" i18n="presetsShowCli" class="tool regular-button" target="_blank" rel="noopener noreferrer"></a>
|
||||||
|
<a id="presets_cli_hide" i18n="presetsHideCli" class="tool regular-button" target="_blank" rel="noopener noreferrer"></a>
|
||||||
|
<a id="presets_open_online" i18n="presetsViewOnline" class="tool regular-button" target="_blank" rel="noopener noreferrer"></a>
|
||||||
|
<a id="presets_open_discussion" i18n="presetsOpenDiscussion" class="tool regular-button" target="_blank" rel="noopener noreferrer"></a>
|
||||||
|
</div>
|
||||||
|
<a href="#" id="presets_detailed_dialog_applybtn" class="tool regular-button" i18n="presetsApply"></a>
|
||||||
|
<a href="#" id="presets_detailed_dialog_closebtn" class="tool regular-button" i18n="close"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
223
src/tabs/presets/DetailedDialog/PresetsDetailedDialog.js
Normal file
223
src/tabs/presets/DetailedDialog/PresetsDetailedDialog.js
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class PresetsDetailedDialog {
|
||||||
|
constructor(domDialog, pickedPresetList, onPresetPickedCallback) {
|
||||||
|
this._domDialog = domDialog;
|
||||||
|
this._pickedPresetList = pickedPresetList;
|
||||||
|
this._finalDialogYesNoSettings = {};
|
||||||
|
this._onPresetPickedCallback = onPresetPickedCallback;
|
||||||
|
this._openPromiseResolve = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this._domDialog.load("./tabs/presets/DetailedDialog/PresetsDetailedDialog.html", () => {
|
||||||
|
this._setupdialog();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
open(preset, presetsRepo) {
|
||||||
|
this._presetsRepo = presetsRepo;
|
||||||
|
this._preset = preset;
|
||||||
|
this._setLoadingState(true);
|
||||||
|
this._domDialog[0].showModal();
|
||||||
|
|
||||||
|
this._presetsRepo.loadPreset(this._preset)
|
||||||
|
.then(() => {
|
||||||
|
this._loadPresetUi();
|
||||||
|
this._setLoadingState(false);
|
||||||
|
this._setFinalYesNoDialogSettings();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
const msg = i18n.getMessage("presetsLoadError");
|
||||||
|
this._showError(msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise(resolve => this._openPromiseResolve = resolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setFinalYesNoDialogSettings() {
|
||||||
|
this._finalDialogYesNoSettings = {
|
||||||
|
title: i18n.getMessage("presetsWarningDialogTitle"),
|
||||||
|
text: GUI.escapeHtml(this._preset.completeWarning),
|
||||||
|
buttonYesText: i18n.getMessage("presetsWarningDialogYesButton"),
|
||||||
|
buttonNoText: i18n.getMessage("presetsWarningDialogNoButton"),
|
||||||
|
buttonYesCallback: () => this._pickPresetFwVersionCheck(),
|
||||||
|
buttonNoCallback: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_getFinalCliText() {
|
||||||
|
const optionsToInclude = this._domOptionsSelect.multipleSelect("getSelects", "text");
|
||||||
|
return this._presetsRepo.removeUncheckedOptions(this._preset.originalPresetCliStrings, optionsToInclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadPresetUi() {
|
||||||
|
this._domDescription.text(this._preset.description?.join("\n"));
|
||||||
|
|
||||||
|
this._domGitHubLink.attr("href", this._presetsRepo.getPresetOnlineLink(this._preset));
|
||||||
|
|
||||||
|
if (this._preset.discussion) {
|
||||||
|
this._domDiscussionLink.removeClass(GUI.buttonDisabledClass);
|
||||||
|
this._domDiscussionLink.attr("href", this._preset.discussion);
|
||||||
|
} else{
|
||||||
|
this._domDiscussionLink.addClass(GUI.buttonDisabledClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._titlePanel.empty();
|
||||||
|
const titlePanel = new PresetTitlePanel(this._titlePanel, this._preset, false, () => this._setLoadingState(false));
|
||||||
|
titlePanel.load();
|
||||||
|
this._loadOptionsSelect();
|
||||||
|
this._updateFinalCliText();
|
||||||
|
this._showCliText(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateFinalCliText() {
|
||||||
|
this._domCliText.text(this._getFinalCliText().join("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
_setLoadingState(isLoading) {
|
||||||
|
this._domProperties.toggle(!isLoading);
|
||||||
|
this._domLoading.toggle(isLoading);
|
||||||
|
this._domError.toggle(false);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
this._domButtonApply.addClass(GUI.buttonDisabledClass);
|
||||||
|
} else {
|
||||||
|
this._domButtonApply.removeClass(GUI.buttonDisabledClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_showError(msg) {
|
||||||
|
this._domError.toggle(true);
|
||||||
|
this._domError.text(msg);
|
||||||
|
this._domProperties.toggle(false);
|
||||||
|
this._domLoading.toggle(false);
|
||||||
|
this._domButtonApply.addClass(GUI.buttonDisabledClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
_readDom() {
|
||||||
|
this._domButtonApply = $('#presets_detailed_dialog_applybtn');
|
||||||
|
this._domButtonCancel = $('#presets_detailed_dialog_closebtn');
|
||||||
|
this._domLoading = $('#presets_detailed_dialog_loading');
|
||||||
|
this._domError = $('#presets_detailed_dialog_error');
|
||||||
|
this._domProperties = $('#presets_detailed_dialog_properties');
|
||||||
|
this._titlePanel = $('.preset_detailed_dialog_title_panel');
|
||||||
|
this._domDescription = $('#presets_detailed_dialog_text_description');
|
||||||
|
this._domCliText = $('#presets_detailed_dialog_text_cli');
|
||||||
|
this._domGitHubLink = this._domDialog.find('#presets_open_online');
|
||||||
|
this._domDiscussionLink = this._domDialog.find('#presets_open_discussion');
|
||||||
|
this._domOptionsSelect = $('#presets_options_select');
|
||||||
|
this._domOptionsSelectPanel = $('#presets_options_panel');
|
||||||
|
this._domButtonCliShow = $('#presets_cli_show');
|
||||||
|
this._domButtonCliHide = $('#presets_cli_hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
_showCliText(value) {
|
||||||
|
this._domDescription.toggle(!value);
|
||||||
|
this._domCliText.toggle(value);
|
||||||
|
this._domButtonCliShow.toggle(!value);
|
||||||
|
this._domButtonCliHide.toggle(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createOptionsSelect(options) {
|
||||||
|
options.forEach(option => {
|
||||||
|
let selectedString = "selected=\"selected\"";
|
||||||
|
if (!option.checked) {
|
||||||
|
selectedString = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
this._domOptionsSelect.append(`<option value="${option.name}" ${selectedString}>${option.name}</option>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._domOptionsSelect.multipleSelect({
|
||||||
|
placeholder: i18n.getMessage("dropDownAll"),
|
||||||
|
formatSelectAll () { return i18n.getMessage("dropDownSelectAll"); },
|
||||||
|
formatAllSelected() { return i18n.getMessage("dropDownAll"); },
|
||||||
|
onClick: () => this._optionsSelectionChanged(),
|
||||||
|
onCheckAll: () => this._optionsSelectionChanged(),
|
||||||
|
onUncheckAll: () => this._optionsSelectionChanged(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_optionsSelectionChanged() {
|
||||||
|
this._updateFinalCliText();
|
||||||
|
}
|
||||||
|
|
||||||
|
_destroyOptionsSelect() {
|
||||||
|
this._domOptionsSelect.multipleSelect('destroy');
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadOptionsSelect() {
|
||||||
|
|
||||||
|
const optionsVisible = 0 !== this._preset.options.length;
|
||||||
|
this._domOptionsSelect.empty();
|
||||||
|
this._domOptionsSelectPanel.toggle(optionsVisible);
|
||||||
|
|
||||||
|
if (optionsVisible) {
|
||||||
|
this._createOptionsSelect(this._preset.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._domOptionsSelect.multipleSelect('refresh');
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupdialog() {
|
||||||
|
i18n.localizePage();
|
||||||
|
this._readDom();
|
||||||
|
|
||||||
|
this._domButtonApply.on("click", () => this._onApplyButtonClicked());
|
||||||
|
this._domButtonCancel.on("click", () => this._onCancelButtonClicked(false));
|
||||||
|
this._domButtonCliShow.on("click", () => this._showCliText(true));
|
||||||
|
this._domButtonCliHide.on("click", () => this._showCliText(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
_onApplyButtonClicked() {
|
||||||
|
if (!this._preset.completeWarning) {
|
||||||
|
this._pickPresetFwVersionCheck();
|
||||||
|
} else {
|
||||||
|
GUI.showYesNoDialog(this._finalDialogYesNoSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pickPreset() {
|
||||||
|
const cliStrings = this._getFinalCliText();
|
||||||
|
const pickedPreset = new PickedPreset(this._preset, cliStrings);
|
||||||
|
this._pickedPresetList.push(pickedPreset);
|
||||||
|
this._onPresetPickedCallback?.();
|
||||||
|
this._onCancelButtonClicked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pickPresetFwVersionCheck() {
|
||||||
|
let compatitable = false;
|
||||||
|
|
||||||
|
for (const fw of this._preset.firmware_version) {
|
||||||
|
if (FC.CONFIG.flightControllerVersion.startsWith(fw)) {
|
||||||
|
compatitable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compatitable) {
|
||||||
|
this._pickPreset();
|
||||||
|
} else {
|
||||||
|
const dialogSettings = {
|
||||||
|
title: i18n.getMessage("presetsWarningDialogTitle"),
|
||||||
|
text: i18n.getMessage("presetsWarningWrongVersionConfirmation", [this._preset.firmware_version, FC.CONFIG.flightControllerVersion]),
|
||||||
|
buttonYesText: i18n.getMessage("presetsWarningDialogYesButton"),
|
||||||
|
buttonNoText: i18n.getMessage("presetsWarningDialogNoButton"),
|
||||||
|
buttonYesCallback: () => this._pickPreset(),
|
||||||
|
buttonNoCallback: null,
|
||||||
|
};
|
||||||
|
GUI.showYesNoDialog(dialogSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onCancelButtonClicked(isPresetPicked) {
|
||||||
|
this._destroyOptionsSelect();
|
||||||
|
this._domDialog[0].close();
|
||||||
|
this._openPromiseResolve?.(isPresetPicked);
|
||||||
|
}
|
||||||
|
}
|
10
src/tabs/presets/PickedPreset.js
Normal file
10
src/tabs/presets/PickedPreset.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class PickedPreset
|
||||||
|
{
|
||||||
|
constructor(preset, presetCli)
|
||||||
|
{
|
||||||
|
this.preset = preset;
|
||||||
|
this.presetCli = presetCli;
|
||||||
|
}
|
||||||
|
}
|
26
src/tabs/presets/PresetsRepoIndexed/PresetsGithubRepo.js
Normal file
26
src/tabs/presets/PresetsRepoIndexed/PresetsGithubRepo.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class PresetsGithubRepo extends PresetsRepoIndexed {
|
||||||
|
constructor(urlRepo, branch) {
|
||||||
|
let correctUrlRepo = urlRepo.trim();
|
||||||
|
|
||||||
|
if (!correctUrlRepo.endsWith("/")) {
|
||||||
|
correctUrlRepo += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
let correctBranch = branch.trim();
|
||||||
|
|
||||||
|
if (correctBranch.startsWith("/")) {
|
||||||
|
correctBranch = correctBranch.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correctBranch.endsWith("/")) {
|
||||||
|
correctBranch = correctBranch.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlRaw = `https://raw.githubusercontent.com${correctUrlRepo.slice("https://github.com".length)}${correctBranch}/`;
|
||||||
|
const urlViewOnline = `${correctUrlRepo}blob/${correctBranch}/`;
|
||||||
|
|
||||||
|
super(urlRaw, urlViewOnline);
|
||||||
|
}
|
||||||
|
}
|
223
src/tabs/presets/PresetsRepoIndexed/PresetsRepoIndexed.js
Normal file
223
src/tabs/presets/PresetsRepoIndexed/PresetsRepoIndexed.js
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class PresetsRepoIndexed {
|
||||||
|
constructor(urlRaw, urlViewOnline) {
|
||||||
|
this._urlRaw = urlRaw;
|
||||||
|
this._urlViewOnline = urlViewOnline;
|
||||||
|
this._index = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get index() {
|
||||||
|
return this._index;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadIndex() {
|
||||||
|
return fetch(this._urlRaw + "index.json")
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(out => this._index = out);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeUncheckedOptions(strings, checkedOptions) {
|
||||||
|
let resultStrings = [];
|
||||||
|
let isCurrentOptionExcluded = false;
|
||||||
|
const lowerCasedCheckedOptions = checkedOptions.map(optionName => optionName.toLowerCase());
|
||||||
|
|
||||||
|
strings.forEach(str => {
|
||||||
|
if (this._isLineAttribute(str)) {
|
||||||
|
const line = this._removeAttributeDirective(str);
|
||||||
|
|
||||||
|
if (this._isOptionBegin(line)) {
|
||||||
|
const optionNameLowCase = this._getOptionName(line).toLowerCase();
|
||||||
|
|
||||||
|
if (!lowerCasedCheckedOptions.includes(optionNameLowCase)) {
|
||||||
|
isCurrentOptionExcluded = true;
|
||||||
|
}
|
||||||
|
} else if (this._isOptionEnd(line)) {
|
||||||
|
isCurrentOptionExcluded = false;
|
||||||
|
}
|
||||||
|
} else if (!isCurrentOptionExcluded) {
|
||||||
|
resultStrings.push(str);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resultStrings = this._removeExcessiveEmptyLines(resultStrings);
|
||||||
|
|
||||||
|
return resultStrings;
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeExcessiveEmptyLines(strings) {
|
||||||
|
// removes empty lines if there are two or more in a row leaving just one empty line
|
||||||
|
const result = [];
|
||||||
|
let lastStringEmpty = false;
|
||||||
|
|
||||||
|
strings.forEach(str => {
|
||||||
|
if ("" !== str || !lastStringEmpty) {
|
||||||
|
result.push(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("" === str) {
|
||||||
|
lastStringEmpty = true;
|
||||||
|
} else {
|
||||||
|
lastStringEmpty = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isLineAttribute(line) {
|
||||||
|
return line.trim().startsWith(PresetsRepoIndexed._sCliAttributeDirective);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isOptionBegin(line) {
|
||||||
|
const lowCaseLine = line.toLowerCase();
|
||||||
|
return lowCaseLine.startsWith(this._index.settings.OptionsDirectives.BEGIN_OPTION_DIRECTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isOptionEnd(line) {
|
||||||
|
const lowCaseLine = line.toLowerCase();
|
||||||
|
return lowCaseLine.startsWith(this._index.settings.OptionsDirectives.END_OPTION_DIRECTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getOptionName(line) {
|
||||||
|
const directiveRemoved = line.slice(this._index.settings.OptionsDirectives.BEGIN_OPTION_DIRECTIVE.length).trim();
|
||||||
|
const regExpRemoveChecked = new RegExp(this._escapeRegex(this._index.settings.OptionsDirectives.OPTION_CHECKED +":"), 'gi');
|
||||||
|
const regExpRemoveUnchecked = new RegExp(this._escapeRegex(this._index.settings.OptionsDirectives.OPTION_UNCHECKED +":"), 'gi');
|
||||||
|
let optionName = directiveRemoved.replace(regExpRemoveChecked, "");
|
||||||
|
optionName = optionName.replace(regExpRemoveUnchecked, "").trim();
|
||||||
|
return optionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
_escapeRegex(string) {
|
||||||
|
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeAttributeDirective(line) {
|
||||||
|
return line.trim().slice(PresetsRepoIndexed._sCliAttributeDirective.length).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
getPresetOnlineLink(preset) {
|
||||||
|
return this._urlViewOnline + preset.fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
_parceInclude(strings, includeRowIndexes, promises)
|
||||||
|
{
|
||||||
|
for (let i = 0; i < strings.length; i++) {
|
||||||
|
const match = PresetsRepoIndexed._sRegExpInclude.exec(strings[i].toLowerCase());
|
||||||
|
|
||||||
|
if (match !== null) {
|
||||||
|
includeRowIndexes.push(i);
|
||||||
|
const filePath = this._urlRaw + match.groups.filePath;
|
||||||
|
const promise = this._loadPresetText(filePath);
|
||||||
|
promises.push(promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_executeIncludeOnce(strings) {
|
||||||
|
const includeRowIndexes = []; // row indexes with "#include" statements
|
||||||
|
const promises = []; // promises to load included files
|
||||||
|
this._parceInclude(strings, includeRowIndexes, promises);
|
||||||
|
|
||||||
|
return Promise.all(promises)
|
||||||
|
.then(includedTexts => {
|
||||||
|
for (let i = 0; i < includedTexts.length; i++) {
|
||||||
|
strings[includeRowIndexes[i]] = includedTexts[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = strings.join('\n');
|
||||||
|
return text.split("\n").map(str => str.trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_executeIncludeNested(strings) {
|
||||||
|
const isIncludeFound = this._isIncludeFound(strings);
|
||||||
|
|
||||||
|
if (isIncludeFound) {
|
||||||
|
return this._executeIncludeOnce(strings)
|
||||||
|
.then(resultStrings => this._executeIncludeNested(resultStrings));
|
||||||
|
} else {
|
||||||
|
return new Promise.resolve(strings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_isIncludeFound(strings) {
|
||||||
|
for (const str of strings) {
|
||||||
|
const match = PresetsRepoIndexed._sRegExpInclude.exec(str.toLowerCase());
|
||||||
|
|
||||||
|
if (match !== null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPreset(preset) {
|
||||||
|
const promiseMainText = this._loadPresetText(this._urlRaw + preset.fullPath);
|
||||||
|
|
||||||
|
return promiseMainText
|
||||||
|
.then(text => {
|
||||||
|
let strings = text.split("\n");
|
||||||
|
strings = strings.map(str => str.trim());
|
||||||
|
return strings;
|
||||||
|
})
|
||||||
|
.then(strings => this._executeIncludeNested(strings))
|
||||||
|
.then(strings => {
|
||||||
|
preset.originalPresetCliStrings = strings;
|
||||||
|
return this._loadPresetWarning(preset);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadPresetWarning(preset) {
|
||||||
|
let completeWarning = "";
|
||||||
|
|
||||||
|
if (preset.warning) {
|
||||||
|
completeWarning += (completeWarning?"\n":"") + preset.warning;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preset.disclaimer) {
|
||||||
|
completeWarning += (completeWarning?"\n":"") + preset.disclaimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allFiles = [].concat(...[preset.include_warning, preset.include_disclaimer].filter(Array.isArray));
|
||||||
|
|
||||||
|
return this._loadFilesInOneText(allFiles)
|
||||||
|
.then(text => {
|
||||||
|
completeWarning += (completeWarning?"\n":"") + text;
|
||||||
|
preset.completeWarning = completeWarning;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadFilesInOneText(fileUrls) {
|
||||||
|
const loadPromises = [];
|
||||||
|
|
||||||
|
fileUrls?.forEach(url => {
|
||||||
|
const filePath = this._urlRaw + url;
|
||||||
|
loadPromises.push(this._loadPresetText(filePath));
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(loadPromises)
|
||||||
|
.then(texts => {
|
||||||
|
return texts.join('\n');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadPresetText(fullUrl) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetch(fullUrl)
|
||||||
|
.then(res => res.text())
|
||||||
|
.then(text => resolve(text))
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PresetsRepoIndexed._sCliCommentDirective = "#";
|
||||||
|
PresetsRepoIndexed._sCliAttributeDirective = "#$";
|
||||||
|
|
||||||
|
// Reg exp extracts file/path.txt from # include: file/path.txt
|
||||||
|
PresetsRepoIndexed._sRegExpInclude = /^#\$[ ]+?include:[ ]+?(?<filePath>\S+$)/;
|
16
src/tabs/presets/PresetsRepoIndexed/PresetsWebsiteRepo.js
Normal file
16
src/tabs/presets/PresetsRepoIndexed/PresetsWebsiteRepo.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class PresetsWebsiteRepo extends PresetsRepoIndexed {
|
||||||
|
constructor(url) {
|
||||||
|
let correctUrl = url.trim();
|
||||||
|
|
||||||
|
if (!correctUrl.endsWith("/")) {
|
||||||
|
correctUrl += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlRaw = correctUrl;
|
||||||
|
const urlViewOnline = correctUrl;
|
||||||
|
|
||||||
|
super(urlRaw, urlViewOnline);
|
||||||
|
}
|
||||||
|
}
|
14
src/tabs/presets/SourcesDialog/PresetSource.js
Normal file
14
src/tabs/presets/SourcesDialog/PresetSource.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class PresetSource {
|
||||||
|
constructor(name, url, gitHubBranch = "") {
|
||||||
|
this.name = name;
|
||||||
|
this.url = url;
|
||||||
|
this.gitHubBranch = gitHubBranch;
|
||||||
|
this.official = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isUrlGithubRepo(url) {
|
||||||
|
return url.trim().toLowerCase().startsWith("https://github.com/");
|
||||||
|
}
|
||||||
|
}
|
70
src/tabs/presets/SourcesDialog/SourcePanel.css
Normal file
70
src/tabs/presets/SourcesDialog/SourcePanel.css
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
.presets_source_panel {
|
||||||
|
background-color: var(--boxBackground);
|
||||||
|
border: 1px solid var(--subtleAccent);
|
||||||
|
padding: 1.5ex;
|
||||||
|
box-shadow: 2px 2px 5px rgba(92, 92, 92, 0.25);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_not_selected {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_selected {
|
||||||
|
cursor: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_not_selected:hover {
|
||||||
|
background-color: var(--subtleAccent);
|
||||||
|
box-shadow: 2px 2px 5px rgba(92, 92, 92, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_editing_table {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
border-spacing: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_editing_row {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_editing_field_label {
|
||||||
|
display: table-cell;
|
||||||
|
white-space: pre;
|
||||||
|
padding-right: 10px;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_editing_field_edit {
|
||||||
|
display: table-cell;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_editing_row .standard_input {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_no_editing_name {
|
||||||
|
font-size: 130%;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_no_editing_selected {
|
||||||
|
background-image: url(../../../images/icons/cf_icon_check_orange.svg);
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
margin-right: 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_source_panel_reset, .presets_source_panel_save, .presets_source_panel_delete {
|
||||||
|
float: right;
|
||||||
|
}
|
29
src/tabs/presets/SourcesDialog/SourcePanel.html
Normal file
29
src/tabs/presets/SourcesDialog/SourcePanel.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="presets_source_panel">
|
||||||
|
<div class="presets_source_panel_no_editing">
|
||||||
|
<div class="presets_source_panel_no_editing_selected"></div>
|
||||||
|
<div class="presets_source_panel_no_editing_name"></div>
|
||||||
|
</div>
|
||||||
|
<div class="presets_source_panel_editing">
|
||||||
|
<div class="presets_source_panel_editing_table">
|
||||||
|
<div class="presets_source_panel_editing_row">
|
||||||
|
<div class="presets_source_panel_editing_field_label">Name</div>
|
||||||
|
<div class="presets_source_panel_editing_field_edit"><input type="text" class="presets_source_panel_editing_name_field standard_input"/></div>
|
||||||
|
</div>
|
||||||
|
<div class="presets_source_panel_editing_row">
|
||||||
|
<div class="presets_source_panel_editing_field_label">Url</div>
|
||||||
|
<div class="presets_source_panel_editing_field_edit"><input type="text" class="presets_source_panel_editing_url_field standard_input"/></div>
|
||||||
|
</div>
|
||||||
|
<div class="presets_source_panel_editing_row presets_source_panel_editing_github_branch">
|
||||||
|
<div class="presets_source_panel_editing_field_label">GitHub branch</div>
|
||||||
|
<div class="presets_source_panel_editing_field_edit"><input type="text" class="presets_source_panel_editing_branch_field standard_input"/></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="btn">
|
||||||
|
<div class="presets_source_panel_no_editing_selected"></div>
|
||||||
|
<a href="#" class="tool regular-button presets_source_panel_activate" i18n="presetsSourcesDialogMakeSourceActive"></a>
|
||||||
|
<a href="#" class="tool regular-button presets_source_panel_save" i18n="presetsSourcesDialogSaveSource"></a>
|
||||||
|
<a href="#" class="tool regular-button presets_source_panel_reset" i18n="presetsSourcesDialogResetSource"></a>
|
||||||
|
<a href="#" class="tool regular-button presets_source_panel_delete" i18n="presetsSourcesDialogDeleteSource"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
199
src/tabs/presets/SourcesDialog/SourcePanel.js
Normal file
199
src/tabs/presets/SourcesDialog/SourcePanel.js
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class SourcePanel {
|
||||||
|
constructor(parentDiv, presetSource) {
|
||||||
|
this._parentDiv = parentDiv;
|
||||||
|
this._presetSource = presetSource;
|
||||||
|
this._active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get presetSource() {
|
||||||
|
return this._presetSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
SourcePanel.s_panelCounter++;
|
||||||
|
this._domId = `source_panel_${SourcePanel.s_panelCounter}`;
|
||||||
|
this._parentDiv.append(`<div id="${this._domId}"></div>`);
|
||||||
|
this._domWrapperDiv = $(`#${this._domId}`);
|
||||||
|
this._domWrapperDiv.toggle(false);
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this._domWrapperDiv.load("./tabs/presets/SourcesDialog/SourcePanel.html",
|
||||||
|
() => {
|
||||||
|
this._setupHtml();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnSelectedCallback(onSelectedCallback) {
|
||||||
|
// callback with this (SourcePanel) argument
|
||||||
|
// so that consumer knew which panel was clicked on
|
||||||
|
this._onSelectedCallback = onSelectedCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnDeleteCallback(onDeletedCallback) {
|
||||||
|
// callback with this (SourcePanel) argument
|
||||||
|
// so that consumer knew which panel was clicked on
|
||||||
|
this._onDeletedCallback = onDeletedCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnActivateCallback(onActivateCallback) {
|
||||||
|
// callback with this (SourcePanel) argument
|
||||||
|
// so that consumer knew which panel was clicked on
|
||||||
|
this._onActivateCallback = onActivateCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnSaveCallback(onSaveCallback) {
|
||||||
|
// callback with this (SourcePanel) argument
|
||||||
|
// so that consumer knew which panel was clicked on
|
||||||
|
this._onSaveCallback = onSaveCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelected(isSelected) {
|
||||||
|
this._setUiSelected(isSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
get active() {
|
||||||
|
return this._active;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActive(isActive) {
|
||||||
|
this._active = isActive;
|
||||||
|
this._domDivSelectedIndicator.toggle(this._active);
|
||||||
|
this._domButtonActivate.toggle(!isActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setUiOfficial() {
|
||||||
|
if (this.presetSource.official){
|
||||||
|
this._domButtonSave.toggle(false);
|
||||||
|
this._domButtonReset.toggle(false);
|
||||||
|
this._domButtonDelete.toggle(false);
|
||||||
|
this._domEditName.prop("disabled", true);
|
||||||
|
this._domEditUrl.prop("disabled", true);
|
||||||
|
this._domEditGitHubBranch.prop("disabled", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setUiSelected(isSelected) {
|
||||||
|
if (this._selected !== isSelected) {
|
||||||
|
this._domDivNoEditing.toggle(!isSelected);
|
||||||
|
this._domDivEditing.toggle(isSelected);
|
||||||
|
|
||||||
|
this._onResetButtonClick();
|
||||||
|
this._updateNoEditingName();
|
||||||
|
|
||||||
|
this._domDivInnerPanel.toggleClass("presets_source_panel_not_selected", !isSelected);
|
||||||
|
this._domDivInnerPanel.toggleClass("presets_source_panel_selected", isSelected);
|
||||||
|
if (isSelected) {
|
||||||
|
this._domDivInnerPanel.off("click");
|
||||||
|
} else {
|
||||||
|
this._domDivInnerPanel.on("click", () => this._onPanelSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
this._selected = isSelected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateNoEditingName() {
|
||||||
|
this._domDivNoEditingName.text(this._presetSource.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupHtml() {
|
||||||
|
this._readDom();
|
||||||
|
this._setupActions();
|
||||||
|
this.setSelected(false);
|
||||||
|
this._setIsSaved(true);
|
||||||
|
this._checkIfGithub();
|
||||||
|
this.setActive(this._active);
|
||||||
|
this._setUiOfficial();
|
||||||
|
|
||||||
|
i18n.localizePage();
|
||||||
|
this._domWrapperDiv.toggle(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupActions() {
|
||||||
|
this._domButtonSave.on("click", () => this._onSaveButtonClick());
|
||||||
|
this._domButtonReset.on("click", () => this._onResetButtonClick());
|
||||||
|
this._domButtonDelete.on("click", () => this._onDeleteButtonClick());
|
||||||
|
this._domButtonActivate.on("click", () => this._onActivateButtonClick());
|
||||||
|
|
||||||
|
this._domEditName.on("input", () => this._onInputChange());
|
||||||
|
this._domEditUrl.on("input", () => this._onInputChange());
|
||||||
|
this._domEditGitHubBranch.on("input", () => this._onInputChange());
|
||||||
|
}
|
||||||
|
|
||||||
|
_onPanelSelected() {
|
||||||
|
this._setUiSelected(true);
|
||||||
|
this._onSelectedCallback?.(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkIfGithub() {
|
||||||
|
const isGithubUrl = PresetSource.isUrlGithubRepo(this._domEditUrl.val());
|
||||||
|
this._domDivGithubBranch.toggle(isGithubUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onInputChange() {
|
||||||
|
this._checkIfGithub();
|
||||||
|
this._setIsSaved(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onSaveButtonClick() {
|
||||||
|
this._presetSource.name = this._domEditName.val();
|
||||||
|
this._presetSource.url = this._domEditUrl.val();
|
||||||
|
this._presetSource.gitHubBranch = this._domEditGitHubBranch.val();
|
||||||
|
this._setIsSaved(true);
|
||||||
|
this._onSaveCallback?.(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onResetButtonClick() {
|
||||||
|
this._domEditName.val(this._presetSource.name);
|
||||||
|
this._domEditUrl.val(this._presetSource.url);
|
||||||
|
this._domEditGitHubBranch.val(this._presetSource.gitHubBranch);
|
||||||
|
this._checkIfGithub();
|
||||||
|
this._setIsSaved(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDeleteButtonClick() {
|
||||||
|
this._domWrapperDiv.remove();
|
||||||
|
this._onDeletedCallback?.(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onActivateButtonClick() {
|
||||||
|
this.setActive(true);
|
||||||
|
this._onActivateCallback?.(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setIsSaved(isSaved) {
|
||||||
|
if (isSaved) {
|
||||||
|
this._domButtonSave.addClass(GUI.buttonDisabledClass);
|
||||||
|
this._domButtonReset.addClass(GUI.buttonDisabledClass);
|
||||||
|
} else {
|
||||||
|
this._domButtonSave.removeClass(GUI.buttonDisabledClass);
|
||||||
|
this._domButtonReset.removeClass(GUI.buttonDisabledClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_readDom() {
|
||||||
|
this._domDivInnerPanel = this._domWrapperDiv.find(".presets_source_panel");
|
||||||
|
this._domDivNoEditing = this._domWrapperDiv.find(".presets_source_panel_no_editing");
|
||||||
|
this._domDivEditing = this._domWrapperDiv.find(".presets_source_panel_editing");
|
||||||
|
|
||||||
|
this._domEditName = this._domWrapperDiv.find(".presets_source_panel_editing_name_field");
|
||||||
|
this._domEditUrl = this._domWrapperDiv.find(".presets_source_panel_editing_url_field");
|
||||||
|
this._domEditGitHubBranch = this._domWrapperDiv.find(".presets_source_panel_editing_branch_field");
|
||||||
|
|
||||||
|
this._domButtonSave = this._domWrapperDiv.find(".presets_source_panel_save");
|
||||||
|
this._domButtonReset = this._domWrapperDiv.find(".presets_source_panel_reset");
|
||||||
|
this._domButtonActivate = this._domWrapperDiv.find(".presets_source_panel_activate");
|
||||||
|
this._domButtonDelete = this._domWrapperDiv.find(".presets_source_panel_delete");
|
||||||
|
this._domDivGithubBranch = this._domWrapperDiv.find(".presets_source_panel_editing_github_branch");
|
||||||
|
this._domDivNoEditingName = this._domWrapperDiv.find(".presets_source_panel_no_editing_name");
|
||||||
|
|
||||||
|
this._domDivSelectedIndicator = this._domWrapperDiv.find(".presets_source_panel_no_editing_selected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SourcePanel.s_panelCounter = 0;
|
||||||
|
|
27
src/tabs/presets/SourcesDialog/SourcesDialog.css
Normal file
27
src/tabs/presets/SourcesDialog/SourcesDialog.css
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
.presets_sources_dialog_title_panel {
|
||||||
|
padding-bottom: .5ex;
|
||||||
|
border-bottom: 1px solid var(--accent);
|
||||||
|
margin-bottom: 2ex;
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_sources_dialog_scrollable {
|
||||||
|
height: 430px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 575px) {
|
||||||
|
#presets_sources_dialog_content_wrapper .content_toolbar {
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_sources_dialog_scrollable {
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding-bottom: 51px;
|
||||||
|
height: unset;
|
||||||
|
}
|
||||||
|
}
|
16
src/tabs/presets/SourcesDialog/SourcesDialog.html
Normal file
16
src/tabs/presets/SourcesDialog/SourcesDialog.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<div id="presets_sources_dialog_content_wrapper">
|
||||||
|
<div id="presets_sources_dialog_content">
|
||||||
|
<div class="presets_sources_dialog_title_panel" i18n="presetsSourcesDialogTitle"></div>
|
||||||
|
<div class="presets_sources_dialog_scrollable">
|
||||||
|
<div class="note" i18n="presets_sources_dialog_warning"></div>
|
||||||
|
<div class="presets_sources_dialog_sources"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content_toolbar">
|
||||||
|
<div class="btn">
|
||||||
|
<a id="presets_sources_dialog_close" i18n="OK" class="tool regular-button" target="_blank" rel="noopener noreferrer"></a>
|
||||||
|
<a id="presets_sources_dialog_add_new" i18n="presetsSourcesDialogAddNew" class="tool regular-button" target="_blank" rel="noopener noreferrer"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
193
src/tabs/presets/SourcesDialog/SourcesDialog.js
Normal file
193
src/tabs/presets/SourcesDialog/SourcesDialog.js
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class PresetsSourcesDialog {
|
||||||
|
constructor(domDialog) {
|
||||||
|
this._domDialog = domDialog;
|
||||||
|
this._sourceSelectedPromiseResolve = null;
|
||||||
|
this._sourcesPanels = [];
|
||||||
|
this._sources = [];
|
||||||
|
this._activeSourceIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this._domDialog.load("./tabs/presets/SourcesDialog/SourcesDialog.html",
|
||||||
|
() => {
|
||||||
|
this._setupDialog();
|
||||||
|
this._initializeSources();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this._domDialog[0].showModal();
|
||||||
|
return new Promise(resolve => this._sourceSelectedPromiseResolve = resolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
getActivePresetSource() {
|
||||||
|
return this._sources[this._activeSourceIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
get isOfficialActive() {
|
||||||
|
return this._activeSourceIndex === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_initializeSources() {
|
||||||
|
this._sources = this._readSourcesFromStorage();
|
||||||
|
this._activeSourceIndex = this._readActiveSourceIndexFromStorage(this._sources.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < this._sources.length; i++) {
|
||||||
|
const isActive = this._activeSourceIndex === i;
|
||||||
|
this._addNewSourcePanel(this._sources[i], isActive, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_readSourcesFromStorage() {
|
||||||
|
const officialSource = this._createOfficialSource();
|
||||||
|
const officialSourceSecondary = this._createSecondaryOfficialSource();
|
||||||
|
|
||||||
|
const obj = ConfigStorage.get('PresetSources');
|
||||||
|
let sources = obj.PresetSources;
|
||||||
|
|
||||||
|
if (sources && sources.length > 0) {
|
||||||
|
sources[0] = officialSource;
|
||||||
|
} else {
|
||||||
|
sources = [officialSource, officialSourceSecondary];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sources.length === 1) {
|
||||||
|
sources.push(officialSourceSecondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
_readActiveSourceIndexFromStorage(sourcesCount) {
|
||||||
|
const obj = ConfigStorage.get('PresetSourcesActiveIndex');
|
||||||
|
const index = Number(obj.PresetSourcesActiveIndex);
|
||||||
|
let result = 0;
|
||||||
|
|
||||||
|
if (index && Number.isInteger(index) && index < sourcesCount) {
|
||||||
|
result = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createOfficialSource() {
|
||||||
|
const officialSource = new PresetSource("Betaflight Official Presets", "https://api.betaflight.com/firmware-presets/", "");
|
||||||
|
officialSource.official = true;
|
||||||
|
return officialSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createSecondaryOfficialSource() {
|
||||||
|
const officialSource = new PresetSource("Betaflight Presets - GitHub BACKUP", "https://github.com/betaflight/firmware-presets", "backup");
|
||||||
|
officialSource.official = false;
|
||||||
|
return officialSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupDialog() {
|
||||||
|
this._readDom();
|
||||||
|
this._setupEvents();
|
||||||
|
this._domButtonAddNew.on("click", () => this._onAddNewSourceButtonClick());
|
||||||
|
i18n.localizePage();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onAddNewSourceButtonClick() {
|
||||||
|
const presetSource = new PresetSource(i18n.getMessage("presetsSourcesDialogDefaultSourceName"), "", "");
|
||||||
|
this._addNewSourcePanel(presetSource).then(() => {
|
||||||
|
this._scrollDown();
|
||||||
|
this._updateSourcesFromPanels();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_scrollDown() {
|
||||||
|
this._domDivSourcesPanel.stop();
|
||||||
|
this._domDivSourcesPanel.animate({scrollTop: this._domDivSourcesPanel.prop('scrollHeight') + "px"});
|
||||||
|
}
|
||||||
|
|
||||||
|
_addNewSourcePanel(presetSource, isActive = false, isSelected = true) {
|
||||||
|
const sourcePanel = new SourcePanel(this._domDivSourcesPanel, presetSource);
|
||||||
|
this._sourcesPanels.push(sourcePanel);
|
||||||
|
return sourcePanel.load().then(() => {
|
||||||
|
sourcePanel.setOnSelectedCallback(selectedPanel => this._onSourcePanelSelected(selectedPanel));
|
||||||
|
sourcePanel.setOnDeleteCallback(selectedPanel => this._onSourcePanelDeleted(selectedPanel));
|
||||||
|
sourcePanel.setOnActivateCallback(selectedPanel => this._onSourcePanelActivated(selectedPanel));
|
||||||
|
sourcePanel.setOnSaveCallback(() => this._onSourcePanelSaved());
|
||||||
|
sourcePanel.setActive(isActive);
|
||||||
|
if (isSelected) {
|
||||||
|
this._onSourcePanelSelected(sourcePanel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupEvents() {
|
||||||
|
this._domButtonClose.on("click", () => this._onCloseButtonClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
_onCloseButtonClick() {
|
||||||
|
this._sourceSelectedPromiseResolve?.();
|
||||||
|
this._domDialog[0].close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_readPanels() {
|
||||||
|
this._sources = [];
|
||||||
|
this._activeSourceIndex = 0;
|
||||||
|
for(let i = 0; i < this._sourcesPanels.length; i++) {
|
||||||
|
this._sources.push(this._sourcesPanels[i].presetSource);
|
||||||
|
if (this._sourcesPanels[i].active) {
|
||||||
|
this._activeSourceIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveSources() {
|
||||||
|
ConfigStorage.set({'PresetSources': this._sources});
|
||||||
|
ConfigStorage.set({'PresetSourcesActiveIndex': this._activeSourceIndex});
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateSourcesFromPanels() {
|
||||||
|
this._readPanels();
|
||||||
|
this._saveSources();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onSourcePanelSaved() {
|
||||||
|
this._updateSourcesFromPanels();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onSourcePanelSelected(selectedPanel) {
|
||||||
|
for (const panel of this._sourcesPanels) {
|
||||||
|
if (panel !== selectedPanel) {
|
||||||
|
panel.setSelected(false);
|
||||||
|
} else {
|
||||||
|
panel.setSelected(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onSourcePanelDeleted(selectedPanel) {
|
||||||
|
this._sourcesPanels = this._sourcesPanels.filter(panel => panel !== selectedPanel);
|
||||||
|
if (selectedPanel.active) {
|
||||||
|
this._sourcesPanels[0].setActive(true);
|
||||||
|
}
|
||||||
|
this._updateSourcesFromPanels();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onSourcePanelActivated(selectedPanel) {
|
||||||
|
for (const panel of this._sourcesPanels) {
|
||||||
|
if (panel !== selectedPanel) {
|
||||||
|
panel.setActive(false);
|
||||||
|
} else {
|
||||||
|
panel.setActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._updateSourcesFromPanels();
|
||||||
|
}
|
||||||
|
|
||||||
|
_readDom() {
|
||||||
|
this._domButtonAddNew = $("#presets_sources_dialog_add_new");
|
||||||
|
this._domButtonClose = $("#presets_sources_dialog_close");
|
||||||
|
this._domDivSourcesPanel = $(".presets_sources_dialog_sources");
|
||||||
|
}
|
||||||
|
}
|
82
src/tabs/presets/TitlePanel/PresetTitlePanel.css
Normal file
82
src/tabs/presets/TitlePanel/PresetTitlePanel.css
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
.preset_title_panel {
|
||||||
|
color: var(--defaultText);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_border {
|
||||||
|
background-color: var(--boxBackground);
|
||||||
|
border: 1px solid var(--subtleAccent);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 1.5ex;
|
||||||
|
box-shadow: 2px 2px 5px rgba(92, 92, 92, 0.25);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_title {
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 1ex;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_category {
|
||||||
|
color: var(--mutedText);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_official {
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-right: 3px;
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
color: white;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_status_official {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_status_community {
|
||||||
|
background-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_status_experimental {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_versions_text {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_keywords_text {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_title_panel_table {
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_title_panel_table td{
|
||||||
|
overflow: hidden;
|
||||||
|
height: 24px;
|
||||||
|
width: auto;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_title_panel_table td:nth-child(1){
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset_title_panel_label {
|
||||||
|
color: var(--quietHeader);
|
||||||
|
}
|
92
src/tabs/presets/TitlePanel/PresetTitlePanel.js
Normal file
92
src/tabs/presets/TitlePanel/PresetTitlePanel.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class PresetTitlePanel
|
||||||
|
{
|
||||||
|
constructor(parentDiv, preset, clickable, onLoadedCallback)
|
||||||
|
{
|
||||||
|
PresetTitlePanel.s_panelCounter ++;
|
||||||
|
this._parentDiv = parentDiv;
|
||||||
|
this._onLoadedCallback = onLoadedCallback;
|
||||||
|
this._domId = `preset_title_panel_${PresetTitlePanel.s_panelCounter}`;
|
||||||
|
this._preset = preset;
|
||||||
|
|
||||||
|
this._parentDiv.append(`<div class="${this._domId}"></div>`);
|
||||||
|
this._domWrapperDiv = $(`.${this._domId}`);
|
||||||
|
this._domWrapperDiv.toggle(false);
|
||||||
|
|
||||||
|
if (clickable) {
|
||||||
|
this._domWrapperDiv.addClass("preset_title_panel_border");
|
||||||
|
// setting up hover effect here, because if setup in SCC it stops working after background animation like - this._domWrapperDiv.animate({ backgroundColor....
|
||||||
|
this._domWrapperDiv.on("mouseenter", () => this._domWrapperDiv.css({"background-color": "var(--subtleAccent)"}));
|
||||||
|
this._domWrapperDiv.on("mouseleave", () => this._domWrapperDiv.css({"background-color": "var(--boxBackground)"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
this._domWrapperDiv.load("./tabs/presets/TitlePanel/PresetTitlePanelBody.html", () => this._setupHtml());
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeClick(presetsDetailedDialog, presetsRepo)
|
||||||
|
{
|
||||||
|
this._domWrapperDiv.on("click", () => {
|
||||||
|
presetsDetailedDialog.open(this._preset, presetsRepo).then(isPresetPicked => {
|
||||||
|
if (isPresetPicked) {
|
||||||
|
const color = this._domWrapperDiv.css( "background-color" );
|
||||||
|
this._domWrapperDiv.css('background-color', 'green');
|
||||||
|
this._domWrapperDiv.animate({ backgroundColor: color }, 2000);
|
||||||
|
this.setPicked(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setPicked(isPicked) {
|
||||||
|
this._preset.isPicked = isPicked;
|
||||||
|
|
||||||
|
if (isPicked) {
|
||||||
|
this._domWrapperDiv.css({"border": "2px solid green"});
|
||||||
|
} else {
|
||||||
|
this._domWrapperDiv.css({"border": "1px solid var(--subtleAccent)"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupHtml()
|
||||||
|
{
|
||||||
|
this._readDom();
|
||||||
|
|
||||||
|
this._domCategory.text(this._preset.category);
|
||||||
|
this._domTitle.text(this._preset.title);
|
||||||
|
this._domTitle.prop("title", this._preset.title);
|
||||||
|
this._domAuthor.text(this._preset.author);
|
||||||
|
this._domVersions.text(this._preset.firmware_version?.join("; "));
|
||||||
|
this._domKeywords.text(this._preset.keywords?.join("; "));
|
||||||
|
this._domKeywords.prop("title", this._preset.keywords?.join("; "));
|
||||||
|
this._domStatusOfficial.toggle(this._preset.status === "OFFICIAL");
|
||||||
|
this._domStatusCommunity.toggle(this._preset.status === "COMMUNITY");
|
||||||
|
this._domStatusExperimental.toggle(this._preset.status === "EXPERIMENTAL");
|
||||||
|
this.setPicked(this._preset.isPicked);
|
||||||
|
|
||||||
|
i18n.localizePage();
|
||||||
|
this._domWrapperDiv.toggle(true);
|
||||||
|
this._onLoadedCallback?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
_readDom()
|
||||||
|
{
|
||||||
|
this._domTitle = this._domWrapperDiv.find('.preset_title_panel_title');
|
||||||
|
this._domCategory = this._domWrapperDiv.find('.preset_title_panel_category');
|
||||||
|
this._domAuthor = this._domWrapperDiv.find('.preset_title_panel_author_text');
|
||||||
|
this._domKeywords = this._domWrapperDiv.find('.preset_title_panel_keywords_text');
|
||||||
|
this._domVersions = this._domWrapperDiv.find('.preset_title_panel_versions_text');
|
||||||
|
this._domStatusOfficial = this._domWrapperDiv.find('.preset_title_panel_status_official');
|
||||||
|
this._domStatusCommunity = this._domWrapperDiv.find('.preset_title_panel_status_community');
|
||||||
|
this._domStatusExperimental = this._domWrapperDiv.find('.preset_title_panel_status_experimental');
|
||||||
|
}
|
||||||
|
|
||||||
|
remove()
|
||||||
|
{
|
||||||
|
this._domWrapperDiv.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PresetTitlePanel.s_panelCounter = 0;
|
45
src/tabs/presets/TitlePanel/PresetTitlePanelBody.html
Normal file
45
src/tabs/presets/TitlePanel/PresetTitlePanelBody.html
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<div class="preset_title_panel">
|
||||||
|
<div>
|
||||||
|
<span class="preset_title_panel_title"></span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<table class="presets_title_panel_table" role="presentation">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="preset_title_panel_official preset_title_panel_status_experimental" i18n="presetsExperimental"></span>
|
||||||
|
<span class="preset_title_panel_official preset_title_panel_status_community" i18n="presetsCommunity"></span>
|
||||||
|
<span class="preset_title_panel_official preset_title_panel_status_official" i18n="presetsOfficial"></span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="preset_title_panel_category"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="preset_title_panel_label preset_title_panel_author_label" i18n="presetsAuthor"></span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="preset_title_panel_author_text"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="preset_title_panel_label preset_title_panel_versions_label" i18n="presetsVersions"></span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="preset_title_panel_versions_text"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="preset_title_panel_label preset_title_panel_keywords_label" i18n="presetsKeywords"></span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="preset_title_panel_keywords_text"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
426
src/tabs/presets/presets.css
Normal file
426
src/tabs/presets/presets.css
Normal file
|
@ -0,0 +1,426 @@
|
||||||
|
.tab-presets {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .content_wrapper {
|
||||||
|
height: calc(100% - 30px - 3ex);
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets p {
|
||||||
|
padding: 0;
|
||||||
|
border: 0 dotted var(--subtleAccent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .presets_warnings {
|
||||||
|
padding-left : 20px;
|
||||||
|
padding-right : 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_content_wrapper {
|
||||||
|
padding: 0px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_top_bar_pannel {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_title_text {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_top_bar_button_pannel {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_warning_backup {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr fit-content(300px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_warning_backup_text {
|
||||||
|
padding-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_warning_backup_button_hide {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-right: 0px;
|
||||||
|
line-height: 17px;
|
||||||
|
font-size: 10px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .top_panel_spacer {
|
||||||
|
width: 0px;
|
||||||
|
display: inline;
|
||||||
|
border: 1px var(--subtleAccent);
|
||||||
|
border-style: none none none solid;
|
||||||
|
height: 60%;
|
||||||
|
margin-right: 10px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .tab_title .presets_top_bar_button_pannel .regular-button{
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-top: 0px;
|
||||||
|
line-height : 17px;
|
||||||
|
font-size : 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presetsWikiButton {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .tab_title
|
||||||
|
{
|
||||||
|
padding: 20px 20px 0px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_list {
|
||||||
|
padding: 0px 0px 20px 0px;
|
||||||
|
height: calc(100% - 180px);
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 12px;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
#preset_list_wrapper {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_global_loading {
|
||||||
|
padding: 0px 20px 20px 20px;
|
||||||
|
display: none;
|
||||||
|
width: 50%;
|
||||||
|
height: 50%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_global_loading_error {
|
||||||
|
padding: 0px 20px 20px 20px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_main_content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_list:last-child::after {
|
||||||
|
visibility: hidden;
|
||||||
|
display: block;
|
||||||
|
font-size: 0;
|
||||||
|
content: " ";
|
||||||
|
clear: both;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_filter_select {
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--boxBackground);
|
||||||
|
color: var(--defaultText);
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_search_settings {
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
background-color: var(--boxBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .ms-choice,.ms-drop {
|
||||||
|
background-color: var(--boxBackground)!important;
|
||||||
|
color: var(--defaultText)!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_text_input {
|
||||||
|
border: 1px solid var(--subtleAccent);
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: var(--boxBackground);
|
||||||
|
color: var(--defaultText);
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_filter_text {
|
||||||
|
height: 26px;
|
||||||
|
flex: 1;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_search_hint {
|
||||||
|
float: left;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin-right: 12px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-image: url(../../images/icons/cf_icon_search_orange.svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_search_bar_wrapper {
|
||||||
|
display: flex;
|
||||||
|
padding: 2ex 20px 2ex 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_cli {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_cli_background {
|
||||||
|
border: 1px solid var(--subtleAccent);
|
||||||
|
background-color: rgba(64, 64, 64, 1);
|
||||||
|
margin-top: 0;
|
||||||
|
height: 300px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.80);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .window {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
font-family: monospace;
|
||||||
|
color: white;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
user-select: text;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_fitler_table_header
|
||||||
|
{
|
||||||
|
background: var(--quietHeader);
|
||||||
|
color: var(--quietText);
|
||||||
|
border-right: 1px solid var(--subtleAccent);
|
||||||
|
font-weight: normal;
|
||||||
|
text-align: left;
|
||||||
|
padding: 4px 4px 4px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_fitler_table_value {
|
||||||
|
background-color: var(--alternativeBackground);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets textarea[name='commands'] {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 4px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid var(--subtleAccent);
|
||||||
|
background-color: rgba(64, 64, 64, 1);
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .presets_cli_errors_dialog_warning {
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets #content-watermark {
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_cli_errors_dialog .regular-button {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-right: 0px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .window .wrapper {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .window .error_message {
|
||||||
|
color: red;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .save {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_detailed_dialog {
|
||||||
|
width : 600px;
|
||||||
|
height : 520px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_sources_dialog {
|
||||||
|
width : 600px;
|
||||||
|
height : 520px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_cli_errors_dialog {
|
||||||
|
width : 600px;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_apply_progress_dialog {
|
||||||
|
width: 300px;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .presets_apply_progress_dialog_progress_bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .ms-drop {
|
||||||
|
z-index: 2001;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hack-fix for multiple-select with small font size. Originally checkboxes are not ligned with the text options when the font size is smaller than 0.7em */
|
||||||
|
.tab-presets
|
||||||
|
.ms-drop input[type="radio"], .ms-drop input[type="checkbox"] {
|
||||||
|
margin-top: 0.1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_filter_table_wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 5px;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
padding: 0px 20px 0px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_filter_table_wrapper .freelabel {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .cf_doc_version_bt {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_list_no_found, #presets_list_too_many_found {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) {
|
||||||
|
|
||||||
|
.tab-presets .content_wrapper {
|
||||||
|
height: calc(100% - 87px);
|
||||||
|
}
|
||||||
|
.tab-presets .content_toolbar {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_search_settings {
|
||||||
|
position: static;
|
||||||
|
top: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_secondary_top_bar_panel_buttons {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_secondary_top_bar_panel_buttons .regular-button {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
line-height: 17px;
|
||||||
|
font-size: 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 575px) {
|
||||||
|
.tab-presets .content_wrapper {
|
||||||
|
height: calc(100% - 51px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .tab_title
|
||||||
|
{
|
||||||
|
padding: 20px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .backdrop {
|
||||||
|
background-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_list {
|
||||||
|
grid-template-columns: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preset_list_wrapper, .presets_search_settings {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_search_bar_wrapper {
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 0px;
|
||||||
|
padding-top: 1ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_filter_row {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_filter_table_wrapper {
|
||||||
|
display: table;
|
||||||
|
border-spacing: 6px;
|
||||||
|
margin-right: -6px;
|
||||||
|
margin-left: -6px;
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_fitler_table_header {
|
||||||
|
display: table-cell;
|
||||||
|
background-color: unset;
|
||||||
|
border-right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_fitler_table_value {
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-presets .presets_warnings {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_cli_errors_dialog {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_apply_progress_dialog {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_cli_errors_dialog .btn {
|
||||||
|
position: fixed;
|
||||||
|
right: 12px;
|
||||||
|
bottom: 12px;
|
||||||
|
}
|
||||||
|
#presets_cli {
|
||||||
|
height: calc(100% - 121px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#presets_cli_background {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_warning_backup {
|
||||||
|
display: block;
|
||||||
|
grid-template-columns: 1fr fit-content(300px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.presets_warning_backup_text {
|
||||||
|
padding-right: 24px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
}
|
116
src/tabs/presets/presets.html
Normal file
116
src/tabs/presets/presets.html
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
<div class="tab-presets toolbar_fixed_bottom">
|
||||||
|
<div class="content_wrapper" id="presets_content_wrapper">
|
||||||
|
<div class="tab_title">
|
||||||
|
<div i18n="tabPresets" class="presets_title_text"></div>
|
||||||
|
<div class="presets_top_bar_button_pannel">
|
||||||
|
<a i18n="presetsWiki" class="presetsWikiButton tool regular-button right" href="https://github.com/betaflight/betaflight/wiki/Presets-in-BF-4.3" target="_blank" rel="noopener noreferrer">Presets Wiki</a>
|
||||||
|
<div class = "top_panel_spacer visible-on-desktop-only"></div>
|
||||||
|
<a href="#" class="presets_sources_show tool regular-button right" i18n="presetSources"></a>
|
||||||
|
<div class = "top_panel_spacer visible-on-desktop-only"></div>
|
||||||
|
<a href="#" class="presets_load_config visible-on-desktop-only tool regular-button right" i18n="presetsBackupLoad"></a>
|
||||||
|
<a href="#" class="presets_save_config visible-on-desktop-only tool regular-button right" i18n="presetsBackupSave"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="visible-on-phone-only presets_secondary_top_bar_panel_buttons">
|
||||||
|
<a href="#" class="presets_load_config tool regular-button disabled" i18n="presetsBackupLoad"></a>
|
||||||
|
<a href="#" class="presets_save_config tool regular-button disabled" i18n="presetsBackupSave"></a>
|
||||||
|
</div>
|
||||||
|
<div class="presets_warnings">
|
||||||
|
<div class="note presets_warning_not_official_source" i18n="presetsWarningNotOfficialSource"></div>
|
||||||
|
<div class="note presets_warning_backup">
|
||||||
|
<div class="presets_warning_backup_text" i18n="presetsWarningBackup"></div>
|
||||||
|
<a href="#" id="" class="tool regular-button presets_warning_backup_button_hide" i18n="dontShowAgain">Don't show again</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="presets_global_loading" class="data-loading">
|
||||||
|
</div>
|
||||||
|
<div id="presets_global_loading_error">
|
||||||
|
<h3 i18n="presetsLoadError"></h3>
|
||||||
|
<a href="#" id="presets_reload" class="tool regular-button" i18n="presetsReload"></a>
|
||||||
|
</div>
|
||||||
|
<div id="presets_main_content">
|
||||||
|
<div class="presets_search_settings">
|
||||||
|
<div class="presets_filter_table_wrapper">
|
||||||
|
<div class="presets_filter_row">
|
||||||
|
<div class = "presets_fitler_table_header" i18n="presetsFilterCategory"></div>
|
||||||
|
<div class = "presets_fitler_table_value">
|
||||||
|
<select multiple="multiple" class="presets_filter_select" id="presets_filter_category"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="presets_filter_row">
|
||||||
|
<div class = "presets_fitler_table_header" i18n="presetsFilterKeyword"></div>
|
||||||
|
<div class = "presets_fitler_table_value">
|
||||||
|
<select multiple="multiple" class="presets_filter_select" id="presets_filter_keyword"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="presets_filter_row">
|
||||||
|
<div class = "presets_fitler_table_header" i18n="presetsFilterAuthor"></div>
|
||||||
|
<div class = "presets_fitler_table_value">
|
||||||
|
<select multiple="multiple" class="presets_filter_select" id="presets_filter_author"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="presets_filter_row">
|
||||||
|
<div class = "presets_fitler_table_header" i18n="presetsFilterFirmware"></div>
|
||||||
|
<div class = "presets_fitler_table_value">
|
||||||
|
<select multiple="multiple" class="presets_filter_select" id="presets_filter_firmware_version"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="presets_filter_row">
|
||||||
|
<div class = "presets_fitler_table_header" i18n="presetsFilterStatus"></div>
|
||||||
|
<div class = "presets_fitler_table_value">
|
||||||
|
<select multiple="multiple" class="presets_filter_select" id="presets_filter_status"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="presets_search_bar_wrapper">
|
||||||
|
<div id="presets_search_hint">
|
||||||
|
</div>
|
||||||
|
<input type="text" class="presets_text_input" id="presets_filter_text"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="preset_list_wrapper">
|
||||||
|
<div id="presets_list_no_found" i18n="presetsNoPresetsFound"></div>
|
||||||
|
<div id="presets_list"></div>
|
||||||
|
<div id="presets_list_too_many_found" i18n="presetsTooManyPresetsFound">Too many found</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content_toolbar">
|
||||||
|
<div class="btn">
|
||||||
|
<a href="#" id="presets_save_button" class="tool regular-button" i18n="presetsButtonSave"></a>
|
||||||
|
<a href="#" id="presets_cancel_button" class="tool regular-button" i18n="presetsButtonCancel"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dialog id="presets_detailed_dialog">
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="presets_sources_dialog">
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="presets_apply_progress_dialog">
|
||||||
|
<div class="presets_apply_progress_dialog_label" i18n="presetsApplyingPresets"></div>
|
||||||
|
<div class="presets_apply_progress_dialog_please_wait" i18n="presetsPleaseWait"></div>
|
||||||
|
<progress class="presets_apply_progress_dialog_progress_bar" value="0" max="100"></progress>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="presets_cli_errors_dialog">
|
||||||
|
<div class="presets_cli_errors_dialog_warning" i18n="presetsCliErrorsWaring"></div>
|
||||||
|
<div id="presets_cli">
|
||||||
|
<div id="presets_cli_background">
|
||||||
|
<div class="window" id="presets_cli_window">
|
||||||
|
<div class="wrapper" id="presets_cli_window_wrapper"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="commandline">
|
||||||
|
<textarea name="commands" id="presets_cli_command" rows="1" cols="0"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="btn">
|
||||||
|
<a href="#" id="presets_cli_errors_save_anyway_button" class="tool regular-button" i18n="presetsSaveAnyway"></a>
|
||||||
|
<a href="#" id="presets_cli_errors_exit_no_save_button" class="tool regular-button" i18n="presetsButtonCancel"></a>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
</div>
|
549
src/tabs/presets/presets.js
Normal file
549
src/tabs/presets/presets.js
Normal file
|
@ -0,0 +1,549 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
TABS.presets = {
|
||||||
|
presetsRepo: null,
|
||||||
|
cliEngine: null,
|
||||||
|
pickedPresetList: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.initialize = function (callback) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
self.cliEngine = new CliEngine(self);
|
||||||
|
self.cliEngine.setProgressCallback(value => this.onApplyProgressChange(value));
|
||||||
|
self._presetPanels = [];
|
||||||
|
|
||||||
|
$('#content').load("./tabs/presets/presets.html", () => self.onHtmlLoad(callback));
|
||||||
|
|
||||||
|
if (GUI.active_tab !== 'presets') {
|
||||||
|
GUI.active_tab = 'presets';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.readDom = function() {
|
||||||
|
this._divGlobalLoading = $('#presets_global_loading');
|
||||||
|
this._divGlobalLoadingError = $('#presets_global_loading_error');
|
||||||
|
this._divCli = $('#presets_cli');
|
||||||
|
this._divMainContent = $('#presets_main_content');
|
||||||
|
this._selectCategory = $('#presets_filter_category');
|
||||||
|
this._selectKeyword = $('#presets_filter_keyword');
|
||||||
|
this._selectAuthor = $('#presets_filter_author');
|
||||||
|
this._selectFirmwareVersion = $('#presets_filter_firmware_version');
|
||||||
|
this._selectStatus = $('#presets_filter_status');
|
||||||
|
this._inputTextFilter = $('#presets_filter_text');
|
||||||
|
this._divPresetList = $('#presets_list');
|
||||||
|
|
||||||
|
this._domButtonSave = $("#presets_save_button");
|
||||||
|
this._domButtonCancel = $("#presets_cancel_button");
|
||||||
|
|
||||||
|
this._domReloadButton = $("#presets_reload");
|
||||||
|
this._domContentWrapper = $("#presets_content_wrapper");
|
||||||
|
this._domProgressDialog = $("#presets_apply_progress_dialog")[0];
|
||||||
|
this._domProgressDialogProgressBar = $(".presets_apply_progress_dialog_progress_bar");
|
||||||
|
this._domButtonaSaveAnyway = $("#presets_cli_errors_save_anyway_button");
|
||||||
|
this._domButtonaCliExit = $("#presets_cli_errors_exit_no_save_button");
|
||||||
|
this._domDialogCliErrors = $("#presets_cli_errors_dialog")[0];
|
||||||
|
this._domButtonSaveBackup = $(".presets_save_config");
|
||||||
|
this._domButtonLoadBackup = $(".presets_load_config");
|
||||||
|
this._domButtonPresetSources = $(".presets_sources_show");
|
||||||
|
|
||||||
|
this._domWarningNotOfficialSource = $(".presets_warning_not_official_source");
|
||||||
|
this._domWarningBackup = $(".presets_warning_backup");
|
||||||
|
this._domButtonHideBackupWarning = $(".presets_warning_backup_button_hide");
|
||||||
|
|
||||||
|
this._domListNoFound = $("#presets_list_no_found");
|
||||||
|
this._domListTooManyFound = $("#presets_list_too_many_found");
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.getPickedPresetsCli = function() {
|
||||||
|
let result = [];
|
||||||
|
this.pickedPresetList.forEach(pickedPreset => {
|
||||||
|
result.push(...pickedPreset.presetCli);
|
||||||
|
});
|
||||||
|
result = result.filter(command => command !== "");
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.onApplyProgressChange = function(value) {
|
||||||
|
this._domProgressDialogProgressBar.val(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.applyCommandsList = function(strings) {
|
||||||
|
strings.forEach(cliCommand => {
|
||||||
|
this.cliEngine.sendLine(cliCommand);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.onSaveClick = function() {
|
||||||
|
this._domProgressDialogProgressBar.val(0);
|
||||||
|
this._domProgressDialog.showModal();
|
||||||
|
const currentCliErrorsCount = this.cliEngine.errorsCount;
|
||||||
|
|
||||||
|
this.activateCli().then(() => {
|
||||||
|
const cliCommandsArray = this.getPickedPresetsCli();
|
||||||
|
this.cliEngine.executeCommandsArray(cliCommandsArray).then(() => {
|
||||||
|
const newCliErrorsCount = this.cliEngine.errorsCount;
|
||||||
|
|
||||||
|
if (newCliErrorsCount !== currentCliErrorsCount) {
|
||||||
|
this._domProgressDialog.close();
|
||||||
|
this._domDialogCliErrors.showModal();
|
||||||
|
} else {
|
||||||
|
this._domProgressDialog.close();
|
||||||
|
this.cliEngine.sendLine(CliEngine.s_commandSave);
|
||||||
|
this.disconnectCliMakeSure();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.disconnectCliMakeSure = function() {
|
||||||
|
GUI.timeout_add('disconnect', function () {
|
||||||
|
$('div.connect_controls a.connect').trigger( "click" );
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.setupMenuButtons = function() {
|
||||||
|
this._domButtonSave.on("click", () => this.onSaveClick());
|
||||||
|
|
||||||
|
|
||||||
|
this._domButtonCancel.on("click", () => {
|
||||||
|
for(const pickedPreset of this.pickedPresetList) {
|
||||||
|
pickedPreset.preset.isPicked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateSearchResults();
|
||||||
|
this.pickedPresetList.length = 0;
|
||||||
|
this.enableSaveCancelButtons(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._domButtonaCliExit.on("click", () =>{
|
||||||
|
this._domDialogCliErrors.close();
|
||||||
|
this.cliEngine.sendLine(CliEngine.s_commandExit);
|
||||||
|
this.disconnectCliMakeSure();
|
||||||
|
});
|
||||||
|
this._domButtonaSaveAnyway.on("click", () => {
|
||||||
|
this._domDialogCliErrors.close();
|
||||||
|
this.cliEngine.sendLine(CliEngine.s_commandSave);
|
||||||
|
this.disconnectCliMakeSure();
|
||||||
|
});
|
||||||
|
this._domButtonSaveBackup.on("click", () => this.onSaveConfigClick());
|
||||||
|
this._domButtonLoadBackup.on("click", () => this.onLoadConfigClick());
|
||||||
|
this._domButtonPresetSources.on("click", () => this.onPresetSourcesShowClick());
|
||||||
|
this._domButtonHideBackupWarning.on("click", () => this.onButtonHideBackupWarningClick());
|
||||||
|
|
||||||
|
this._domButtonSave.toggleClass(GUI.buttonDisabledClass, false);
|
||||||
|
this._domButtonCancel.toggleClass(GUI.buttonDisabledClass, false);
|
||||||
|
this._domReloadButton.on("click", () => this.reload());
|
||||||
|
|
||||||
|
this.enableSaveCancelButtons(false);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.enableSaveCancelButtons = function (isEnabled) {
|
||||||
|
this._domButtonSave.toggleClass(GUI.buttonDisabledClass, !isEnabled);
|
||||||
|
this._domButtonCancel.toggleClass(GUI.buttonDisabledClass, !isEnabled);
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.onButtonHideBackupWarningClick = function() {
|
||||||
|
this._domWarningBackup.toggle(false);
|
||||||
|
ConfigStorage.set({ 'showPresetsWarningBackup': false });
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.setupBackupWarning = function() {
|
||||||
|
const obj = ConfigStorage.get('showPresetsWarningBackup');
|
||||||
|
if (obj.showPresetsWarningBackup === undefined) {
|
||||||
|
obj.showPresetsWarningBackup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const warningVisible = !!obj.showPresetsWarningBackup;
|
||||||
|
this._domWarningBackup.toggle(warningVisible);
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.onPresetSourcesShowClick = function() {
|
||||||
|
this.presetsSourcesDialog.show().then(() => {
|
||||||
|
this.reload();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.onSaveConfigClick = function() {
|
||||||
|
const waitingDialog = GUI.showWaitDialog({title: i18n.getMessage("presetsLoadingDumpAll"), buttonCancelCallback: null});
|
||||||
|
|
||||||
|
const saveFailedDialogSettings = {
|
||||||
|
title: i18n.getMessage("warningTitle"),
|
||||||
|
text: i18n.getMessage("dumpAllNotSavedWarning"),
|
||||||
|
buttonConfirmText: i18n.getMessage("close"),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.activateCli()
|
||||||
|
.then(() => this.readDumpAll())
|
||||||
|
.then(cliStrings => {
|
||||||
|
const prefix = 'cli_backup';
|
||||||
|
const suffix = 'txt';
|
||||||
|
const text = cliStrings.join("\n");
|
||||||
|
const filename = generateFilename(prefix, suffix);
|
||||||
|
return GUI.saveToTextFileDialog(text, filename, suffix);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
waitingDialog.close();
|
||||||
|
this.cliEngine.sendLine(CliEngine.s_commandExit);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
waitingDialog.close();
|
||||||
|
return GUI.showInformationDialog(saveFailedDialogSettings);
|
||||||
|
})
|
||||||
|
.then(() => this.cliEngine.sendLine(CliEngine.s_commandExit));
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.readDumpAll = function() {
|
||||||
|
let lastCliStringReceived = performance.now();
|
||||||
|
const diffAll = [CliEngine.s_commandDefaultsNoSave, ""];
|
||||||
|
const readingDumpIntervalName = "PRESETS_READING_DUMP_INTERVAL";
|
||||||
|
|
||||||
|
this.cliEngine.subscribeOnRowCame(str => {
|
||||||
|
lastCliStringReceived = performance.now();
|
||||||
|
|
||||||
|
if (CliEngine.s_commandDiffAll !== str && CliEngine.s_commandSave !== str) {
|
||||||
|
diffAll.push(str);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cliEngine.sendLine(CliEngine.s_commandDiffAll);
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
GUI.interval_add(readingDumpIntervalName, () => {
|
||||||
|
const currentTime = performance.now();
|
||||||
|
if (currentTime - lastCliStringReceived > 500) {
|
||||||
|
this.cliEngine.unsubscribeOnRowCame();
|
||||||
|
GUI.interval_remove(readingDumpIntervalName);
|
||||||
|
resolve(diffAll);
|
||||||
|
}
|
||||||
|
}, 500, false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.onLoadConfigClick = function() {
|
||||||
|
GUI.readTextFileDialog("txt")
|
||||||
|
.then(text => {
|
||||||
|
if (text) {
|
||||||
|
const cliStrings = text.split("\n");
|
||||||
|
const pickedPreset = new PickedPreset({title: "user configuration"}, cliStrings);
|
||||||
|
this.pickedPresetList.push(pickedPreset);
|
||||||
|
this.onSaveClick();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.onHtmlLoad = function(callback) {
|
||||||
|
i18n.localizePage();
|
||||||
|
TABS.presets.adaptPhones();
|
||||||
|
this.readDom();
|
||||||
|
this.setupMenuButtons();
|
||||||
|
this.setupBackupWarning();
|
||||||
|
this._inputTextFilter.attr("placeholder", "example: \"karate racing\", or \"5'' freestyle\"");
|
||||||
|
|
||||||
|
this.presetsDetailedDialog = new PresetsDetailedDialog($("#presets_detailed_dialog"), this.pickedPresetList, () => this.onPresetPickedCallback());
|
||||||
|
this.presetsSourcesDialog = new PresetsSourcesDialog($("#presets_sources_dialog"));
|
||||||
|
|
||||||
|
this.presetsDetailedDialog.load()
|
||||||
|
.then(() => this.presetsSourcesDialog.load())
|
||||||
|
.then(() => {
|
||||||
|
this.tryLoadPresets();
|
||||||
|
GUI.content_ready(callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.onPresetPickedCallback = function() {
|
||||||
|
this.enableSaveCancelButtons(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.activateCli = function() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
CONFIGURATOR.cliEngineActive = true;
|
||||||
|
this.cliEngine.setUi($('#presets_cli_window'), $('#presets_cli_window_wrapper'), $('#presets_cli_command'));
|
||||||
|
this.cliEngine.enterCliMode();
|
||||||
|
|
||||||
|
GUI.timeout_add('presets_enter_cli_mode_done', () => {
|
||||||
|
resolve();
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.reload = function() {
|
||||||
|
this.resetInitialValues();
|
||||||
|
this.tryLoadPresets();
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.tryLoadPresets = function() {
|
||||||
|
const presetSource = this.presetsSourcesDialog.getActivePresetSource();
|
||||||
|
|
||||||
|
if (PresetSource.isUrlGithubRepo(presetSource.url)) {
|
||||||
|
this.presetsRepo = new PresetsGithubRepo(presetSource.url, presetSource.gitHubBranch);
|
||||||
|
} else {
|
||||||
|
this.presetsRepo = new PresetsWebsiteRepo(presetSource.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._divMainContent.toggle(false);
|
||||||
|
this._divGlobalLoadingError.toggle(false);
|
||||||
|
this._divGlobalLoading.toggle(true);
|
||||||
|
this._domWarningNotOfficialSource.toggle(!this.presetsSourcesDialog.isOfficialActive);
|
||||||
|
|
||||||
|
this.presetsRepo.loadIndex().then(() => {
|
||||||
|
this.prepareFilterFields();
|
||||||
|
this._divGlobalLoading.toggle(false);
|
||||||
|
this._divMainContent.toggle(true);
|
||||||
|
}).catch(err => {
|
||||||
|
this._divGlobalLoading.toggle(false);
|
||||||
|
this._divGlobalLoadingError.toggle(true);
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.prepareFilterFields = function() {
|
||||||
|
this._freezeSearch = true;
|
||||||
|
this.prepareFilterSelectField(this._selectCategory, this.presetsRepo.index.uniqueValues.category);
|
||||||
|
this.prepareFilterSelectField(this._selectKeyword, this.presetsRepo.index.uniqueValues.keywords);
|
||||||
|
this.prepareFilterSelectField(this._selectAuthor, this.presetsRepo.index.uniqueValues.author);
|
||||||
|
this.prepareFilterSelectField(this._selectFirmwareVersion, this.presetsRepo.index.uniqueValues.firmware_version);
|
||||||
|
this.prepareFilterSelectField(this._selectStatus, this.presetsRepo.index.settings.PresetStatusEnum);
|
||||||
|
|
||||||
|
this.preselectFilterFields();
|
||||||
|
this._inputTextFilter.on('input', () => this.updateSearchResults());
|
||||||
|
|
||||||
|
this._freezeSearch = false;
|
||||||
|
|
||||||
|
this.updateSearchResults();
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.preselectFilterFields = function() {
|
||||||
|
this._selectCategory.multipleSelect('setSelects', ["TUNE"]);
|
||||||
|
|
||||||
|
const currentVersion = FC.CONFIG.flightControllerVersion;
|
||||||
|
const selectedVersions = [];
|
||||||
|
|
||||||
|
for(const bfVersion of this.presetsRepo.index.uniqueValues.firmware_version) {
|
||||||
|
if (currentVersion.startsWith(bfVersion)) {
|
||||||
|
selectedVersions.push(bfVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._selectFirmwareVersion.multipleSelect('setSelects', selectedVersions);
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.prepareFilterSelectField = function(domSelectElement, selectOptions) {
|
||||||
|
domSelectElement.multipleSelect("destroy");
|
||||||
|
domSelectElement.multipleSelect({
|
||||||
|
data: selectOptions,
|
||||||
|
placeholder: i18n.getMessage("dropDownAll"),
|
||||||
|
onClick: () => { this.updateSearchResults(); },
|
||||||
|
onCheckAll: () => { this.updateSearchResults(); },
|
||||||
|
onUncheckAll: () => { this.updateSearchResults(); },
|
||||||
|
formatSelectAll () { return i18n.getMessage("dropDownSelectAll"); },
|
||||||
|
formatAllSelected() { return i18n.getMessage("dropDownAll"); },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.updateSearchResults = function() {
|
||||||
|
if (!this._freezeSearch)
|
||||||
|
{
|
||||||
|
const searchParams = {
|
||||||
|
categories: this._selectCategory.multipleSelect("getSelects", "text"),
|
||||||
|
keywords: this._selectKeyword.multipleSelect("getSelects", "text"),
|
||||||
|
authors: this._selectAuthor.multipleSelect("getSelects", "text"),
|
||||||
|
firmwareVersions: this._selectFirmwareVersion.multipleSelect("getSelects", "text"),
|
||||||
|
status: this._selectStatus.multipleSelect("getSelects", "text"),
|
||||||
|
searchString: this._inputTextFilter.val().trim(),
|
||||||
|
};
|
||||||
|
|
||||||
|
searchParams.authors = searchParams.authors.map(str => str.toLowerCase());
|
||||||
|
|
||||||
|
const fitPresets = this.getFitPresets(searchParams);
|
||||||
|
this.displayPresets(fitPresets);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.displayPresets = function(fitPresets) {
|
||||||
|
this._presetPanels.forEach(presetPanel => {
|
||||||
|
presetPanel.remove();
|
||||||
|
});
|
||||||
|
this._presetPanels = [];
|
||||||
|
|
||||||
|
const maxPresetsToShow = 60;
|
||||||
|
this._domListTooManyFound.toggle(fitPresets.length > maxPresetsToShow);
|
||||||
|
fitPresets.length = Math.min(fitPresets.length, maxPresetsToShow);
|
||||||
|
|
||||||
|
this._domListNoFound.toggle(fitPresets.length === 0);
|
||||||
|
|
||||||
|
fitPresets.forEach(preset => {
|
||||||
|
const presetPanel = new PresetTitlePanel(this._divPresetList, preset, true);
|
||||||
|
presetPanel.load();
|
||||||
|
this._presetPanels.push(presetPanel);
|
||||||
|
presetPanel.subscribeClick(this.presetsDetailedDialog, this.presetsRepo);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._domListTooManyFound.appendTo(this._divPresetList);
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.getFitPresets = function(searchParams) {
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
for(const preset of this.presetsRepo.index.presets) {
|
||||||
|
if(this.isPresetFitSearch(preset, searchParams)) {
|
||||||
|
result.push(preset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.isPresetFitSearchStatuses = function(preset, searchParams) {
|
||||||
|
return 0 === searchParams.status.length || searchParams.status.includes(preset.status);
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.isPresetFitSearchCategories = function(preset, searchParams) {
|
||||||
|
if (0 !== searchParams.categories.length) {
|
||||||
|
if (undefined === preset.category) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!searchParams.categories.includes(preset.category)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.isPresetFitSearchKeywords = function(preset, searchParams) {
|
||||||
|
if (0 !== searchParams.keywords.length) {
|
||||||
|
if (!Array.isArray(preset.keywords)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keywordsIntersection = searchParams.keywords.filter(value => preset.keywords.includes(value));
|
||||||
|
if (0 === keywordsIntersection.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.isPresetFitSearchAuthors = function(preset, searchParams) {
|
||||||
|
if (0 !== searchParams.authors.length) {
|
||||||
|
if (undefined === preset.author) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!searchParams.authors.includes(preset.author.toLowerCase())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.isPresetFitSearchFirmwareVersions = function(preset, searchParams) {
|
||||||
|
if (0 !== searchParams.firmwareVersions.length) {
|
||||||
|
if (!Array.isArray(preset.firmware_version)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firmwareVersionsIntersection = searchParams.firmwareVersions.filter(value => preset.firmware_version.includes(value));
|
||||||
|
if (0 === firmwareVersionsIntersection.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TABS.presets.isPresetFitSearchString = function(preset, searchParams) {
|
||||||
|
if (searchParams.searchString)
|
||||||
|
{
|
||||||
|
const allKeywords = preset.keywords.join(" ");
|
||||||
|
const totalLine = [preset.description, allKeywords, preset.title, preset.author].join("\n").toLowerCase().replace("''", "\"");
|
||||||
|
const allWords = searchParams.searchString.toLowerCase().replace("''", "\"").split(" ");
|
||||||
|
|
||||||
|
for (const word of allWords) {
|
||||||
|
if (!totalLine.includes(word)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TABS.presets.isPresetFitSearch = function(preset, searchParams) {
|
||||||
|
if (preset.hidden) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isPresetFitSearchStatuses(preset, searchParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isPresetFitSearchCategories(preset, searchParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isPresetFitSearchKeywords(preset, searchParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isPresetFitSearchAuthors(preset, searchParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isPresetFitSearchFirmwareVersions(preset, searchParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isPresetFitSearchString(preset, searchParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.adaptPhones = function() {
|
||||||
|
if (GUI.isCordova()) {
|
||||||
|
UI_PHONES.initToolbar();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.read = function(readInfo) {
|
||||||
|
TABS.presets.cliEngine.readSerial(readInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.cleanup = function(callback) {
|
||||||
|
this.resetInitialValues();
|
||||||
|
|
||||||
|
if (!(CONFIGURATOR.connectionValid && CONFIGURATOR.cliEngineActive && CONFIGURATOR.cliEngineValid)) {
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TABS.presets.cliEngine.close(() => {
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.resetInitialValues = function() {
|
||||||
|
CONFIGURATOR.cliEngineActive = false;
|
||||||
|
CONFIGURATOR.cliEngineValid = false;
|
||||||
|
TABS.presets.presetsRepo = null;
|
||||||
|
TABS.presets.pickedPresetList.length = 0;
|
||||||
|
this._domProgressDialog.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.presets.expertModeChanged = function(expertModeEnabled) {
|
||||||
|
this._domShowHideCli.toggle(expertModeEnabled);
|
||||||
|
};
|
13
yarn.lock
13
yarn.lock
|
@ -6311,6 +6311,11 @@ multipipe@^0.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
duplexer2 "0.0.2"
|
duplexer2 "0.0.2"
|
||||||
|
|
||||||
|
multiple-select@^1.5.2:
|
||||||
|
version "1.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/multiple-select/-/multiple-select-1.5.2.tgz#a7290785fc10fa4b9f26d2a92937effc63648bc8"
|
||||||
|
integrity sha512-sTNNRrjnTtB1b1+HTKcjQ/mjWY7Gvigo9F3C/3oTQCTFEpYzwaRYFPRAOu2SogfA1hEfyJTXjyS1VAbanJMsmA==
|
||||||
|
|
||||||
murmur-32@^0.1.0:
|
murmur-32@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/murmur-32/-/murmur-32-0.1.0.tgz#c1a79d4fc5fabf0405749d0aff77c41402055861"
|
resolved "https://registry.yarnpkg.com/murmur-32/-/murmur-32-0.1.0.tgz#c1a79d4fc5fabf0405749d0aff77c41402055861"
|
||||||
|
@ -10155,10 +10160,10 @@ yargs@~3.10.0:
|
||||||
decamelize "^1.0.0"
|
decamelize "^1.0.0"
|
||||||
window-size "0.1.0"
|
window-size "0.1.0"
|
||||||
|
|
||||||
yarn@^1.22.0:
|
yarn@^1.22.17:
|
||||||
version "1.22.5"
|
version "1.22.17"
|
||||||
resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.5.tgz#1933b7635429ca00847222dd9d38f05646e2df23"
|
resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.17.tgz#bf910747d22497b573131f7341c0e1d15c74036c"
|
||||||
integrity sha512-5uzKXwdMc++mYktXqkfpNYT9tY8ViWegU58Hgbo+KXzrzzhEyP1Ip+BTtXloLrXNcNlxFJbLiFKGaS9vK9ym6Q==
|
integrity sha512-H0p241BXaH0UN9IeH//RT82tl5PfNraVpSpEoW+ET7lmopNC61eZ+A+IDvU8FM6Go5vx162SncDL8J1ZjRBriQ==
|
||||||
|
|
||||||
yauzl@2.4.1:
|
yauzl@2.4.1:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue