diff --git a/src/js/tabs/firmware_flasher.js b/src/js/tabs/firmware_flasher.js index 7acc8fa0..ba49ac2e 100644 --- a/src/js/tabs/firmware_flasher.js +++ b/src/js/tabs/firmware_flasher.js @@ -29,17 +29,14 @@ TABS.firmware_flasher.initialize = function (callback) { var unifiedSource = 'https://api.github.com/repos/betaflight/unified-targets/contents/configs/default'; - - /** - * Change boldness of firmware option depending on cache status - * - * @param {Descriptor} release - */ function onFirmwareCacheUpdate(release) { - $("option[value='{0}']".format(release.version)) - .css("font-weight", FirmwareCache.has(release) - ? "bold" - : "normal"); + $('select[name="firmware_version"] option').each(function () { + const option_e = $(this); + const optionRelease = option_e.data("summary"); + if (optionRelease && optionRelease.file === release.file) { + option_e.css("font-weight", FirmwareCache.has(release) ? "bold" : "normal"); + } + }); } function onDocumentLoad() { @@ -219,27 +216,23 @@ TABS.firmware_flasher.initialize = function (callback) { loadUnifiedBuilds(releases); }; - function checkOneVersionForUnification(version) { + function supportsUnifiedTargets(version) { return semver.gte(version.split(' ')[0], '4.1.0-RC1'); } - function checkBuildsForUnification(builds) { + function hasUnifiedTargetBuild(builds) { // Find a build that is newer than 4.1.0, return true if found - let foundSuitable = false; - Object.keys(builds).forEach(function (key) { - builds[key].forEach(function(target) { - if (checkOneVersionForUnification(target.version)) { - foundSuitable = true; - } + return Object.keys(builds).some(function (key) { + return builds[key].some(function(target) { + return supportsUnifiedTargets(target.version); }); }); - return foundSuitable; } function loadUnifiedBuilds(builds) { var expirationPeriod = 3600 * 2; // Two of your earth hours. var checkTime = Math.floor(Date.now() / 1000); // Lets deal in seconds. - if (builds && checkBuildsForUnification(builds)) { + if (builds && hasUnifiedTargetBuild(builds)) { console.log('loaded some builds for later'); var storageTag = 'unifiedSourceCache'; chrome.storage.local.get(storageTag, function (result) { @@ -272,46 +265,29 @@ TABS.firmware_flasher.initialize = function (callback) { } function parseUnifiedBuilds(data, builds) { - if (!data) { return; } + if (!data) { + return; + } let releases = {}; let unifiedConfigs = {}; let items = {}; - let unifiedTargetNames = []; + // Get the legacy builds + Object.keys(builds).forEach(function (targetName) { + items[targetName] = { }; + releases[targetName] = builds[targetName]; + }); + // Get the Unified Target configurations data.forEach(function(target) { - const TARGET_REGEXP = /^(?:([^-]{1,4})-)?(.*).config$/; + const TARGET_REGEXP = /^([^-]{1,4})-(.*).config$/; let targetParts = target.name.match(TARGET_REGEXP); if (!targetParts) { return; } - let boardName = targetParts[2]; - let manufacturerId = targetParts[1]; - let targetName; - let displayName; - if (manufacturerId) { - targetName = `${boardName}+${manufacturerId}`; - displayName = `${boardName} (${manufacturerId})`; - } else { - targetName = boardName; - } - unifiedTargetNames.push(boardName); - unifiedConfigs[targetName] = target.download_url; - items[targetName] = { displayName: displayName }; - // Chicken and egg problem: We need to know what Unified Target this configuration uses before reading the configuration. - // Solving this by assuming that all Unified Targets have the same availability for now. - const DEFAULT_UNIFIED_TARGET_NAME = "STM32F405"; - releases[targetName] = builds[DEFAULT_UNIFIED_TARGET_NAME]; - }); - Object.keys(builds).forEach(function (key) { - let targetName; - let displayName; - if (unifiedTargetNames.includes(key)) { - targetName = `${key}-legacy`; - displayName = i18n.getMessage("firmwareFlasherLegacyLabel", { target: key }); - } else { - targetName = key; - } - items[targetName] = { displayName: displayName }; - releases[targetName] = builds[key]; + const targetName = targetParts[2]; + const manufacturerId = targetParts[1]; + items[targetName] = { }; + unifiedConfigs[targetName] = (unifiedConfigs[targetName] || {}); + unifiedConfigs[targetName][manufacturerId] = target.download_url; }); var boards_e = $('select[name="board"]'); var versions_e = $('select[name="firmware_version"]'); @@ -326,7 +302,7 @@ TABS.firmware_flasher.initialize = function (callback) { .forEach(function(target, i) { let item = items[target]; - var select_e = $("".format(target, items[target].displayName || target)); + const select_e = $(`"`); boards_e.append(select_e); }); TABS.firmware_flasher.releases = releases; @@ -432,27 +408,84 @@ TABS.firmware_flasher.initialize = function (callback) { chrome.storage.local.set({'selected_build_type': build_type}); }); - function populateVersions(versions_element, targetVersions, target) { - versions_element.empty(); + function populateBuilds(builds, target, manufacturerId, duplicateName, targetVersions, callback) { if (targetVersions) { - versions_element.append($("".format(i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersionFor'), target))); targetVersions.forEach(function(descriptor) { - if (self.remoteUnifiedTargetConfig && !checkOneVersionForUnification(descriptor.version)) { + const versionRegex = /^(\d.\d.\d(?:-\w+)?)(?: #(\d+))?$/; + const versionParts = descriptor.version.match(versionRegex); + if (!versionParts) { return; } - var select_e = - $("".format( - descriptor.version, - descriptor.date - )) - .css("font-weight", FirmwareCache.has(descriptor) - ? "bold" - : "normal" - ); - select_e.data('summary', descriptor); - versions_element.append(select_e); + let version = versionParts[1]; + const buildNumber = versionParts[2] ? `${versionParts[2]}` : ''; + + const build = { descriptor }; + if (manufacturerId) { + if (!supportsUnifiedTargets(descriptor.version)) { + return; + } + + version = `${version}+${buildNumber}${manufacturerId}`; + build.manufacturerId = manufacturerId; + build.duplicateName = duplicateName; + } else { + version = `${version}+${buildNumber}-legacy`; + build.isLegacy = true; + } + builds[version] = build; }); - // Assume flashing latest, so default to it. + } + + if (callback) { + callback(); + } + } + + function populateVersions(versions_element, builds, target) { + const sortVersions = function (a, b) { + return -semver.compareBuild(a, b); + }; + + versions_element.empty(); + const targetVersions = Object.keys(builds); + if (targetVersions.length > 0) { + versions_element.append($("".format(i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersionFor'), target))); + targetVersions + .sort(sortVersions) + .forEach(function(versionName) { + const version = builds[versionName]; + if (!version.isLegacy && !supportsUnifiedTargets(version.descriptor.version)) { + return; + } + + let versionLabel; + if (version.isLegacy && Object.values(builds).some(function (build) { + return build.descriptor.version === version.descriptor.version && !build.isLegacy; + })) { + versionLabel = i18n.getMessage("firmwareFlasherLegacyLabel", { target: version.descriptor.version }); + } else if (!version.isLegacy && Object.values(builds).some(function (build) { + return build.descriptor.version === version.descriptor.version && build.manufacturerId !== version.manufacturerId && !build.isLegacy; + })) { + versionLabel = `${version.descriptor.version} (${version.manufacturerId})`; + } else { + versionLabel = version.descriptor.version; + } + + + var select_e = + $("".format( + versionName, + version.descriptor.date, + versionLabel + )) + .css("font-weight", FirmwareCache.has(version.descriptor) + ? "bold" + : "normal" + ); + select_e.data('summary', version.descriptor); + versions_element.append(select_e); + }); + // Assume flashing latest, so default to it. versions_element.prop("selectedIndex", 1).change(); } } @@ -471,7 +504,6 @@ TABS.firmware_flasher.initialize = function (callback) { function setUnifiedConfig(target, configText, bareBoard) { // a target might request a firmware with the same name, remove configuration in this case. if (bareBoard == target) { - console.log(bareBoard, '==', target); if (!self.isConfigLocal) { self.unifiedTargetConfig = undefined; self.unifiedTargetConfigName = undefined; @@ -486,6 +518,7 @@ TABS.firmware_flasher.initialize = function (callback) { self.remoteUnifiedTargetConfig = configText; } } + function clearBufferedFirmware() { self.isConfigLocal = false; self.unifiedTargetConfig = undefined; @@ -526,7 +559,7 @@ TABS.firmware_flasher.initialize = function (callback) { } var versions_e = $('select[name="firmware_version"]'); - if(target == 0) { + if (target == 0) { // target == 0 is the "Choose a Board" option. Throw out anything loaded clearBufferedFirmware(); @@ -536,65 +569,86 @@ TABS.firmware_flasher.initialize = function (callback) { // Show a loading message as there is a delay in loading a configuration versions_e.empty(); versions_e.append($("".format(i18n.getMessage('firmwareFlasherOptionLoading')))); + let selecteBuild = buildTypesToShow[$('select[name="build_type"]').val()]; + const builds = []; + + const finishPopulatingBuilds = function () { + if (TABS.firmware_flasher.releases[target]) { + TABS.firmware_flasher.bareBoard = target; + populateBuilds(builds, target, undefined, false, TABS.firmware_flasher.releases[target]); + } + + populateVersions(versions_e, builds, target); + }; + if (TABS.firmware_flasher.unifiedConfigs[target]) { var storageTag = 'unifiedConfigLast'; var expirationPeriod = 3600; // One of your earth hours. var checkTime = Math.floor(Date.now() / 1000); // Lets deal in seconds. chrome.storage.local.get(storageTag, function (result) { let storageObj = result[storageTag]; - let bareBoard = null; - // Check to see if the cached configuration is the one we want. - if (!storageObj || !storageObj.target || storageObj.target != target) { - // Have to go and try and get the unified config, and then do stuff - $.get(TABS.firmware_flasher.unifiedConfigs[target], function(data) { - console.log('got unified config'); - // cache it for later - let tempObj = {}; - tempObj['data'] = data; - tempObj['target'] = target; - tempObj['checkTime'] = checkTime; - let newStorageObj = {}; - newStorageObj[storageTag] = tempObj; - chrome.storage.local.set(newStorageObj); + const unifiedConfigBoard = TABS.firmware_flasher.unifiedConfigs[target]; + const duplicateName = Object.keys(unifiedConfigBoard).length > 1; + const manufacturerIds = Object.keys(unifiedConfigBoard); - bareBoard = grabBuildNameFromConfig(data); + const processManufacturer = function(index) { + const processNext = function () { + if (index < manufacturerIds.length - 1) { + processManufacturer(index + 1); + } else { + finishPopulatingBuilds(); + } + }; + + const manufacturerId = manufacturerIds[index]; + const targetId = `${target}+${manufacturerId}`; + // Check to see if the cached configuration is the one we want. + if (!storageObj || !storageObj.target || storageObj.target !== targetId) { + // Have to go and try and get the unified config, and then do stuff + $.get(unifiedConfigBoard[manufacturerId], function(response) { + console.log('got unified config'); + // cache it for later + let tempObj = {}; + tempObj['data'] = response; + tempObj['target'] = targetId; + tempObj['checkTime'] = checkTime; + let newStorageObj = {}; + newStorageObj[storageTag] = tempObj; + chrome.storage.local.set(newStorageObj); + + const bareBoard = grabBuildNameFromConfig(response); + TABS.firmware_flasher.bareBoard = bareBoard; + setUnifiedConfig(target, response, bareBoard); + populateBuilds(builds, target, manufacturerId, duplicateName, TABS.firmware_flasher.releases[bareBoard], processNext); + }).fail(xhr => { + //TODO error, populate nothing? + self.unifiedTargetConfig = undefined; + self.unifiedTargetConfigName = undefined; + self.isConfigLocal = false; + self.remoteUnifiedTargetConfig = undefined; + const baseFileName = unifiedConfigBoard[manufacturerId].reverse()[0]; + GUI.log(i18n.getMessage('firmwareFlasherFailedToLoadUnifiedConfig', + {remote_file: baseFileName})); + }); + } else { + console.log('We have the config cached for', targetId); + var data = storageObj.data; + + const bareBoard = grabBuildNameFromConfig(data); TABS.firmware_flasher.bareBoard = bareBoard; setUnifiedConfig(target, data, bareBoard); - populateVersions(versions_e, TABS.firmware_flasher.releases[bareBoard], target); - }).fail(xhr => { - //TODO error, populate nothing? - self.unifiedTargetConfig = undefined; - self.unifiedTargetConfigName = undefined; - self.isConfigLocal = false; - self.remoteUnifiedTargetConfig = undefined; - let baseFileName = TABS.firmware_flasher.unifiedConfigs[target].reverse()[0]; - GUI.log(i18n.getMessage('firmwareFlasherFailedToLoadUnifiedConfig', - {remote_file: baseFileName})); - }); - } else { - console.log('We have the config cached for', target); - var data = storageObj.data; + populateBuilds(builds, target, manufacturerId, duplicateName, TABS.firmware_flasher.releases[bareBoard], processNext); + } + }; - bareBoard = grabBuildNameFromConfig(data); - TABS.firmware_flasher.bareBoard = bareBoard; - setUnifiedConfig(target, data, bareBoard); - populateVersions(versions_e, TABS.firmware_flasher.releases[bareBoard], target); - } + processManufacturer(0); }); } else { - if (!self.isConfigLocal) { - self.unifiedTargetConfig = undefined; - self.unifiedTargetConfigName = undefined; - self.remoteUnifiedTargetConfig = undefined; - } else { - self.remoteUnifiedTargetConfig = undefined; - } - TABS.firmware_flasher.bareBoard = target; - populateVersions(versions_e, TABS.firmware_flasher.releases[target], target); + setUnifiedConfig(target, null, target); + finishPopulatingBuilds(); } } - } });