1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-26 01:35:28 +03:00

Cordova framework integration, Android support, mobile UI & options tab

Cordova integration and android platform :
- Added cordova directory with required config
- Added cordova applications generation in gulpfile
- Added cordova development instructions
- Used cordova plugins to simulate missing chrome api  plugins (chrome.serial and chrome.fileSystem)
- Added cordova clipboard support
- Added android operating system and Cordova gui mode
- Fixed some css and js files to make them working on Android as well as on computers
- Added --skipdep argument to accelerate cordova build (gulp task)
- Added a webview helper to help people to update the webview app of their device

New options tab :
- Added options tab replacing the options dropdown
- Added option to switch between phones UI and computers UI

Mobile interface and global interface improvements :
- Simplified the structure of the header with flex css
- Made headerbar and tab container responsive (compact headerbar and side menu)
- All tabs are adapted to mobile interface (except firmware flasher)
- The servos and adjustments tabs are not fully adapted but are "usable"
- Improved header bar animation
- Improved log expandation animation
- Added swipe gesture to toggle side menu

Fixes during the development :
- Logo position
- Dark mode
- Auto connection
- Error messages (cordova_chromeapi.js)
- Responsive grid
- Testing
- Disconnection
- Width of boxes inside the OSD tab
- Fixed cli tab
- OSD tab
- Motor stop switch
- White spaces in boxes
- Dialogs size
- Connect button state
- Prevent tablet with a height larger than 575px to switch to computers ui
- Fixed logging tab
- Fixed code smell
- Fixed yarn cordova plugin install issue
- Fixed content_wrapper
- Fixed vibrations when scrolling
- Fixed scrolling bar alignment
- Fixed dialogReportProblem height
- Fixed rates logo
- Fixed auto connection default value (true)
- Fixed D to D max
- Fixed dialogs

Added required messages in locales/en/messages.json file

Requested changes
This commit is contained in:
WalcoFPV 2020-07-03 16:18:55 +02:00
parent ea880840a8
commit 4f93e54ae6
99 changed files with 9095 additions and 3015 deletions

View file

@ -36,7 +36,7 @@ Clipboard._configureClipboardAsNwJs = function(nwGui) {
if (onSuccess) {
onSuccess(text);
}
}
};
this.readText = function(onSuccess, onError) {
@ -54,8 +54,8 @@ Clipboard._configureClipboardAsNwJs = function(nwGui) {
if (onSuccess) {
onSuccess(text);
}
}
}
};
};
Clipboard._configureClipboardAsChrome = function() {
@ -69,15 +69,33 @@ Clipboard._configureClipboardAsChrome = function() {
navigator.clipboard.writeText(text)
.then(onSuccess)
.catch(onError);
}
};
this.readText = function(onSuccess, onError) {
navigator.clipboard.readText()
.then(onSuccess)
.catch(onError);
}
};
}
};
Clipboard._configureClipboardAsCordova = function() {
console.log('Cordova Clipboard available');
this.available = true;
this.readAvailable = true;
this.writeAvailable = true;
this.writeText = function(text, onSuccess, onError) {
cordova.plugins.clipboard.copy(text, onSuccess, onError);
};
this.readText = function(onSuccess, onError) {
cordova.plugins.clipboard.paste(onSuccess, onError);
};
};
Clipboard._configureClipboardAsOther = function() {
@ -89,12 +107,12 @@ Clipboard._configureClipboardAsOther = function() {
this.writeText = function(text, onSuccess, onError) {
onError('Clipboard not available');
}
};
this.readText = function(onSuccess, onError) {
onError('Clipboard not available');
}
}
};
};
switch (GUI.Mode) {
@ -106,6 +124,10 @@ case GUI_Modes.ChromeApp:
Clipboard._configureClipboardAsChrome();
break;
case GUI_Modes.Cordova:
Clipboard._configureClipboardAsCordova();
break;
default:
Clipboard._configureClipboardAsOther();
}

View file

@ -172,9 +172,10 @@ Features.prototype.generateElements = function (featuresElements) {
+ self._features[i].name
+ '" title="'
+ self._features[i].name
+ '" type="checkbox"/></td>'
+ '" type="checkbox"/></td><td><div>'
+ featureName
+ '<td><span i18n="feature' + self._features[i].name + '"></span>'
+ '</div><span class="xs" i18n="feature' + self._features[i].name + '"></span></td>'
+ '<td><span class="sm-min" i18n="feature' + self._features[i].name + '"></span>'
+ feature_tip_html + '</td></tr>');
var feature_e = newElement.find('input.feature');

View file

@ -318,7 +318,8 @@ function configuration_restore(callback) {
};
reader.onloadend = function (e) {
if (e.total != 0 && e.total == e.loaded) {
if ((e.total != 0 && e.total == e.loaded) || GUI.isCordova()) {
// Cordova: Ignore verification : seem to have a bug with progressEvent returned
console.log('Read SUCCESSFUL');
try { // check if string provided is a valid JSON

418
src/js/cordova_chromeapi.js vendored Normal file
View file

@ -0,0 +1,418 @@
'use strict';
const chromeCallbackWithError = function(message, callback) {
let err;
if (typeof message === 'string') {
err = { 'message' : message };
} else {
err = message;
}
if (typeof callback !== 'function') {
console.error(err.message);
return;
}
try {
if (typeof chrome.runtime !== 'undefined') {
chrome.runtime.lastError = err;
} else {
console.error(err.message);
}
callback.apply(null, Array.prototype.slice.call(arguments, 2));
} finally {
if (typeof chrome.runtime !== 'undefined') {
delete chrome.runtime.lastError;
}
}
};
const chromeCallbackWithSuccess = function(argument, callback) {
if (typeof callback === 'function') {
if (typeof argument === 'undefined') {
callback();
} else {
callback(argument);
}
}
};
const removeItemOfAnArray = async function (array, item) {
for (let i = (array.length - 1); i >= 0; i--) {
if (array[i] === item) {
return array.splice(i, 1);
}
}
return array;
};
const chromeapiSerial = {
logHeader: 'SERIAL (adapted from Cordova): ',
connection: {
connectionId: 1, // Only one connection possible
paused: false,
persistent: false,
name,
bufferSize: 4096,
receiveTimeout: 0,
sendTimeout: 0,
bitrate: 9600,
dataBits: 'eight',
parityBit: 'no',
stopBits: 'one',
ctsFlowControl: false,
},
setConnectionOptions: function(ConnectionOptions) {
if (ConnectionOptions.persistent) {
this.connection.persistent = ConnectionOptions.persistent;
}
if (ConnectionOptions.name) {
this.connection.name = ConnectionOptions.name;
}
if (ConnectionOptions.bufferSize) {
this.connection.bufferSize = ConnectionOptions.bufferSize;
}
if (ConnectionOptions.receiveTimeout) {
this.connection.receiveTimeout = ConnectionOptions.receiveTimeout;
}
if (ConnectionOptions.sendTimeout) {
this.connection.sendTimeout = ConnectionOptions.sendTimeout;
}
if (ConnectionOptions.bitrate) {
this.connection.bitrate = ConnectionOptions.bitrate;
}
if (ConnectionOptions.dataBits) {
this.connection.dataBits = ConnectionOptions.dataBits;
}
if (ConnectionOptions.parityBit) {
this.connection.parityBit = ConnectionOptions.parityBit;
}
if (ConnectionOptions.stopBits) {
this.connection.stopBits = ConnectionOptions.stopBits;
}
if (ConnectionOptions.ctsFlowControl) {
this.connection.ctsFlowControl = ConnectionOptions.ctsFlowControl;
}
},
getCordovaSerialConnectionOptions: function() {
let dataBits, stopBits, parityBit;
if (this.connection.dataBits === 'seven') {
dataBits = 7;
} else {
dataBits = 8;
}
if (this.connection.stopBits === 'two') {
stopBits = 2;
} else {
stopBits = 1;
}
if (this.connection.parityBit === 'odd') {
parityBit = 0;
} else if (this.connection.parityBit === 'even') {
parityBit = 1;
}
return {
baudRate: this.connection.bitrate,
dataBits: dataBits,
stopBits: stopBits,
parity: parityBit,
sleepOnPause: false,
};
},
// Chrome serial API methods
getDevices: async function(callback) {
const self = this;
cordova.plugins.usbevent.listDevices(function(list) {
const devices = [];
if (list.devices !== undefined) {
let count = 0;
list.devices.forEach(device => {
count++;
devices.push({
path: `${device.vendorId}/${device.productId}`,
vendorId: device.vendorId,
productId: device.productId,
displayName: `${device.vendorId}/${device.productId}`,
});
if (count === list.devices.length) {
if (callback) {
callback(devices);
}
}
});
} else {
if (callback) {
callback(devices);
}
}
}, function(error) {
chromeCallbackWithError(self.logHeader+error, callback);
});
},
connect: function(path, ConnectionOptions, callback) {
const self = this;
if (typeof ConnectionOptions !== 'undefined') {
self.setConnectionOptions(ConnectionOptions);
}
const pathSplit = path.split('/');
if (pathSplit.length === 2) {
const vid = parseInt(pathSplit[0]);
const pid = parseInt(pathSplit[1]);
console.log(`${self.logHeader}request permission (vid=${vid} / pid=${pid})`);
cordova_serial.requestPermission({vid: vid, pid: pid}, function() {
const options = self.getCordovaSerialConnectionOptions();
cordova_serial.open(options, function () {
cordova_serial.registerReadCallback(function (data) {
const info = {
connectionId: self.connection.connectionId,
data: data,
};
self.onReceive.receiveData(info);
}, function () {
console.warn(`${self.logHeader}failed to register read callback`);
});
chromeCallbackWithSuccess(self.connection, callback);
}, function(error) {
chromeCallbackWithError(self.logHeader+error, callback);
});
}, function(error) {
chromeCallbackWithError(self.logHeader+error, callback);
});
} else {
chromeCallbackWithError(`${self.logHeader} invalid vendor id / product id`, callback);
}
},
disconnect: function(connectionId, callback) {
const self = this;
cordova_serial.close(function () {
chromeCallbackWithSuccess(true, callback);
}, function(error) {
chromeCallbackWithError(self.logHeader+error, callback(false));
});
},
setPaused: function(connectionId, paused, callback) {
this.connection.paused = paused; // Change connectionInfo but don't pause the connection
chromeCallbackWithSuccess(undefined, callback);
},
getInfo: function(callback) {
chromeCallbackWithSuccess(this.connection, callback);
},
send: function(connectionId, data, callback) {
const string = Array.prototype.map.call(new Uint8Array(data), x => (`00${x.toString(16)}`).slice(-2)).join('');
cordova_serial.writeHex(string, function () {
chromeCallbackWithSuccess({
bytesSent: string.length >> 1,
}, callback);
}, function(error) {
const info = {
bytesSent: 0,
error: 'undefined',
};
chrome.serial.onReceiveError.receiveError(info);
chromeCallbackWithError(`SERIAL (adapted from Cordova): ${error}`, callback(info));
});
},
getControlSignals: function(connectionId, callback) {
// Not supported yet
console.warn('chrome.serial.getControlSignals not supported yet');
chromeCallbackWithSuccess({}, callback);
},
setControlSignals: function(connectionId, signals, callback) {
// Not supported yet
console.warn('chrome.serial.setControlSignals not supported yet');
chromeCallbackWithSuccess({
result: false,
}, callback);
},
// update: function() { },
// getConnections: function() { },
// flush: function() { },
// setBreak: function() { },
// clearBreak: function() { },
onReceive: {
listeners: [],
addListener: function(functionReference) {
this.listeners.push(functionReference);
},
removeListener: async function(functionReference) {
this.listeners = await removeItemOfAnArray(this.listeners, functionReference);
},
receiveData: function(data) {
if (data.data.byteLength > 0) {
for (let i = (this.listeners.length - 1); i >= 0; i--) {
this.listeners[i](data);
}
}
},
},
onReceiveError: {
listeners: [],
addListener: function(functionReference) {
this.listeners.push(functionReference);
},
removeListener: async function(functionReference) {
this.listeners = await removeItemOfAnArray(this.listeners, functionReference);
},
receiveError: function(error) {
for (let i = (this.listeners.length - 1); i >= 0; i--) {
this.listeners[i](error);
}
},
},
};
const chromeapiFilesystem = {
logHeader: 'FILESYSTEM (adapted from Cordova): ',
savedEntries: [],
getFileExtension: function(fileName) {
const re = /(?:\.([^.]+))?$/;
return re.exec(fileName)[1];
},
// Chrome fileSystem API methods
getDisplayPath: function(entry, callback) {
chromeCallbackWithSuccess(entry.fullPath, callback);
},
getWritableEntry: function(entry, callback) {
// Entry returned by chooseEntry method is writable on Android
chromeCallbackWithSuccess(entry, callback);
},
isWritableEntry: function(entry, callback) {
// Entry returned by chooseEntry method is writable on Android
chromeCallbackWithSuccess(true, callback);
},
chooseEntryOpenFile: function(options, callback) {
const self = this;
fileChooser.open(function(uri) {
window.resolveLocalFileSystemURL(uri, function(entry) {
if (options.accepts && options.accepts[0].extensions && options.accepts[0].extensions && options.accepts[0].extensions.length > 0) {
self.getDisplayPath(entry, function(fileName) {
const extension = self.getFileExtension(fileName);
if (options.accepts[0].extensions.indexOf(extension) > -1) {
chromeCallbackWithSuccess(entry, callback);
} else {
navigator.notification.alert('Invalid file extension', function() {
chromeCallbackWithError(`${self.logHeader}file opened has an incorrect extension`, callback);
}, 'Choose a file', 'Ok');
}
});
} else {
console.log('no extensions : any type of file accepted');
chromeCallbackWithSuccess(entry, callback);
}
}, function(error) {
chromeCallbackWithError(self.logHeader+error, callback);
});
}, function(error) {
chromeCallbackWithError(self.logHeader+error, callback);
});
},
chooseEntrySaveFile: function(options, callback) {
const self = this;
if (!options.suggestedName) {
options.suggestedName = 'newfile';
}
const extension = self.getFileExtension(options.suggestedName);
const folder = 'Betaflight configurator';
navigator.notification.prompt(i18n.getMessage('dialogFileNameDescription', {
folder: folder,
}), function(res) {
if (res.buttonIndex === 1) {
const newExtension = self.getFileExtension(res.input1);
let fileName = res.input1;
if (newExtension === undefined) {
fileName += `.${extension}`;
}
window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory, function(rootEntry) {
rootEntry.getDirectory(folder, { create: true }, function(directoryEntry) {
directoryEntry.getFile(fileName, { create: false }, function(fileEntry) {
console.log(fileEntry);
navigator.notification.confirm(i18n.getMessage('dialogFileAlreadyExistsDescription'), function(resp) {
if (resp === 1) {
chromeCallbackWithSuccess(fileEntry, callback);
} else {
chromeCallbackWithError(`${self.logHeader}Canceled: file already exists`, callback);
}
}, i18n.getMessage('dialogFileAlreadyExistsTitle'), [i18n.getMessage('yes'), i18n.getMessage('cancel')]);
}, function() {
directoryEntry.getFile(fileName, { create: true }, function(fileEntry) {
chromeCallbackWithSuccess(fileEntry, callback);
}, function(error) {
chromeCallbackWithError(self.logHeader+error, callback);
});
});
}, function(error) {
chromeCallbackWithError(self.logHeader+error, callback);
});
}, function(error) {
chromeCallbackWithError(self.logHeader+error, callback);
});
} else {
chromeCallbackWithError(`${self.logHeader}Canceled: no file name`, callback);
}
}, i18n.getMessage('dialogFileNameTitle'), [i18n.getMessage('initialSetupButtonSave'), i18n.getMessage('cancel')], options.suggestedName);
},
chooseEntry: function(options, callback) {
const self = this;
if (typeof options === 'undefined' || typeof options.type === 'undefined') {
self.chooseEntryOpenFile(options, callback);
} else if (options.type === 'openDirectory') {
// not supported yet
console.warn('chrome.fileSystem.chooseEntry: options.type = openDirectory not supported yet');
chromeCallbackWithSuccess(undefined, callback);
} else if (options.type === 'openWritableFile') {
// Entry returned by chooseEntry method is writable on Android
self.chooseEntryOpenFile(options, callback);
} else if (options.type === 'saveFile') {
self.chooseEntrySaveFile(options, callback);
} else {
self.chooseEntryOpenFile(options, callback);
}
},
restoreEntry: function(id, callback) {
this.isRestorable(id, function(isRestorable) {
if (isRestorable) {
chromeCallbackWithSuccess(this.savedEntries[id], callback);
} else {
chromeCallbackWithError(`${self.logHeader}This entry can't be restored`, callback);
}
});
},
isRestorable: function(id, callback) {
if (typeof this.savedEntries[id] !== 'undefined') {
chromeCallbackWithSuccess(true, callback);
} else {
chromeCallbackWithSuccess(false, callback);
}
},
retainEntry: function(entry) {
const id = this.savedEntries.length;
if (id >= 500) {
for (let i=0 ; i<500 ; i++) {
if (i < 499) {
this.savedEntries[i] = this.savedEntries[i+1];
} else {
this.savedEntries[i] = entry;
}
}
return 499;
} else {
this.savedEntries[id] = entry;
return id;
}
},
/**requestFileSystem: function(options, callback) { },
getVolumeList: function(callback) { },*/
};
const cordovaChromeapi = {
init: function(callback) {
chrome.serial = chromeapiSerial;
chrome.fileSystem = chromeapiFilesystem;
if (callback) {
callback();
}
},
};

67
src/js/cordova_startup.js vendored Normal file
View file

@ -0,0 +1,67 @@
'use strict';
const cordovaUI = {
uiZoom: 1,
canChangeUI: true,
init: async function() {
const self = this;
const screenWidth = $(window).width();
const screenHeight = $(window).height();
let length;
let orientation;
if (screenWidth > screenHeight) {
length = screenWidth;
orientation = 'landscape';
} else {
length = screenHeight;
orientation = 'portrait';
}
if (length < 1024) {
self.uiZoom = length/1024;
}
if (screenWidth > 575 && screenHeight > 575) {
self.canChangeUI = false;
}
ConfigStorage.get('cordovaForceComputerUI', function (result) {
if (result.cordovaForceComputerUI === undefined) {
if ((orientation === 'landscape' && screenHeight <= 575)
|| (orientation === 'portrait' && screenWidth <= 575)) {
ConfigStorage.set({'cordovaForceComputerUI': false});
} else {
ConfigStorage.set({'cordovaForceComputerUI': true});
}
}
});
self.set();
},
set: function() {
const self = this;
ConfigStorage.get('cordovaForceComputerUI', function (result) {
if (result.cordovaForceComputerUI) {
window.screen.orientation.lock('landscape');
$('body').css('zoom', self.uiZoom);
} else {
window.screen.orientation.lock('portrait');
$('body').css('zoom', 1);
}
});
},
};
const cordovaApp = {
initialize: function() {
this.bindEvents();
},
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
onDeviceReady: function() {
$('.open_firmware_flasher, .tab_firmware_flasher').hide();
cordovaUI.init();
navigator.splashscreen.hide();
cordovaChromeapi.init();
appReady();
},
};
cordovaApp.initialize();

View file

@ -19,6 +19,7 @@ var GUI_control = function () {
'changelog',
'firmware_flasher',
'privacy_policy',
'options',
'help'
];
this.defaultAllowedFCTabsWhenConnected = [
@ -48,33 +49,51 @@ var GUI_control = function () {
this.allowedTabs = this.defaultAllowedTabsWhenDisconnected;
// check which operating system is user running
if (navigator.appVersion.indexOf("Win") != -1) this.operating_system = "Windows";
else if (navigator.appVersion.indexOf("Mac") != -1) this.operating_system = "MacOS";
else if (navigator.appVersion.indexOf("CrOS") != -1) this.operating_system = "ChromeOS";
else if (navigator.appVersion.indexOf("Linux") != -1) this.operating_system = "Linux";
else if (navigator.appVersion.indexOf("X11") != -1) this.operating_system = "UNIX";
else this.operating_system = "Unknown";
this.operating_system = GUI_checkOperatingSystem();
// Check the method of execution
this.nwGui = null;
try {
this.nwGui = require('nw.gui');
this.Mode = GUI_Modes.NWJS;
this.nwGui = require('nw.gui');
this.Mode = GUI_Modes.NWJS;
} catch (ex) {
if (window.chrome && chrome.storage && chrome.storage.local) {
this.Mode = GUI_Modes.ChromeApp;
} else {
this.Mode = GUI_Modes.Other;
}
if (typeof cordovaApp !== 'undefined') {
this.Mode = GUI_Modes.Cordova;
} else {
if (window.chrome && chrome.storage && chrome.storage.local) {
this.Mode = GUI_Modes.ChromeApp;
} else {
this.Mode = GUI_Modes.Other;
}
}
}
};
const GUI_Modes = {
NWJS: "NW.js",
ChromeApp: "Chrome",
Cordova: "Cordova",
Other: "Other"
};
function GUI_checkOperatingSystem() {
if (navigator.appVersion.indexOf("Win") !== -1) {
return "Windows";
} else if (navigator.appVersion.indexOf("Mac") !== -1) {
return "MacOS";
} else if (navigator.appVersion.indexOf("CrOS") !== -1) {
return "ChromeOS";
} else if (navigator.appVersion.indexOf("Android") !== -1) {
return "Android";
} else if (navigator.appVersion.indexOf("Linux") !== -1) {
return "Linux";
} else if (navigator.appVersion.indexOf("X11") !== -1) {
return "UNIX";
} else {
return "Unknown";
}
}
// Timer managing methods
// name = string
@ -363,13 +382,16 @@ GUI_control.prototype.selectDefaultTabWhenConnected = function() {
};
GUI_control.prototype.isChromeApp = function () {
return this.Mode == GUI_Modes.ChromeApp;
return this.Mode === GUI_Modes.ChromeApp;
};
GUI_control.prototype.isNWJS = function () {
return this.Mode == GUI_Modes.NWJS;
return this.Mode === GUI_Modes.NWJS;
};
GUI_control.prototype.isCordova = function () {
return this.Mode === GUI_Modes.Cordova;
};
GUI_control.prototype.isOther = function () {
return this.Mode == GUI_Modes.Other;
return this.Mode === GUI_Modes.Other;
};

View file

@ -51,10 +51,12 @@ i18n.init = function(cb) {
};
i18n.changeLanguage = function(languageSelected) {
ConfigStorage.set({'userLanguageSelect': languageSelected});
i18next.changeLanguage(getValidLocale(languageSelected));
i18n.selectedLanguage = languageSelected;
GUI.log(i18n.getMessage('language_changed'));
if (typeof ConfigStorage !== 'undefined') {
ConfigStorage.set({'userLanguageSelect': languageSelected});
}
i18next.changeLanguage(getValidLocale(languageSelected));
i18n.selectedLanguage = languageSelected;
GUI.log(i18n.getMessage('language_changed'));
};
i18n.getMessage = function(messageID, parameters) {
@ -159,17 +161,22 @@ i18n.localizePage = function(forceReTranslate) {
* returns the current locale to the callback
*/
function getStoredUserLocale(cb) {
ConfigStorage.get('userLanguageSelect', function (result) {
let userLanguage = 'DEFAULT';
if (result.userLanguageSelect) {
userLanguage = result.userLanguageSelect;
}
i18n.selectedLanguage = userLanguage;
if (typeof ConfigStorage !== 'undefined') {
ConfigStorage.get('userLanguageSelect', function (result) {
let userLanguage = 'DEFAULT';
if (result.userLanguageSelect) {
userLanguage = result.userLanguageSelect;
}
i18n.selectedLanguage = userLanguage;
userLanguage = getValidLocale(userLanguage);
userLanguage = getValidLocale(userLanguage);
cb(userLanguage);
});
} else {
const userLanguage = getValidLocale('DEFAULT');
cb(userLanguage);
});
}
}
function getValidLocale(userLocale) {

View file

@ -4,6 +4,12 @@ window.googleAnalytics = analytics;
window.analytics = null;
$(document).ready(function () {
if (typeof cordovaApp === 'undefined') {
appReady();
}
});
function appReady() {
$.getJSON('version.json', function(data) {
CONFIGURATOR.version = data.version;
CONFIGURATOR.gitChangesetId = data.gitChangesetId;
@ -29,7 +35,7 @@ $(document).ready(function () {
initializeSerialBackend();
});
});
});
}
function checkSetupAnalytics(callback) {
if (!analytics) {
@ -143,13 +149,17 @@ function closeSerial() {
}
function closeHandler() {
this.hide();
if (!GUI.isCordova()) {
this.hide();
}
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'AppClose', { sessionControl: 'end' });
closeSerial();
this.close(true);
if (!GUI.isCordova()) {
this.close(true);
}
}
//Process to execute to real start the app
@ -171,14 +181,16 @@ function startProcess() {
GUI.nwGui.Shell.openExternal(url);
});
nwWindow.on('close', closeHandler);
} else if (!GUI.isOther()) {
} else if (GUI.isChromeApp()) {
chrome.app.window.onClosed.addListener(closeHandler);
// This event does not actually get fired:
chrome.runtime.onSuspend.addListener(closeHandler);
} else if (GUI.isCordova()) {
window.addEventListener('beforeunload', closeHandler);
}
$('.connect_b a.connect').removeClass('disabled');
$('#logo .version').text(CONFIGURATOR.version);
$('#logo .version, #tab_logoversion .version').text(CONFIGURATOR.version);
updateStatusBarVersion();
updateTopBarVersion();
@ -202,6 +214,10 @@ function startProcess() {
);
});
if (GUI.isCordova()) {
UI_PHONES.init();
}
const ui_tabs = $('#tabs > ul');
$('a', ui_tabs).click(function () {
if ($(this).parent().hasClass('active') === false && !GUI.tab_switch_in_progress) { // only initialize when the tab isn't already active
@ -274,6 +290,9 @@ function startProcess() {
case 'privacy_policy':
TABS.staticTab.initialize('privacy_policy', content_ready);
break;
case 'options':
TABS.options.initialize(content_ready);
break;
case 'firmware_flasher':
TABS.firmware_flasher.initialize(content_ready);
break;
@ -354,117 +373,6 @@ function startProcess() {
$('#tabs ul.mode-disconnected li a:first').click();
// options
$('a#options').click(function () {
const el = $(this);
if (!el.hasClass('active')) {
el.addClass('active');
el.after('<div id="options-window"></div>');
$('div#options-window').load('./tabs/options.html', function () {
// translate to user-selected language
i18n.localizePage();
ConfigStorage.get('permanentExpertMode', function (result) {
if (result.permanentExpertMode) {
$('div.permanentExpertMode input').prop('checked', true);
}
$('div.permanentExpertMode input').change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'permanentExpertMode': checked});
$('input[name="expertModeCheckbox"]').prop('checked', checked).change();
}).change();
});
ConfigStorage.get('rememberLastTab', function (result) {
$('div.rememberLastTab input')
.prop('checked', !!result.rememberLastTab)
.change(function() { ConfigStorage.set({rememberLastTab: $(this).is(':checked')}); })
.change();
});
if (GUI.operating_system !== 'ChromeOS') {
ConfigStorage.get('checkForConfiguratorUnstableVersions', function (result) {
if (result.checkForConfiguratorUnstableVersions) {
$('div.checkForConfiguratorUnstableVersions input').prop('checked', true);
}
$('div.checkForConfiguratorUnstableVersions input').change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'checkForConfiguratorUnstableVersions': checked});
checkForConfiguratorUpdates();
});
});
} else {
$('div.checkForConfiguratorUnstableVersions').hide();
}
ConfigStorage.get('analyticsOptOut', function (result) {
if (result.analyticsOptOut) {
$('div.analyticsOptOut input').prop('checked', true);
}
$('div.analyticsOptOut input').change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'analyticsOptOut': checked});
checkSetupAnalytics(function (analyticsService) {
if (checked) {
analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, 'OptOut');
}
analyticsService.setOptOut(checked);
if (!checked) {
analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, 'OptIn');
}
});
}).change();
});
$('div.cliAutoComplete input')
.prop('checked', CliAutoComplete.configEnabled)
.change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'cliAutoComplete': checked});
CliAutoComplete.setEnabled(checked);
}).change();
$('#darkThemeSelect')
.val(DarkTheme.configEnabled)
.change(function () {
const value = parseInt($(this).val());
ConfigStorage.set({'darkTheme': value});
setDarkTheme(value);
}).change();
function close_and_cleanup(e) {
if (e.type === 'click' && !$.contains($('div#options-window')[0], e.target) || e.type === 'keyup' && e.keyCode === 27) {
$(document).unbind('click keyup', close_and_cleanup);
$('div#options-window').slideUp(250, function () {
el.removeClass('active');
$(this).empty().remove();
});
}
}
$(document).bind('click keyup', close_and_cleanup);
$(this).slideDown(250);
});
}
});
// listen to all input change events and adjust the value within limits if necessary
$("#content").on('focus', 'input[type="number"]', function () {
const element = $(this);
@ -535,22 +443,19 @@ function startProcess() {
$("#showlog").on('click', function () {
let state = $(this).data('state');
if (state) {
$("#log").animate({height: 27}, 200, function () {
setTimeout(function() {
const command_log = $('div#log');
command_log.scrollTop($('div.wrapper', command_log).height());
});
}, 200);
$("#log").removeClass('active');
$("#content").removeClass('logopen');
$(".tab_container").removeClass('logopen');
$("#tab-content-container").removeClass('logopen');
$("#scrollicon").removeClass('active');
ConfigStorage.set({'logopen': false});
state = false;
} else {
$("#log").animate({height: 111}, 200);
$("#log").addClass('active');
$("#content").addClass('logopen');
$(".tab_container").addClass('logopen');
$("#tab-content-container").addClass('logopen');
$("#scrollicon").addClass('active');
ConfigStorage.set({'logopen': true});
@ -567,11 +472,12 @@ function startProcess() {
});
ConfigStorage.get('permanentExpertMode', function (result) {
const experModeCheckbox = 'input[name="expertModeCheckbox"]';
if (result.permanentExpertMode) {
$('input[name="expertModeCheckbox"]').prop('checked', true);
$(experModeCheckbox).prop('checked', true);
}
$('input[name="expertModeCheckbox"]').change(function () {
$(experModeCheckbox).change(function () {
const checked = $(this).is(':checked');
checkSetupAnalytics(function (analyticsService) {
analyticsService.setDimension(analyticsService.DIMENSIONS.CONFIGURATOR_EXPERT_MODE, checked ? 'On' : 'Off');
@ -819,7 +725,7 @@ function updateTopBarVersion(firmwareVersion, firmwareId, hardwareId) {
const versionText = `${configuratorVersion}<br />${firmwareVersionAndId}<br />${targetVersion}`;
$('#logo .logo_text').html(versionText);
$('#logo .logo_text, #tab_logoversion .version').html(versionText);
}
function updateStatusBarVersion(firmwareVersion, firmwareId, hardwareId) {

251
src/js/main_cordova.js Normal file
View file

@ -0,0 +1,251 @@
'use strict';
const REQUIRED_WEBVIEW_VERSION = 72;
const WEBVIEW = {
chromeVersion: '',
majorChromeVersion: 0,
appsId: {
androidWebview: 'com.android.webview',
googleWebview: 'com.google.android.webview',
chrome: 'com.android.chrome',
},
apps: {
'com.android.webview': { },
'com.google.android.webview': {
name: 'Android System WebView',
displayName: 'Google Android Webview',
},
'com.android.chrome': {
name: 'Google Chrome',
displayName: 'Chrome',
},
},
matchingVersion: 0,
usedApp: null,
uptodateApps: [],
htmlElements: {
webview_step_msg: '#webview_step_msg',
webview_step_btn1: '#webview_step_btn1',
webview_step_btn2: '#webview_step_btn2',
},
advices: {
installGoogleAndroidWebview: function(callback) {
$(WEBVIEW.htmlElements.webview_step_msg).html(i18n.getMessage('cordovaWebviewInstall', {
app: WEBVIEW.apps[WEBVIEW.appsId.googleWebview].name,
}));
$('#webview_step_btn1').text(i18n.getMessage('cordovaWebviewInstallBtn'))
.attr('app_id', WEBVIEW.appsId.googleWebview);
callback();
},
updateGoogleAndroidWebview: function(callback) {
$(WEBVIEW.htmlElements.webview_step_msg).html(i18n.getMessage('cordovaWebviewUpdate', {
app: WEBVIEW.apps[WEBVIEW.appsId.googleWebview].name,
}));
$(WEBVIEW.htmlElements.webview_step_btn1).text(i18n.getMessage('cordovaWebviewUpdateBtn'))
.attr('app_id', WEBVIEW.appsId.googleWebview);
callback();
},
updateAndroidChrome: function(callback) {
$(WEBVIEW.htmlElements.webview_step_msg).html(i18n.getMessage('cordovaWebviewUpdate', {
app: WEBVIEW.apps[WEBVIEW.appsId.chrome].name,
}));
$(WEBVIEW.htmlElements.webview_step_btn1).text(i18n.getMessage('cordovaWebviewUpdateBtn'))
.attr('app_id', WEBVIEW.appsId.chrome);
callback();
},
uninstallGoogleAndroidWebview: function(callback) {
$(WEBVIEW.htmlElements.webview_step_msg).html(i18n.getMessage('cordovaWebviewUninstall', {
app: WEBVIEW.apps[WEBVIEW.appsId.googleWebview].name,
}));
$(WEBVIEW.htmlElements.webview_step_btn1).text(i18n.getMessage('cordovaWebviewUninstallBtn1'))
.attr('app_id', WEBVIEW.appsId.googleWebview);
$(WEBVIEW.htmlElements.webview_step_btn2).text(i18n.getMessage('cordovaWebviewUninstallBtn2'))
.attr('app_id', WEBVIEW.appsId.googleWebview)
.show();
callback();
},
selectWebview: function(id, callback) {
let app;
if (id === WEBVIEW.appsId.googleWebview) {
app = WEBVIEW.apps[WEBVIEW.appsId.googleWebview].displayName;
} else if (id === WEBVIEW.appsId.chrome) {
app = WEBVIEW.apps[WEBVIEW.appsId.chrome].displayName;
}
$(WEBVIEW.htmlElements.webview_step_msg).html(i18n.getMessage('cordovaWebviewEnable', {
app: app,
}));
$(WEBVIEW.htmlElements.webview_step_btn1).hide();
$(WEBVIEW.htmlElements.webview_step_btn2).text(i18n.getMessage('cordovaWebviewEnableBtn')).show();
callback();
},
},
getAdvice1: function(callback) {
const self = this;
if (self.usedApp === WEBVIEW.appsId.googleWebview) {
self.advices.updateGoogleAndroidWebview(callback);
} else if (self.usedApp === WEBVIEW.appsId.chrome) {
self.advices.updateAndroidChrome(callback);
}
},
getAdvice2: function(callback) {
const self = this;
if (self.uptodateApps.length > 0) {
self.advices.selectWebview(callback);
} else {
if ((self.apps[WEBVIEW.appsId.googleWebview].installed && self.apps[WEBVIEW.appsId.googleWebview].enabled)
&& (self.apps[WEBVIEW.appsId.chrome].installed && self.apps[WEBVIEW.appsId.chrome].enabled)) {
self.advices.uninstallGoogleAndroidWebview(callback);
} else if (!(self.apps[WEBVIEW.appsId.googleWebview].installed && self.apps[WEBVIEW.appsId.googleWebview].enabled)
&& !(self.apps[WEBVIEW.appsId.chrome].installed && self.apps[WEBVIEW.appsId.chrome].enabled)) {
self.advices.installGoogleAndroidWebview(callback);
} else {
self.getAdvice3(callback);
}
}
},
getAdvice3: function(callback) {
const self = this;
if (self.apps[WEBVIEW.appsId.googleWebview].installed && self.apps[WEBVIEW.appsId.googleWebview].enabled
&& !self.apps[WEBVIEW.appsId.googleWebview].uptodate) {
self.advices.updateGoogleAndroidWebview(callback);
} else if (self.apps[WEBVIEW.appsId.chrome].installed && self.apps[WEBVIEW.appsId.chrome].enabled
&& !self.apps[WEBVIEW.appsId.chrome].uptodate) {
self.advices.updateAndroidChrome(callback);
}
},
getAdvice: function(callback) {
const self = this;
if (self.usedApp && self.usedApp !== WEBVIEW.appsId.androidWebview) {
this.getAdvice1(callback);
} else {
this.getAdvice2(callback);
}
},
tryToFindUsedApp: function(callback) {
const self = this;
const appsId = Object.keys(self.apps);
for (let i=0; i<appsId.length; i++) {
const id = appsId[i];
if (self.matchingVersion === 1 && self.apps[id].used === 'could') {
self.apps[id].used = 'yes';
self.usedApp = id;
$(`li[app_id='${id}']`).append(` (<span style="color: green">${i18n.getMessage('cordovaWebviewUsed')}</span>)`);
}
if (i === appsId.length-1) {
callback();
}
}
},
checkInstalledApps: function(callback) {
const self = this;
const appsId = Object.keys(self.apps);
let installedApps = 0;
function checkAvailability(id, i) {
appAvailability.check(id, function(info) {
appInstalled(info, id, i);
}, function() {
appNotInstalled(id, i);
});
}
function end(i) {
if (i === appsId.length-1) {
if (installedApps === 0) {
$('#webview_apps').append('<li i18n="cordovaNoWebview" style="color: red"></li>');
}
i18n.localizePage();
console.log('callback');
callback();
}
}
function appInstalled(info, id, i) {
installedApps++;
self.apps[id].installed = true;
self.apps[id].enabled = info.enabled;
self.apps[id].version = info.version;
self.apps[id].majorVersion = parseInt(info.version.split('.')[0]);
if (self.chromeVersion === self.apps[id].version) {
self.apps[id].used = 'could';
self.matchingVersion++;
} else {
self.apps[id].used = 'no';
}
let color;
if (self.apps[id].majorVersion >= REQUIRED_WEBVIEW_VERSION) {
color = 'green';
self.apps[id].uptodate = true;
self.uptodateApps.push(id);
} else {
color = 'red';
self.apps[id].uptodate = false;
}
let app = `<li app_id="${id}">${id} (<span style="color: ${color}">${self.apps[id].version}</span>)`;
if (!self.apps[id].enabled) {
app += ' (<span i18n="portsTelemetryDisabled"></span>)';
}
app += '</li>';
$('#webview_apps').append(app);
end(i);
}
function appNotInstalled(id, i) {
self.apps[id].installed = false;
end(i);
}
for (let i=0; i<appsId.length; i++) {
const id = appsId[i];
checkAvailability(id, i);
}
},
exec: function() {
const self = this;
$('#webview_troubleshooting').hide();
$('#loading').show();
self.chromeVersion = window.navigator.appVersion.replace(/.*Chrome\/([0-9.]*).*/, "$1");
self.majorChromeVersion = self.chromeVersion.split('.')[0];
if (self.majorChromeVersion >= REQUIRED_WEBVIEW_VERSION) {
navigator.splashscreen.show();
document.location.href = 'main.html';
} else {
navigator.splashscreen.hide();
self.checkInstalledApps(function() {
self.tryToFindUsedApp(function() {
self.getAdvice(function() {
$('#loading').hide();
$('#webview_troubleshooting').show();
});
});
});
}
},
};
const cordovaApp = {
initialize: function() {
this.bindEvents();
},
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
onDeviceReady: function() {
i18n.init(function() {
i18n.localizePage();
WEBVIEW.exec();
});
},
};
cordovaApp.initialize();
$(WEBVIEW.htmlElements.webview_step_btn1).on('click', function() {
const appId = $(WEBVIEW.htmlElements.webview_step_btn1).attr('app_id');
cordova.plugins.market.open(appId);
});
$(WEBVIEW.htmlElements.webview_step_btn2).on('click', function() {
if ($(WEBVIEW.htmlElements.webview_step_btn2).attr('app_id') !== undefined) {
const appId = $(WEBVIEW.htmlElements.webview_step_btn2).attr('app_id');
window.cordova.plugins.settings.open(['application_details', false, appId]);
} else {
window.cordova.plugins.settings.open('settings');
}
});

View file

@ -146,6 +146,8 @@ Model.prototype.resize = function () {
};
Model.prototype.dispose = function () {
this.renderer.forceContextLoss();
this.renderer.dispose();
if (this.canUseWebGLRenderer()) {
this.renderer.forceContextLoss();
this.renderer.dispose();
}
};

87
src/js/phones_ui.js Normal file
View file

@ -0,0 +1,87 @@
'use strict';
const UI_PHONES = {
background: '#background',
tabContainer: '.tab_container',
tabContentContainer: '#tab-content-container',
headerbar: '.headerbar',
init: function() {
const self = this;
$('#menu_btn').click(function() {
self.openSideMenu();
});
$(this.background).click(function() {
self.closeSideMenu();
});
$('#tabs a').click(function() {
if ($('.tab_container').hasClass('reveal')) {
self.closeSideMenu();
}
});
$('#reveal_btn').click(function() {
self.expandHeader();
});
$(`${this.background}, ${this.tabContainer}`).swipe( {
swipeLeft: function() {
self.closeSideMenu();
},
});
$('#side_menu_swipe').swipe( {
swipeRight: function() {
self.openSideMenu();
},
});
},
initToolbar: function() {
$('.toolbar_expand_btn').click(this.expandToolbar);
},
openSideMenu: function() {
$(this.background).fadeIn(300);
$(this.tabContainer).addClass('reveal');
},
closeSideMenu: function() {
$(this.background).fadeOut(300);
$(this.tabContainer).removeClass('reveal');
},
expandHeader: function() {
const self = this;
let expand, headerExpanded, reveal;
if (GUI.connected_to) {
expand = 'expand2';
headerExpanded = 'header_expanded2';
reveal = '.header-wrapper';
} else {
expand = 'expand';
headerExpanded = 'headerExpanded';
reveal = '#port-picker';
}
if ($(self.headerbar).hasClass(expand)) {
$(reveal).removeClass('reveal');
setTimeout(function() {
$(self.tabContentContainer).removeClass(headerExpanded);
$(self.headerbar).removeClass(expand);
}, 100);
} else {
$(self.tabContentContainer).addClass(headerExpanded);
$(self.headerbar).addClass(expand);
setTimeout(function() {
$(reveal).addClass('reveal');
}, 100);
}
},
expandToolbar: function() {
const toolbar = $('.content_toolbar.xs-compressed');
if (toolbar.length > 0) {
if ($('.content_toolbar.xs-compressed').hasClass('expanded')) {
toolbar.removeClass('expanded');
} else {
toolbar.addClass('expanded');
}
}
},
reset: function() {
$(this.tabContentContainer).removeClass('header_expanded2 header_expanded');
$('#port-picker, .header-wrapper').removeClass('reveal');
$(this.headerbar).removeClass('expand2 expand');
},
};

View file

@ -40,7 +40,7 @@ PortHandler.check = function () {
if (GUI.connected_to) {
for (var i = 0; i < removed_ports.length; i++) {
if (removed_ports[i] == GUI.connected_to) {
$('div#port-picker a.connect').click();
$('div#header_btns a.connect').click();
}
}
}
@ -116,7 +116,7 @@ PortHandler.check = function () {
// we need firmware flasher protection over here
if (GUI.active_tab != 'firmware_flasher') {
GUI.timeout_add('auto-connect_timeout', function () {
$('div#port-picker a.connect').click();
$('div#header_btns a.connect').click();
}, 100); // timeout so bus have time to initialize after being detected by the system
}
}
@ -164,7 +164,6 @@ PortHandler.check_usb_devices = function (callback) {
}
self.dfu_available = false;
}
if(callback) callback(self.dfu_available);
if (!$('option:selected', portPickerElement).data().isDFU) {

View file

@ -68,7 +68,7 @@ function initializeSerialBackend() {
// lock port select & baud while we are connecting / connected
$('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', true);
$('div.connect_controls a.connect_state').text(i18n.getMessage('connecting'));
$('div.connect_controls div.connect_state').text(i18n.getMessage('connecting'));
serial.connect(portName, {bitrate: selected_baud}, onOpen);
@ -107,7 +107,7 @@ function initializeSerialBackend() {
// auto-connect
ConfigStorage.get('auto_connect', function (result) {
if (result.auto_connect === 'undefined' || result.auto_connect) {
if (result.auto_connect === undefined || result.auto_connect) {
// default or enabled by user
GUI.auto_connect = true;
@ -147,6 +147,10 @@ function initializeSerialBackend() {
}
function finishClose(finishedCallback) {
if (GUI.isCordova()) {
UI_PHONES.reset();
}
var wasConnected = CONFIGURATOR.connectionValid;
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Disconnected');
@ -183,7 +187,7 @@ function finishClose(finishedCallback) {
// reset connect / disconnect button
$('div.connect_controls a.connect').removeClass('active');
$('div.connect_controls a.connect_state').text(i18n.getMessage('connect'));
$('div.connect_controls div.connect_state').text(i18n.getMessage('connect'));
// reset active sensor indicators
sensor_status(0);
@ -309,7 +313,7 @@ function onOpen(openInfo) {
}
function abortConnect() {
$('div#connectbutton a.connect_state').text(i18n.getMessage('connect'));
$('div#connectbutton div.connect_state').text(i18n.getMessage('connect'));
$('div#connectbutton a.connect').removeClass('active');
// unlock port select & baud
@ -409,6 +413,7 @@ function checkReportProblems() {
});
problemDialog.showModal();
$('#dialogReportProblems').scrollTop(0);
}
processUid();
@ -456,6 +461,10 @@ function finishOpen() {
GUI.allowedTabs.splice(GUI.allowedTabs.indexOf('led_strip'), 1);
}
if (GUI.isCordova()) {
UI_PHONES.reset();
}
onConnect();
GUI.selectDefaultTabWhenConnected();
@ -474,7 +483,7 @@ function onConnect() {
$('div#flashbutton a.flash').removeClass('active');
}
GUI.timeout_remove('connecting'); // kill connecting timer
$('div#connectbutton a.connect_state').text(i18n.getMessage('disconnect')).addClass('active');
$('div#connectbutton div.connect_state').text(i18n.getMessage('disconnect')).addClass('active');
$('div#connectbutton a.connect').addClass('active');
$('#tabs ul.mode-disconnected').hide();

View file

@ -176,9 +176,13 @@ TABS.auxiliary.initialize = function (callback) {
$(elementName + ' .channel-slider').Link('lower').to($(elementName + ' .lowerLimitValue'));
$(elementName + ' .channel-slider').Link('upper').to($(elementName + ' .upperLimitValue'));
let sliderValues = [900, 1000, 1200, 1400, 1500, 1600, 1800, 2000, 2100];
if ($(window).width() < 575) {
sliderValues = [1000, 1200, 1400, 1600, 1800, 2000];
}
$(rangeElement).find(".pips-channel-range").noUiSlider_pips({
mode: 'values',
values: [900, 1000, 1200, 1400, 1500, 1600, 1800, 2000, 2100],
values: sliderValues,
density: 4,
stepped: true
});
@ -250,7 +254,7 @@ TABS.auxiliary.initialize = function (callback) {
configureRangeTemplate(auxChannelCount);
configureLinkTemplate();
var modeTableBodyElement = $('.tab-auxiliary .modes tbody')
const modeTableBodyElement = $('.tab-auxiliary .modes');
for (var modeIndex = 0; modeIndex < AUX_CONFIG.length; modeIndex++) {
var modeId = AUX_CONFIG_IDS[modeIndex];

View file

@ -111,6 +111,8 @@ TABS.cli.initialize = function (callback) {
// translate to user-selected language
i18n.localizePage();
TABS.cli.adaptPhones();
CONFIGURATOR.cliActive = true;
var textarea = $('.tab-cli textarea[name="commands"]');
@ -320,6 +322,17 @@ TABS.cli.initialize = function (callback) {
});
};
TABS.cli.adaptPhones = function() {
if ($(window).width() < 575) {
const backdropHeight = $('.note').height() + 22 + 38;
$('.backdrop').css('height', `calc(100% - ${backdropHeight}px)`);
}
if (GUI.isCordova()) {
UI_PHONES.initToolbar();
}
};
TABS.cli.history = {
history: [],
index: 0

View file

@ -593,6 +593,11 @@ TABS.led_strip.initialize = function (callback, scrollPosition) {
updateBulkCmd();
if ($(window).width() < 575) {
const gridZoom = $('.tab_title').width() / 496;
$('.mainGrid, .gridSections').css('zoom', gridZoom);
}
GUI.content_ready(callback);
}

143
src/js/tabs/options.js Normal file
View file

@ -0,0 +1,143 @@
'use strict';
TABS.options = {};
TABS.options.initialize = function (callback) {
if (GUI.active_tab !== 'options') {
GUI.active_tab = 'options';
}
$('#content').load("./tabs/options.html", function () {
i18n.localizePage();
TABS.options.initPermanentExpertMode();
TABS.options.initRememberLastTab();
TABS.options.initCheckForConfiguratorUnstableVersions();
TABS.options.initAnalyticsOptOut();
TABS.options.initCliAutoComplete();
TABS.options.initCordovaForceComputerUI();
TABS.options.initDarkTheme();
GUI.content_ready(callback);
});
};
TABS.options.cleanup = function (callback) {
if (callback) {
callback();
}
};
TABS.options.initPermanentExpertMode = function () {
ConfigStorage.get('permanentExpertMode', function (result) {
if (result.permanentExpertMode) {
$('div.permanentExpertMode input').prop('checked', true);
}
$('div.permanentExpertMode input').change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'permanentExpertMode': checked});
$('input[name="expertModeCheckbox"]').prop('checked', checked).change();
}).change();
});
};
TABS.options.initRememberLastTab = function () {
ConfigStorage.get('rememberLastTab', function (result) {
$('div.rememberLastTab input')
.prop('checked', !!result.rememberLastTab)
.change(function() { ConfigStorage.set({rememberLastTab: $(this).is(':checked')}); })
.change();
});
};
TABS.options.initCheckForConfiguratorUnstableVersions = function () {
if (GUI.operating_system !== 'ChromeOS') {
ConfigStorage.get('checkForConfiguratorUnstableVersions', function (result) {
if (result.checkForConfiguratorUnstableVersions) {
$('div.checkForConfiguratorUnstableVersions input').prop('checked', true);
}
$('div.checkForConfiguratorUnstableVersions input').change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'checkForConfiguratorUnstableVersions': checked});
checkForConfiguratorUpdates();
});
});
} else {
$('div.checkForConfiguratorUnstableVersions').hide();
}
};
TABS.options.initAnalyticsOptOut = function () {
ConfigStorage.get('analyticsOptOut', function (result) {
if (result.analyticsOptOut) {
$('div.analyticsOptOut input').prop('checked', true);
}
$('div.analyticsOptOut input').change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'analyticsOptOut': checked});
checkSetupAnalytics(function (analyticsService) {
if (checked) {
analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, 'OptOut');
}
analyticsService.setOptOut(checked);
if (!checked) {
analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, 'OptIn');
}
});
}).change();
});
};
TABS.options.initCliAutoComplete = function () {
$('div.cliAutoComplete input')
.prop('checked', CliAutoComplete.configEnabled)
.change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'cliAutoComplete': checked});
CliAutoComplete.setEnabled(checked);
}).change();
};
TABS.options.initCordovaForceComputerUI = function () {
if (GUI.isCordova() && cordovaUI.canChangeUI) {
ConfigStorage.get('cordovaForceComputerUI', function (result) {
if (result.cordovaForceComputerUI) {
$('div.cordovaForceComputerUI input').prop('checked', true);
}
$('div.cordovaForceComputerUI input').change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'cordovaForceComputerUI': checked});
if (typeof cordovaUI.set === 'function') {
cordovaUI.set();
}
});
});
} else {
$('div.cordovaForceComputerUI').hide();
}
};
TABS.options.initDarkTheme = function () {
$('#darkThemeSelect')
.val(DarkTheme.configEnabled)
.change(function () {
const value = parseInt($(this).val());
ConfigStorage.set({'darkTheme': value});
setDarkTheme(value);
}).change();
};

View file

@ -2200,6 +2200,12 @@ TABS.osd.initialize = function (callback) {
// translate to user-selected language
i18n.localizePage();
if ($(window).width() < 390) {
const previewZoom = ($(window).width() - 30) / 360;
$('.display-layout .preview').css('zoom', previewZoom);
}
// Open modal window
OSD.GUI.fontManager = new jBox('Modal', {
width: 750,

View file

@ -2586,28 +2586,28 @@ TABS.pid_tuning.changeRatesTypeLogo = function() {
switch(self.currentRatesType) {
case self.RATES_TYPE.RACEFLIGHT:
ratesLogoElement.attr("src", "../images/rate_logos/raceflight.svg");
ratesLogoElement.attr("src", "./images/rate_logos/raceflight.svg");
break;
case self.RATES_TYPE.KISS:
ratesLogoElement.attr("src", "../images/rate_logos/kiss.svg");
ratesLogoElement.attr("src", "./images/rate_logos/kiss.svg");
break;
case self.RATES_TYPE.ACTUAL:
ratesLogoElement.attr("src", "../images/rate_logos/actual.svg");
ratesLogoElement.attr("src", "./images/rate_logos/actual.svg");
break;
case self.RATES_TYPE.QUICKRATES:
ratesLogoElement.attr("src", "../images/rate_logos/quickrates.svg");
ratesLogoElement.attr("src", "./images/rate_logos/quickrates.svg");
break;
// add future rates types here
default: // BetaFlight
ratesLogoElement.attr("src", "../images/rate_logos/betaflight.svg");
ratesLogoElement.attr("src", "./images/rate_logos/betaflight.svg");
break;
}

View file

@ -184,9 +184,11 @@ TABS.ports.initialize = function (callback, scrollPosition) {
let lastVtxControlSelected;
var ports_e = $('.tab-ports .ports');
const portIdentifierTemplateE = $('#tab-ports-templates .portIdentifier');
var port_configuration_template_e = $('#tab-ports-templates .portConfiguration');
for (var portIndex = 0; portIndex < SERIAL_CONFIG.ports.length; portIndex++) {
const portIdentifierE = portIdentifierTemplateE.clone();
var port_configuration_e = port_configuration_template_e.clone();
var serialPort = SERIAL_CONFIG.ports[portIndex];
@ -216,6 +218,7 @@ TABS.ports.initialize = function (callback, scrollPosition) {
var blackbox_baudrate_e = port_configuration_e.find('select.blackbox_baudrate');
blackbox_baudrate_e.val(blackboxBaudrate);
portIdentifierE.find('.identifier').text(portIdentifierToNameMapping[serialPort.identifier]);
port_configuration_e.find('.identifier').text(portIdentifierToNameMapping[serialPort.identifier]);
port_configuration_e.data('index', portIndex);
@ -288,6 +291,7 @@ TABS.ports.initialize = function (callback, scrollPosition) {
}
}
ports_e.find('tbody').append(portIdentifierE);
ports_e.find('tbody').append(port_configuration_e);
}

View file

@ -41,6 +41,10 @@ TABS.vtx.initialize = function (callback) {
// translate to user-selected language
i18n.localizePage();
if (GUI.isCordova()) {
UI_PHONES.initToolbar();
}
self.updating = false;
GUI.content_ready(callback);
}
@ -112,8 +116,9 @@ TABS.vtx.initialize = function (callback) {
// Load schema
const urlVtxSchema = chrome.runtime.getURL(`resources/jsonschema/vtxconfig_schema-${vtxConfig.version}.json`);
if (GUI.Mode === GUI_Modes.ChromeApp) {
if (GUI.Mode === GUI_Modes.ChromeApp || GUI.isCordova()) {
// FIXME the ChromeOs don't let us use a Schema Validator because almost all of them use eval, and/or use require
// On android : Fetch API cannot load : URL scheme "file" is not supported
callback_valid();
} else {
fetch(urlVtxSchema)