1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-19 06:15:13 +03:00

Merge branch 'master' into rc_smoothing

This commit is contained in:
Sean M 2018-08-11 20:59:00 -04:00
commit f4ea605685
45 changed files with 18196 additions and 463 deletions

View file

@ -60,13 +60,14 @@ gulp.task('clean-release', clean_release);
gulp.task('clean-cache', clean_cache); gulp.task('clean-cache', clean_cache);
var distBuild = gulp.series(clean_dist, dist_src, dist_locale, dist_libraries, dist_resources); var distBuild = gulp.series(dist_src, dist_locale, dist_libraries, dist_resources);
gulp.task('dist', distBuild); var distRebuild = gulp.series(clean_dist, distBuild);
gulp.task('dist', distRebuild);
var appsBuild = gulp.series(gulp.parallel(clean_apps, distBuild), apps, gulp.parallel(listPostBuildTasks(APPS_DIR))); var appsBuild = gulp.series(gulp.parallel(clean_apps, distRebuild), apps, gulp.parallel(listPostBuildTasks(APPS_DIR)));
gulp.task('apps', appsBuild); gulp.task('apps', appsBuild);
var debugBuild = gulp.series(gulp.parallel(clean_debug, distBuild), debug, gulp.parallel(listPostBuildTasks(DEBUG_DIR)), start_debug) var debugBuild = gulp.series(distBuild, debug, gulp.parallel(listPostBuildTasks(DEBUG_DIR)), start_debug)
gulp.task('debug', debugBuild); gulp.task('debug', debugBuild);
var releaseBuild = gulp.series(gulp.parallel(clean_release, appsBuild), gulp.parallel(listReleaseTasks())); var releaseBuild = gulp.series(gulp.parallel(clean_release, appsBuild), gulp.parallel(listReleaseTasks()));

58
libraries/analytics.js Normal file
View file

@ -0,0 +1,58 @@
(function(){var $c=function(a){this.w=a||[]};$c.prototype.set=function(a){this.w[a]=!0};$c.prototype.encode=function(){for(var a=[],b=0;b<this.w.length;b++)this.w[b]&&(a[Math.floor(b/6)]^=1<<b%6);for(b=0;b<a.length;b++)a[b]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".charAt(a[b]||0);return a.join("")+"~"};var vd=new $c;function J(a){vd.set(a)}var Td=function(a){a=Dd(a);a=new $c(a);for(var b=vd.w.slice(),c=0;c<a.w.length;c++)b[c]=b[c]||a.w[c];return(new $c(b)).encode()},Dd=function(a){a=a.get(Gd);ka(a)||(a=[]);return a};var ea=function(a){return"function"==typeof a},ka=function(a){return"[object Array]"==Object.prototype.toString.call(Object(a))},qa=function(a){return void 0!=a&&-1<(a.constructor+"").indexOf("String")},D=function(a,b){return 0==a.indexOf(b)},sa=function(a){return a?a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,""):""},ra=function(){for(var a=O.navigator.userAgent+(M.cookie?M.cookie:"")+(M.referrer?M.referrer:""),b=a.length,c=O.history.length;0<c;)a+=c--^b++;return[hd()^La(a)&2147483647,Math.round((new Date).getTime()/
1E3)].join(".")},ta=function(a){var b=M.createElement("img");b.width=1;b.height=1;b.src=a;return b},ua=function(){},K=function(a){if(encodeURIComponent instanceof Function)return encodeURIComponent(a);J(28);return a},L=function(a,b,c,d){try{a.addEventListener?a.addEventListener(b,c,!!d):a.attachEvent&&a.attachEvent("on"+b,c)}catch(e){J(27)}},f=/^[\w\-:/.?=&%!]+$/,wa=function(a,b,c){a&&(c?(c="",b&&f.test(b)&&(c=' id="'+b+'"'),f.test(a)&&M.write("<script"+c+' src="'+a+'">\x3c/script>')):(c=M.createElement("script"),
c.type="text/javascript",c.async=!0,c.src=a,b&&(c.id=b),a=M.getElementsByTagName("script")[0],a.parentNode.insertBefore(c,a)))},be=function(a,b){return E(M.location[b?"href":"search"],a)},E=function(a,b){return(a=a.match("(?:&|#|\\?)"+K(b).replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1")+"=([^&#]*)"))&&2==a.length?a[1]:""},xa=function(){var a=""+M.location.hostname;return 0==a.indexOf("www.")?a.substring(4):a},de=function(a,b){var c=a.indexOf(b);if(5==c||6==c)if(a=a.charAt(c+b.length),"/"==a||"?"==a||
""==a||":"==a)return!0;return!1},ya=function(a,b){var c=M.referrer;if(/^(https?|android-app):\/\//i.test(c)){if(a)return c;a="//"+M.location.hostname;if(!de(c,a))return b&&(b=a.replace(/\./g,"-")+".cdn.ampproject.org",de(c,b))?void 0:c}},za=function(a,b){if(1==b.length&&null!=b[0]&&"object"===typeof b[0])return b[0];for(var c={},d=Math.min(a.length+1,b.length),e=0;e<d;e++)if("object"===typeof b[e]){for(var g in b[e])b[e].hasOwnProperty(g)&&(c[g]=b[e][g]);break}else e<a.length&&(c[a[e]]=b[e]);return c};var ee=function(){this.keys=[];this.values={};this.m={}};ee.prototype.set=function(a,b,c){this.keys.push(a);c?this.m[":"+a]=b:this.values[":"+a]=b};ee.prototype.get=function(a){return this.m.hasOwnProperty(":"+a)?this.m[":"+a]:this.values[":"+a]};ee.prototype.map=function(a){for(var b=0;b<this.keys.length;b++){var c=this.keys[b],d=this.get(c);d&&a(c,d)}};var O=window,M=document,va=function(a,b){return setTimeout(a,b)};var F=window,Ea=document,G=function(a){var b=F._gaUserPrefs;if(b&&b.ioo&&b.ioo()||a&&!0===F["ga-disable-"+a])return!0;try{var c=F.external;if(c&&c._gaUserPrefs&&"oo"==c._gaUserPrefs)return!0}catch(g){}a=[];b=Ea.cookie.split(";");c=/^\s*AMP_TOKEN=\s*(.*?)\s*$/;for(var d=0;d<b.length;d++){var e=b[d].match(c);e&&a.push(e[1])}for(b=0;b<a.length;b++)if("$OPT_OUT"==decodeURIComponent(a[b]))return!0;return!1};var Ca=function(a){var b=[],c=M.cookie.split(";");a=new RegExp("^\\s*"+a+"=\\s*(.*?)\\s*$");for(var d=0;d<c.length;d++){var e=c[d].match(a);e&&b.push(e[1])}return b},zc=function(a,b,c,d,e,g){e=G(e)?!1:eb.test(M.location.hostname)||"/"==c&&vc.test(d)?!1:!0;if(!e)return!1;b&&1200<b.length&&(b=b.substring(0,1200));c=a+"="+b+"; path="+c+"; ";g&&(c+="expires="+(new Date((new Date).getTime()+g)).toGMTString()+"; ");d&&"none"!==d&&(c+="domain="+d+";");d=M.cookie;M.cookie=c;if(!(d=d!=M.cookie))a:{a=Ca(a);
for(d=0;d<a.length;d++)if(b==a[d]){d=!0;break a}d=!1}return d},Cc=function(a){return encodeURIComponent?encodeURIComponent(a).replace(/\(/g,"%28").replace(/\)/g,"%29"):a},vc=/^(www\.)?google(\.com?)?(\.[a-z]{2})?$/,eb=/(^|\.)doubleclick\.net$/i;var oc,Id=/^.*Version\/?(\d+)[^\d].*$/i,ne=function(){if(void 0!==O.__ga4__)return O.__ga4__;if(void 0===oc){var a=O.navigator.userAgent;if(a){var b=a;try{b=decodeURIComponent(a)}catch(c){}if(a=!(0<=b.indexOf("Chrome"))&&!(0<=b.indexOf("CriOS"))&&(0<=b.indexOf("Safari/")||0<=b.indexOf("Safari,")))b=Id.exec(b),a=11<=(b?Number(b[1]):-1);oc=a}else oc=!1}return oc};var Fa,Ga,fb,Ab,ja=/^https?:\/\/[^/]*cdn\.ampproject\.org\//,Ub=[],ic=function(){Z.D([ua])},tc=function(a,b){var c=Ca("AMP_TOKEN");if(1<c.length)return J(55),!1;c=decodeURIComponent(c[0]||"");if("$OPT_OUT"==c||"$ERROR"==c||G(b))return J(62),!1;if(!ja.test(M.referrer)&&"$NOT_FOUND"==c)return J(68),!1;if(void 0!==Ab)return J(56),va(function(){a(Ab)},0),!0;if(Fa)return Ub.push(a),!0;if("$RETRIEVING"==c)return J(57),va(function(){tc(a,b)},1E4),!0;Fa=!0;c&&"$"!=c[0]||(xc("$RETRIEVING",3E4),setTimeout(Mc,
3E4),c="");return Pc(c,b)?(Ub.push(a),!0):!1},Pc=function(a,b,c){if(!window.JSON)return J(58),!1;var d=O.XMLHttpRequest;if(!d)return J(59),!1;var e=new d;if(!("withCredentials"in e))return J(60),!1;e.open("POST",(c||"https://ampcid.google.com/v1/publisher:getClientId")+"?key=AIzaSyA65lEHUEizIsNtlbNo-l2K18dT680nsaM",!0);e.withCredentials=!0;e.setRequestHeader("Content-Type","text/plain");e.onload=function(){Fa=!1;if(4==e.readyState){try{200!=e.status&&(J(61),Qc("","$ERROR",3E4));var d=JSON.parse(e.responseText);
d.optOut?(J(63),Qc("","$OPT_OUT",31536E6)):d.clientId?Qc(d.clientId,d.securityToken,31536E6):!c&&d.alternateUrl?(Ga&&clearTimeout(Ga),Fa=!0,Pc(a,b,d.alternateUrl)):(J(64),Qc("","$NOT_FOUND",36E5))}catch(ca){J(65),Qc("","$ERROR",3E4)}e=null}};d={originScope:"AMP_ECID_GOOGLE"};a&&(d.securityToken=a);e.send(JSON.stringify(d));Ga=va(function(){J(66);Qc("","$ERROR",3E4)},1E4);return!0},Mc=function(){Fa=!1},xc=function(a,b){if(void 0===fb){fb="";for(var c=id(),d=0;d<c.length;d++){var e=c[d];if(zc("AMP_TOKEN",
encodeURIComponent(a),"/",e,"",b)){fb=e;return}}}zc("AMP_TOKEN",encodeURIComponent(a),"/",fb,"",b)},Qc=function(a,b,c){Ga&&clearTimeout(Ga);b&&xc(b,c);Ab=a;b=Ub;Ub=[];for(c=0;c<b.length;c++)b[c](a)};var oe=function(){return(Ba||"https:"==M.location.protocol?"https:":"http:")+"//www.google-analytics.com"},Da=function(a){this.name="len";this.message=a+"-8192"},ba=function(a,b,c){c=c||ua;if(2036>=b.length)wc(a,b,c);else if(8192>=b.length)x(a,b,c)||wd(a,b,c)||wc(a,b,c);else throw ge("len",b.length),new Da(b.length);},pe=function(a,b,c,d){d=d||ua;wd(a+"?"+b,"",d,c)},wc=function(a,b,c){var d=ta(a+"?"+b);d.onload=d.onerror=function(){d.onload=null;d.onerror=null;c()}},wd=function(a,b,c,d){var e=O.XMLHttpRequest;
if(!e)return!1;var g=new e;if(!("withCredentials"in g))return!1;a=a.replace(/^http:/,"https:");g.open("POST",a,!0);g.withCredentials=!0;g.setRequestHeader("Content-Type","text/plain");g.onreadystatechange=function(){if(4==g.readyState){if(d)try{var a=g.responseText;if(1>a.length)ge("xhr","ver","0"),c();else if("1"!=a.charAt(0))ge("xhr","ver",String(a.length)),c();else if(3<d.count++)ge("xhr","tmr",""+d.count),c();else if(1==a.length)c();else{var b=a.charAt(1);if("d"==b)pe("https://stats.g.doubleclick.net/j/collect",
d.U,d,c);else if("g"==b){var e="https://www.google.%/ads/ga-audiences".replace("%","com");wc(e,d.google,c);var w=a.substring(2);if(w)if(/^[a-z.]{1,6}$/.test(w)){var ha="https://www.google.%/ads/ga-audiences".replace("%",w);wc(ha,d.google,ua)}else ge("tld","bcc",w)}else ge("xhr","brc",b),c()}}catch(ue){ge("xhr","rsp"),c()}else c();g=null}};g.send(b);return!0},x=function(a,b,c){return O.navigator.sendBeacon?O.navigator.sendBeacon(a,b)?(c(),!0):!1:!1},ge=function(a,b,c){1<=100*Math.random()||G("?")||
(a=["t=error","_e="+a,"_v=j68","sr=1"],b&&a.push("_f="+b),c&&a.push("_m="+K(c.substring(0,100))),a.push("aip=1"),a.push("z="+hd()),wc("https://www.google-analytics.com/u/d",a.join("&"),ua))};var h=function(a){var b=O.gaData=O.gaData||{};return b[a]=b[a]||{}};var Ha=function(){this.M=[]};Ha.prototype.add=function(a){this.M.push(a)};Ha.prototype.D=function(a){try{for(var b=0;b<this.M.length;b++){var c=a.get(this.M[b]);c&&ea(c)&&c.call(O,a)}}catch(d){}b=a.get(Ia);b!=ua&&ea(b)&&(a.set(Ia,ua,!0),setTimeout(b,10))};function Ja(a){if(100!=a.get(Ka)&&La(P(a,Q))%1E4>=100*R(a,Ka))throw"abort";}function Ma(a){if(G(P(a,Na)))throw"abort";}function Oa(){var a=M.location.protocol;if("http:"!=a&&"https:"!=a)throw"abort";}
function Pa(a){try{O.navigator.sendBeacon?J(42):O.XMLHttpRequest&&"withCredentials"in new O.XMLHttpRequest&&J(40)}catch(c){}a.set(ld,Td(a),!0);a.set(Ac,R(a,Ac)+1);var b=[];Qa.map(function(c,d){d.F&&(c=a.get(c),void 0!=c&&c!=d.defaultValue&&("boolean"==typeof c&&(c*=1),b.push(d.F+"="+K(""+c))))});b.push("z="+Bd());a.set(Ra,b.join("&"),!0)}
function Sa(a){var b=P(a,gd)||oe()+"/collect",c=a.get(qe),d=P(a,fa);!d&&a.get(Vd)&&(d="beacon");if(c)pe(b,P(a,Ra),c,a.get(Ia));else if(d){c=d;d=P(a,Ra);var e=a.get(Ia);e=e||ua;"image"==c?wc(b,d,e):"xhr"==c&&wd(b,d,e)||"beacon"==c&&x(b,d,e)||ba(b,d,e)}else ba(b,P(a,Ra),a.get(Ia));b=a.get(Na);b=h(b);c=b.hitcount;b.hitcount=c?c+1:1;b=a.get(Na);delete h(b).pending_experiments;a.set(Ia,ua,!0)}
function Hc(a){(O.gaData=O.gaData||{}).expId&&a.set(Nc,(O.gaData=O.gaData||{}).expId);(O.gaData=O.gaData||{}).expVar&&a.set(Oc,(O.gaData=O.gaData||{}).expVar);var b=a.get(Na);if(b=h(b).pending_experiments){var c=[];for(d in b)b.hasOwnProperty(d)&&b[d]&&c.push(encodeURIComponent(d)+"."+encodeURIComponent(b[d]));var d=c.join("!")}else d=void 0;d&&a.set(m,d,!0)}function cd(){if(O.navigator&&"preview"==O.navigator.loadPurpose)throw"abort";}
function yd(a){var b=O.gaDevIds;ka(b)&&0!=b.length&&a.set("&did",b.join(","),!0)}function vb(a){if(!a.get(Na))throw"abort";};var hd=function(){return Math.round(2147483647*Math.random())},Bd=function(){try{var a=new Uint32Array(1);O.crypto.getRandomValues(a);return a[0]&2147483647}catch(b){return hd()}};function Ta(a){var b=R(a,Ua);500<=b&&J(15);var c=P(a,Va);if("transaction"!=c&&"item"!=c){c=R(a,Wa);var d=(new Date).getTime(),e=R(a,Xa);0==e&&a.set(Xa,d);e=Math.round(2*(d-e)/1E3);0<e&&(c=Math.min(c+e,20),a.set(Xa,d));if(0>=c)throw"abort";a.set(Wa,--c)}a.set(Ua,++b)};var Ya=function(){this.data=new ee},Qa=new ee,Za=[];Ya.prototype.get=function(a){var b=$a(a),c=this.data.get(a);b&&void 0==c&&(c=ea(b.defaultValue)?b.defaultValue():b.defaultValue);return b&&b.Z?b.Z(this,a,c):c};var P=function(a,b){a=a.get(b);return void 0==a?"":""+a},R=function(a,b){a=a.get(b);return void 0==a||""===a?0:1*a};Ya.prototype.set=function(a,b,c){if(a)if("object"==typeof a)for(var d in a)a.hasOwnProperty(d)&&ab(this,d,a[d],c);else ab(this,a,b,c)};
var ab=function(a,b,c,d){if(void 0!=c)switch(b){case Na:wb.test(c)}var e=$a(b);e&&e.o?e.o(a,b,c,d):a.data.set(b,c,d)},bb=function(a,b,c,d,e){this.name=a;this.F=b;this.Z=d;this.o=e;this.defaultValue=c},$a=function(a){var b=Qa.get(a);if(!b)for(var c=0;c<Za.length;c++){var d=Za[c],e=d[0].exec(a);if(e){b=d[1](e);Qa.set(b.name,b);break}}return b},yc=function(a){var b;Qa.map(function(c,d){d.F==a&&(b=d)});return b&&b.name},S=function(a,b,c,d,e){a=new bb(a,b,c,d,e);Qa.set(a.name,a);return a.name},cb=function(a,
b){Za.push([new RegExp("^"+a+"$"),b])},T=function(a,b,c){return S(a,b,c,void 0,db)},db=function(){};var gb=qa(window.GoogleAnalyticsObject)&&sa(window.GoogleAnalyticsObject)||"ga",jd=/^(?:utma\.)?\d+\.\d+$/,kd=/^amp-[\w.-]{22,64}$/,Ba=!1,hb=T("apiVersion","v"),ib=T("clientVersion","_v");S("anonymizeIp","aip");var jb=S("adSenseId","a"),Va=S("hitType","t"),Ia=S("hitCallback"),Ra=S("hitPayload");S("nonInteraction","ni");S("currencyCode","cu");S("dataSource","ds");var Vd=S("useBeacon",void 0,!1),fa=S("transport");S("sessionControl","sc","");S("sessionGroup","sg");S("queueTime","qt");var Ac=S("_s","_s");
S("screenName","cd");var kb=S("location","dl",""),lb=S("referrer","dr"),mb=S("page","dp","");S("hostname","dh");var nb=S("language","ul"),ob=S("encoding","de");S("title","dt",function(){return M.title||void 0});cb("contentGroup([0-9]+)",function(a){return new bb(a[0],"cg"+a[1])});var pb=S("screenColors","sd"),qb=S("screenResolution","sr"),rb=S("viewportSize","vp"),sb=S("javaEnabled","je"),tb=S("flashVersion","fl");S("campaignId","ci");S("campaignName","cn");S("campaignSource","cs");
S("campaignMedium","cm");S("campaignKeyword","ck");S("campaignContent","cc");var ub=S("eventCategory","ec"),xb=S("eventAction","ea"),yb=S("eventLabel","el"),zb=S("eventValue","ev"),Bb=S("socialNetwork","sn"),Cb=S("socialAction","sa"),Db=S("socialTarget","st"),Eb=S("l1","plt"),Fb=S("l2","pdt"),Gb=S("l3","dns"),Hb=S("l4","rrt"),Ib=S("l5","srt"),Jb=S("l6","tcp"),Kb=S("l7","dit"),Lb=S("l8","clt"),Mb=S("timingCategory","utc"),Nb=S("timingVar","utv"),Ob=S("timingLabel","utl"),Pb=S("timingValue","utt");
S("appName","an");S("appVersion","av","");S("appId","aid","");S("appInstallerId","aiid","");S("exDescription","exd");S("exFatal","exf");var Nc=S("expId","xid"),Oc=S("expVar","xvar"),m=S("exp","exp"),Rc=S("_utma","_utma"),Sc=S("_utmz","_utmz"),Tc=S("_utmht","_utmht"),Ua=S("_hc",void 0,0),Xa=S("_ti",void 0,0),Wa=S("_to",void 0,20);cb("dimension([0-9]+)",function(a){return new bb(a[0],"cd"+a[1])});cb("metric([0-9]+)",function(a){return new bb(a[0],"cm"+a[1])});S("linkerParam",void 0,void 0,Bc,db);
var ld=S("usage","_u"),Gd=S("_um");S("forceSSL",void 0,void 0,function(){return Ba},function(a,b,c){J(34);Ba=!!c});var ed=S("_j1","jid"),ia=S("_j2","gjid");cb("\\&(.*)",function(a){var b=new bb(a[0],a[1]),c=yc(a[0].substring(1));c&&(b.Z=function(a){return a.get(c)},b.o=function(a,b,g,ca){a.set(c,g,ca)},b.F=void 0);return b});
var Qb=T("_oot"),dd=S("previewTask"),Rb=S("checkProtocolTask"),md=S("validationTask"),Sb=S("checkStorageTask"),Uc=S("historyImportTask"),Tb=S("samplerTask"),Vb=S("_rlt"),Wb=S("buildHitTask"),Xb=S("sendHitTask"),Vc=S("ceTask"),zd=S("devIdTask"),Cd=S("timingTask"),Ld=S("displayFeaturesTask"),oa=S("customTask"),V=T("name"),Q=T("clientId","cid"),n=T("clientIdTime"),xd=T("storedClientId"),Ad=S("userId","uid"),Na=T("trackingId","tid"),U=T("cookieName",void 0,"_ga"),W=T("cookieDomain"),Yb=T("cookiePath",
void 0,"/"),Zb=T("cookieExpires",void 0,63072E3),Hd=T("cookieUpdate",void 0,!0),$b=T("legacyCookieDomain"),Wc=T("legacyHistoryImport",void 0,!0),ac=T("storage",void 0,"cookie"),bc=T("allowLinker",void 0,!1),cc=T("allowAnchor",void 0,!0),Ka=T("sampleRate","sf",100),dc=T("siteSpeedSampleRate",void 0,1),ec=T("alwaysSendReferrer",void 0,!1),I=T("_gid","_gid"),la=T("_gcn"),Kd=T("useAmpClientId"),ce=T("_gclid"),fe=T("_gt"),he=T("_ge",void 0,7776E6),ie=T("_gclsrc"),je=T("storeGac",void 0,!0),gd=S("transportUrl"),
Md=S("_r","_r"),qe=S("_dp"),Ud=S("allowAdFeatures",void 0,!0);function X(a,b,c,d){b[a]=function(){try{return d&&J(d),c.apply(this,arguments)}catch(e){throw ge("exc",a,e&&e.name),e;}}};var Od=function(){this.V=100;this.$=this.fa=!1;this.oa="detourexp";this.groups=1},Ed=function(a){var b=new Od,c;if(b.fa&&b.$)return 0;b.$=!0;if(a){if(b.oa&&void 0!==a.get(b.oa))return R(a,b.oa);if(0==a.get(dc))return 0}if(0==b.V)return 0;void 0===c&&(c=Bd());return 0==c%b.V?Math.floor(c/b.V)%b.groups+1:0};function fc(){var a,b;if((b=(b=O.navigator)?b.plugins:null)&&b.length)for(var c=0;c<b.length&&!a;c++){var d=b[c];-1<d.name.indexOf("Shockwave Flash")&&(a=d.description)}if(!a)try{var e=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");a=e.GetVariable("$version")}catch(g){}if(!a)try{e=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"),a="WIN 6,0,21,0",e.AllowScriptAccess="always",a=e.GetVariable("$version")}catch(g){}if(!a)try{e=new ActiveXObject("ShockwaveFlash.ShockwaveFlash"),a=e.GetVariable("$version")}catch(g){}a&&
(e=a.match(/[\d]+/g))&&3<=e.length&&(a=e[0]+"."+e[1]+" r"+e[2]);return a||void 0};var aa=function(a){var b=Math.min(R(a,dc),100);return La(P(a,Q))%100>=b?!1:!0},gc=function(a){var b={};if(Ec(b)||Fc(b)){var c=b[Eb];void 0==c||Infinity==c||isNaN(c)||(0<c?(Y(b,Gb),Y(b,Jb),Y(b,Ib),Y(b,Fb),Y(b,Hb),Y(b,Kb),Y(b,Lb),va(function(){a(b)},10)):L(O,"load",function(){gc(a)},!1))}},Ec=function(a){var b=O.performance||O.webkitPerformance;b=b&&b.timing;if(!b)return!1;var c=b.navigationStart;if(0==c)return!1;a[Eb]=b.loadEventStart-c;a[Gb]=b.domainLookupEnd-b.domainLookupStart;a[Jb]=b.connectEnd-
b.connectStart;a[Ib]=b.responseStart-b.requestStart;a[Fb]=b.responseEnd-b.responseStart;a[Hb]=b.fetchStart-c;a[Kb]=b.domInteractive-c;a[Lb]=b.domContentLoadedEventStart-c;return!0},Fc=function(a){if(O.top!=O)return!1;var b=O.external,c=b&&b.onloadT;b&&!b.isValidLoadTime&&(c=void 0);2147483648<c&&(c=void 0);0<c&&b.setPageReadyTime();if(void 0==c)return!1;a[Eb]=c;return!0},Y=function(a,b){var c=a[b];if(isNaN(c)||Infinity==c||0>c)a[b]=void 0},Fd=function(a){return function(b){if("pageview"==b.get(Va)&&
!a.I){a.I=!0;var c=aa(b),d=0<E(b.get(kb),"gclid").length;(c||d)&&gc(function(b){c&&a.send("timing",b);d&&a.send("adtiming",b)})}}};var hc=!1,mc=function(a){if("cookie"==P(a,ac)){if(a.get(Hd)||P(a,xd)!=P(a,Q)){var b=1E3*R(a,Zb);ma(a,Q,U,b)}ma(a,I,la,864E5);if(a.get(je)){var c=a.get(ce);if(c){var d=Math.min(R(a,he),1E3*R(a,Zb));d=Math.min(d,1E3*R(a,fe)+d-(new Date).getTime());a.data.set(he,d);b={};var e=a.get(fe),g=a.get(ie),ca=kc(P(a,Yb)),l=lc(P(a,W)),k=P(a,Na);g&&"aw.ds"!=g?b&&(b.ua=!0):(c=["1",e,Cc(c)].join("."),0<d&&(b&&(b.ta=!0),zc("_gac_"+Cc(k),c,ca,l,k,d)));le(b)}}else J(75);if(a="none"===lc(P(a,W)))a=M.location.hostname,
a=eb.test(a)||vc.test(a);a&&J(30)}},ma=function(a,b,c,d){var e=nd(a,b);if(e){c=P(a,c);var g=kc(P(a,Yb)),ca=lc(P(a,W)),l=P(a,Na);if("auto"!=ca)zc(c,e,g,ca,l,d)&&(hc=!0);else{J(32);for(var k=id(),w=0;w<k.length;w++)if(ca=k[w],a.data.set(W,ca),e=nd(a,b),zc(c,e,g,ca,l,d)){hc=!0;return}a.data.set(W,"auto")}}},nc=function(a){if("cookie"==P(a,ac)&&!hc&&(mc(a),!hc))throw"abort";},Yc=function(a){if(a.get(Wc)){var b=P(a,W),c=P(a,$b)||xa(),d=Xc("__utma",c,b);d&&(J(19),a.set(Tc,(new Date).getTime(),!0),a.set(Rc,
d.R),(b=Xc("__utmz",c,b))&&d.hash==b.hash&&a.set(Sc,b.R))}},nd=function(a,b){b=Cc(P(a,b));var c=lc(P(a,W)).split(".").length;a=jc(P(a,Yb));1<a&&(c+="-"+a);return b?["GA1",c,b].join("."):""},Xd=function(a,b){return na(b,P(a,W),P(a,Yb))},na=function(a,b,c){if(!a||1>a.length)J(12);else{for(var d=[],e=0;e<a.length;e++){var g=a[e];var ca=g.split(".");var l=ca.shift();("GA1"==l||"1"==l)&&1<ca.length?(g=ca.shift().split("-"),1==g.length&&(g[1]="1"),g[0]*=1,g[1]*=1,ca={H:g,s:ca.join(".")}):ca=kd.test(g)?
{H:[0,0],s:g}:void 0;ca&&d.push(ca)}if(1==d.length)return J(13),d[0].s;if(0==d.length)J(12);else{J(14);d=Gc(d,lc(b).split(".").length,0);if(1==d.length)return d[0].s;d=Gc(d,jc(c),1);1<d.length&&J(41);return d[0]&&d[0].s}}},Gc=function(a,b,c){for(var d=[],e=[],g,ca=0;ca<a.length;ca++){var l=a[ca];l.H[c]==b?d.push(l):void 0==g||l.H[c]<g?(e=[l],g=l.H[c]):l.H[c]==g&&e.push(l)}return 0<d.length?d:e},lc=function(a){return 0==a.indexOf(".")?a.substr(1):a},id=function(){var a=[],b=xa().split(".");if(4==b.length){var c=
b[b.length-1];if(parseInt(c,10)==c)return["none"]}for(c=b.length-2;0<=c;c--)a.push(b.slice(c).join("."));a.push("none");return a},kc=function(a){if(!a)return"/";1<a.length&&a.lastIndexOf("/")==a.length-1&&(a=a.substr(0,a.length-1));0!=a.indexOf("/")&&(a="/"+a);return a},jc=function(a){a=kc(a);return"/"==a?1:a.split("/").length},le=function(a){a.ta&&J(77);a.na&&J(74);a.pa&&J(73);a.ua&&J(69)};function Xc(a,b,c){"none"==b&&(b="");var d=[],e=Ca(a);a="__utma"==a?6:2;for(var g=0;g<e.length;g++){var ca=(""+e[g]).split(".");ca.length>=a&&d.push({hash:ca[0],R:e[g],O:ca})}if(0!=d.length)return 1==d.length?d[0]:Zc(b,d)||Zc(c,d)||Zc(null,d)||d[0]}function Zc(a,b){if(null==a)var c=a=1;else c=La(a),a=La(D(a,".")?a.substring(1):"."+a);for(var d=0;d<b.length;d++)if(b[d].hash==c||b[d].hash==a)return b[d]};var od=new RegExp(/^https?:\/\/([^\/:]+)/),pd=/(.*)([?&#])(?:_ga=[^&#]*)(?:&?)(.*)/,me=/(.*)([?&#])(?:_gac=[^&#]*)(?:&?)(.*)/;function Bc(a){var b=a.get(Q),c=a.get(I)||"";b="_ga=2."+K(pa(c+b,0)+"."+c+"-"+b);if((c=a.get(ce))&&a.get(je)){var d=R(a,fe);1E3*d+R(a,he)<=(new Date).getTime()?(J(76),a=""):(J(44),a="&_gac=1."+K([pa(c,0),d,c].join(".")))}else a="";return b+a}
function Ic(a,b){var c=new Date,d=O.navigator,e=d.plugins||[];a=[a,d.userAgent,c.getTimezoneOffset(),c.getYear(),c.getDate(),c.getHours(),c.getMinutes()+b];for(b=0;b<e.length;++b)a.push(e[b].description);return La(a.join("."))}function pa(a,b){var c=new Date,d=O.navigator,e=c.getHours()+Math.floor((c.getMinutes()+b)/60);return La([a,d.userAgent,d.language||"",c.getTimezoneOffset(),c.getYear(),c.getDate()+Math.floor(e/24),(24+e)%24,(60+c.getMinutes()+b)%60].join("."))}
var Dc=function(a){J(48);this.target=a;this.T=!1};Dc.prototype.ca=function(a,b){if(a.tagName){if("a"==a.tagName.toLowerCase()){a.href&&(a.href=qd(this,a.href,b));return}if("form"==a.tagName.toLowerCase())return rd(this,a)}if("string"==typeof a)return qd(this,a,b)};
var qd=function(a,b,c){var d=pd.exec(b);d&&3<=d.length&&(b=d[1]+(d[3]?d[2]+d[3]:""));(d=me.exec(b))&&3<=d.length&&(b=d[1]+(d[3]?d[2]+d[3]:""));a=a.target.get("linkerParam");var e=b.indexOf("?");d=b.indexOf("#");c?b+=(-1==d?"#":"&")+a:(c=-1==e?"?":"&",b=-1==d?b+(c+a):b.substring(0,d)+c+a+b.substring(d));b=b.replace(/&+_ga=/,"&_ga=");return b=b.replace(/&+_gac=/,"&_gac=")},rd=function(a,b){if(b&&b.action)if("get"==b.method.toLowerCase()){a=a.target.get("linkerParam").split("&");for(var c=0;c<a.length;c++){var d=
a[c].split("="),e=d[1];d=d[0];for(var g=b.childNodes||[],ca=!1,l=0;l<g.length;l++)if(g[l].name==d){g[l].setAttribute("value",e);ca=!0;break}ca||(g=M.createElement("input"),g.setAttribute("type","hidden"),g.setAttribute("name",d),g.setAttribute("value",e),b.appendChild(g))}}else"post"==b.method.toLowerCase()&&(b.action=qd(a,b.action))};
Dc.prototype.S=function(a,b,c){function d(c){try{c=c||O.event;a:{var d=c.target||c.srcElement;for(c=100;d&&0<c;){if(d.href&&d.nodeName.match(/^a(?:rea)?$/i)){var g=d;break a}d=d.parentNode;c--}g={}}("http:"==g.protocol||"https:"==g.protocol)&&sd(a,g.hostname||"")&&g.href&&(g.href=qd(e,g.href,b))}catch(k){J(26)}}var e=this;this.T||(this.T=!0,L(M,"mousedown",d,!1),L(M,"keyup",d,!1));c&&L(M,"submit",function(b){b=b||O.event;if((b=b.target||b.srcElement)&&b.action){var c=b.action.match(od);c&&sd(a,c[1])&&
rd(e,b)}})};function sd(a,b){if(b==M.location.hostname)return!1;for(var c=0;c<a.length;c++)if(a[c]instanceof RegExp){if(a[c].test(b))return!0}else if(0<=b.indexOf(a[c]))return!0;return!1}function ke(a,b){return b!=Ic(a,0)&&b!=Ic(a,-1)&&b!=Ic(a,-2)&&b!=pa(a,0)&&b!=pa(a,-1)&&b!=pa(a,-2)};var p=/^(GTM|OPT)-[A-Z0-9]+$/,q=/;_gaexp=[^;]*/g,r=/;((__utma=)|([^;=]+=GAX?\d+\.))[^;]*/g,Aa=/^https?:\/\/[\w\-.]+\.google.com(:\d+)?\/optimize\/opt-launch\.html\?.*$/,t=function(a){function b(a,b){b&&(c+="&"+a+"="+K(b))}var c="https://www.google-analytics.com/gtm/js?id="+K(a.id);"dataLayer"!=a.B&&b("l",a.B);b("t",a.target);b("cid",a.clientId);b("cidt",a.ka);b("gac",a.la);b("aip",a.ia);a.sync&&b("m","sync");b("cycle",a.G);a.qa&&b("gclid",a.qa);Aa.test(M.referrer)&&b("cb",String(hd()));return c};var Jd=function(a,b,c){this.aa=b;(b=c)||(b=(b=P(a,V))&&"t0"!=b?Wd.test(b)?"_gat_"+Cc(P(a,Na)):"_gat_"+Cc(b):"_gat");this.Y=b;this.ra=null},Rd=function(a,b){var c=b.get(Wb);b.set(Wb,function(b){Pd(a,b,ed);Pd(a,b,ia);var d=c(b);Qd(a,b);return d});var d=b.get(Xb);b.set(Xb,function(b){var c=d(b);if(se(b)){if(ne()!==H(a,b)){J(80);var e={U:re(a,b,1),google:re(a,b,2),count:0};pe("https://stats.g.doubleclick.net/j/collect",e.U,e)}else ta(re(a,b,0));b.set(ed,"",!0)}return c})},Pd=function(a,b,c){!1===b.get(Ud)||
b.get(c)||("1"==Ca(a.Y)[0]?b.set(c,"",!0):b.set(c,""+hd(),!0))},Qd=function(a,b){se(b)&&zc(a.Y,"1",b.get(Yb),b.get(W),b.get(Na),6E4)},se=function(a){return!!a.get(ed)&&a.get(Ud)},re=function(a,b,c){var d=new ee,e=function(a){$a(a).F&&d.set($a(a).F,b.get(a))};e(hb);e(ib);e(Na);e(Q);e(ed);if(0==c||1==c)e(Ad),e(ia),e(I);d.set($a(ld).F,Td(b));var g="";d.map(function(a,b){g+=K(a)+"=";g+=K(""+b)+"&"});g+="z="+hd();0==c?g=a.aa+g:1==c?g="t=dc&aip=1&_r=3&"+g:2==c&&(g="t=sr&aip=1&_r=4&slf_rd=1&"+g);return g},
H=function(a,b){null===a.ra&&(a.ra=1===Ed(b),a.ra&&J(33));return a.ra},Wd=/^gtm\d+$/;var fd=function(a,b){a=a.b;if(!a.get("dcLoaded")){var c=new $c(Dd(a));c.set(29);a.set(Gd,c.w);b=b||{};var d;b[U]&&(d=Cc(b[U]));b=new Jd(a,"https://stats.g.doubleclick.net/r/collect?t=dc&aip=1&_r=3&",d);Rd(b,a);a.set("dcLoaded",!0)}};var Sd=function(a){if(!a.get("dcLoaded")&&"cookie"==a.get(ac)){var b=new Jd(a);Pd(b,a,ed);Pd(b,a,ia);Qd(b,a);if(se(a)){var c=ne()!==H(b,a);a.set(Md,1,!0);c?(J(79),a.set(gd,oe()+"/j/collect",!0),a.set(qe,{U:re(b,a,1),google:re(b,a,2),count:0},!0)):a.set(gd,oe()+"/r/collect",!0)}}};var Lc=function(){var a=O.gaGlobal=O.gaGlobal||{};return a.hid=a.hid||hd()};var ad,bd=function(a,b,c){if(!ad){var d=M.location.hash;var e=O.name,g=/^#?gaso=([^&]*)/;if(e=(d=(d=d&&d.match(g)||e&&e.match(g))?d[1]:Ca("GASO")[0]||"")&&d.match(/^(?:!([-0-9a-z.]{1,40})!)?([-.\w]{10,1200})$/i))zc("GASO",""+d,c,b,a,0),window._udo||(window._udo=b),window._utcp||(window._utcp=c),a=e[1],wa("https://www.google.com/analytics/web/inpage/pub/inpage.js?"+(a?"prefix="+a+"&":"")+hd(),"_gasojs");ad=!0}};var wb=/^(UA|YT|MO|GP)-(\d+)-(\d+)$/,pc=function(a){function b(a,b){d.b.data.set(a,b)}function c(a,c){b(a,c);d.filters.add(a)}var d=this;this.b=new Ya;this.filters=new Ha;b(V,a[V]);b(Na,sa(a[Na]));b(U,a[U]);b(W,a[W]||xa());b(Yb,a[Yb]);b(Zb,a[Zb]);b(Hd,a[Hd]);b($b,a[$b]);b(Wc,a[Wc]);b(bc,a[bc]);b(cc,a[cc]);b(Ka,a[Ka]);b(dc,a[dc]);b(ec,a[ec]);b(ac,a[ac]);b(Ad,a[Ad]);b(n,a[n]);b(Kd,a[Kd]);b(je,a[je]);b(hb,1);b(ib,"j68");c(Qb,Ma);c(oa,ua);c(dd,cd);c(Rb,Oa);c(md,vb);c(Sb,nc);c(Uc,Yc);c(Tb,Ja);c(Vb,Ta);
c(Vc,Hc);c(zd,yd);c(Ld,Sd);c(Wb,Pa);c(Xb,Sa);c(Cd,Fd(this));Kc(this.b);Jc(this.b,a[Q]);this.b.set(jb,Lc());bd(this.b.get(Na),this.b.get(W),this.b.get(Yb))},Jc=function(a,b){var c=P(a,U);a.data.set(la,"_ga"==c?"_gid":c+"_gid");if("cookie"==P(a,ac)){hc=!1;c=Ca(P(a,U));c=Xd(a,c);if(!c){c=P(a,W);var d=P(a,$b)||xa();c=Xc("__utma",d,c);void 0!=c?(J(10),c=c.O[1]+"."+c.O[2]):c=void 0}c&&(hc=!0);if(d=c&&!a.get(Hd))if(d=c.split("."),2!=d.length)d=!1;else if(d=Number(d[1])){var e=R(a,Zb);d=d+e<(new Date).getTime()/
1E3}else d=!1;d&&(c=void 0);c&&(a.data.set(xd,c),a.data.set(Q,c),c=Ca(P(a,la)),(c=Xd(a,c))&&a.data.set(I,c));if(a.get(je)&&(c=a.get(ce),d=a.get(ie),!c||d&&"aw.ds"!=d)){c={};if(M){d=[];e=M.cookie.split(";");for(var g=/^\s*_gac_(UA-\d+-\d+)=\s*(.+?)\s*$/,ca=0;ca<e.length;ca++){var l=e[ca].match(g);l&&d.push({ja:l[1],value:l[2]})}e={};if(d&&d.length)for(g=0;g<d.length;g++)(ca=d[g].value.split("."),"1"!=ca[0]||3!=ca.length)?c&&(c.na=!0):ca[1]&&(e[d[g].ja]?c&&(c.pa=!0):e[d[g].ja]=[],e[d[g].ja].push({timestamp:ca[1],
qa:ca[2]}));d=e}else d={};d=d[P(a,Na)];le(c);d&&0!=d.length&&(c=d[0],a.data.set(fe,c.timestamp),a.data.set(ce,c.qa))}}if(a.get(Hd))a:if(d=be("_ga",a.get(cc)))if(a.get(bc))if(c=d.indexOf("."),-1==c)J(22);else{e=d.substring(0,c);g=d.substring(c+1);c=g.indexOf(".");d=g.substring(0,c);g=g.substring(c+1);if("1"==e){if(c=g,ke(c,d)){J(23);break a}}else if("2"==e){c=g.indexOf("-");e="";0<c?(e=g.substring(0,c),c=g.substring(c+1)):c=g.substring(1);if(ke(e+c,d)){J(53);break a}e&&(J(2),a.data.set(I,e))}else{J(22);
break a}J(11);a.data.set(Q,c);if(c=be("_gac",a.get(cc)))c=c.split("."),"1"!=c[0]||4!=c.length?J(72):ke(c[3],c[1])?J(71):(a.data.set(ce,c[3]),a.data.set(fe,c[2]),J(70))}else J(21);b&&(J(9),a.data.set(Q,K(b)));a.get(Q)||((b=(b=O.gaGlobal&&O.gaGlobal.vid)&&-1!=b.search(jd)?b:void 0)?(J(17),a.data.set(Q,b)):(J(8),a.data.set(Q,ra())));a.get(I)||(J(3),a.data.set(I,ra()));mc(a)},Kc=function(a){var b=O.navigator,c=O.screen,d=M.location;a.set(lb,ya(a.get(ec),a.get(Kd)));if(d){var e=d.pathname||"";"/"!=e.charAt(0)&&
(J(31),e="/"+e);a.set(kb,d.protocol+"//"+d.hostname+e+d.search)}c&&a.set(qb,c.width+"x"+c.height);c&&a.set(pb,c.colorDepth+"-bit");c=M.documentElement;var g=(e=M.body)&&e.clientWidth&&e.clientHeight,ca=[];c&&c.clientWidth&&c.clientHeight&&("CSS1Compat"===M.compatMode||!g)?ca=[c.clientWidth,c.clientHeight]:g&&(ca=[e.clientWidth,e.clientHeight]);c=0>=ca[0]||0>=ca[1]?"":ca.join("x");a.set(rb,c);a.set(tb,fc());a.set(ob,M.characterSet||M.charset);a.set(sb,b&&"function"===typeof b.javaEnabled&&b.javaEnabled()||
!1);a.set(nb,(b&&(b.language||b.browserLanguage)||"").toLowerCase());a.data.set(ce,be("gclid",!0));a.data.set(ie,be("gclsrc",!0));a.data.set(fe,Math.round((new Date).getTime()/1E3));if(d&&a.get(cc)&&(b=M.location.hash)){b=b.split(/[?&#]+/);d=[];for(c=0;c<b.length;++c)(D(b[c],"utm_id")||D(b[c],"utm_campaign")||D(b[c],"utm_source")||D(b[c],"utm_medium")||D(b[c],"utm_term")||D(b[c],"utm_content")||D(b[c],"gclid")||D(b[c],"dclid")||D(b[c],"gclsrc"))&&d.push(b[c]);0<d.length&&(b="#"+d.join("&"),a.set(kb,
a.get(kb)+b))}};pc.prototype.get=function(a){return this.b.get(a)};pc.prototype.set=function(a,b){this.b.set(a,b)};var qc={pageview:[mb],event:[ub,xb,yb,zb],social:[Bb,Cb,Db],timing:[Mb,Nb,Pb,Ob]};pc.prototype.send=function(a){if(!(1>arguments.length)){if("string"===typeof arguments[0]){var b=arguments[0];var c=[].slice.call(arguments,1)}else b=arguments[0]&&arguments[0][Va],c=arguments;b&&(c=za(qc[b]||[],c),c[Va]=b,this.b.set(c,void 0,!0),this.filters.D(this.b),this.b.data.m={})}};
pc.prototype.ma=function(a,b){var c=this;u(a,c,b)||(v(a,function(){u(a,c,b)}),y(String(c.get(V)),a,void 0,b,!0))};var rc=function(a){if("prerender"==M.visibilityState)return!1;a();return!0},z=function(a){if(!rc(a)){J(16);var b=!1,c=function(){if(!b&&rc(a)){b=!0;var d=c,e=M;e.removeEventListener?e.removeEventListener("visibilitychange",d,!1):e.detachEvent&&e.detachEvent("onvisibilitychange",d)}};L(M,"visibilitychange",c)}};var td=/^(?:(\w+)\.)?(?:(\w+):)?(\w+)$/,sc=function(a){if(ea(a[0]))this.u=a[0];else{var b=td.exec(a[0]);null!=b&&4==b.length&&(this.c=b[1]||"t0",this.K=b[2]||"",this.C=b[3],this.a=[].slice.call(a,1),this.K||(this.A="create"==this.C,this.i="require"==this.C,this.g="provide"==this.C,this.ba="remove"==this.C),this.i&&(3<=this.a.length?(this.X=this.a[1],this.W=this.a[2]):this.a[1]&&(qa(this.a[1])?this.X=this.a[1]:this.W=this.a[1])));b=a[1];a=a[2];if(!this.C)throw"abort";if(this.i&&(!qa(b)||""==b))throw"abort";
if(this.g&&(!qa(b)||""==b||!ea(a)))throw"abort";if(ud(this.c)||ud(this.K))throw"abort";if(this.g&&"t0"!=this.c)throw"abort";}};function ud(a){return 0<=a.indexOf(".")||0<=a.indexOf(":")};var Yd,Zd,$d,A;Yd=new ee;$d=new ee;A=new ee;Zd={ec:45,ecommerce:46,linkid:47};
var u=function(a,b,c){b==N||b.get(V);var d=Yd.get(a);if(!ea(d))return!1;b.plugins_=b.plugins_||new ee;if(b.plugins_.get(a))return!0;b.plugins_.set(a,new d(b,c||{}));return!0},y=function(a,b,c,d,e){if(!ea(Yd.get(b))&&!$d.get(b)){Zd.hasOwnProperty(b)&&J(Zd[b]);if(p.test(b)){J(52);a=N.j(a);if(!a)return!0;c=d||{};d={id:b,B:c.dataLayer||"dataLayer",ia:!!a.get("anonymizeIp"),sync:e,G:!1};a.get("&gtm")==b&&(d.G=!0);var g=String(a.get("name"));"t0"!=g&&(d.target=g);G(String(a.get("trackingId")))||(d.clientId=
String(a.get(Q)),d.ka=Number(a.get(n)),c=c.palindrome?r:q,c=(c=M.cookie.replace(/^|(; +)/g,";").match(c))?c.sort().join("").substring(1):void 0,d.la=c,d.qa=E(a.b.get(kb)||"","gclid"));a=d.B;c=(new Date).getTime();O[a]=O[a]||[];c={"gtm.start":c};e||(c.event="gtm.js");O[a].push(c);c=t(d)}!c&&Zd.hasOwnProperty(b)?(J(39),c=b+".js"):J(43);c&&(c&&0<=c.indexOf("/")||(c=(Ba||"https:"==M.location.protocol?"https:":"http:")+"//www.google-analytics.com/plugins/ua/"+c),d=ae(c),a=d.protocol,c=M.location.protocol,
("https:"==a||a==c||("http:"!=a?0:"http:"==c))&&B(d)&&(wa(d.url,void 0,e),$d.set(b,!0)))}},v=function(a,b){var c=A.get(a)||[];c.push(b);A.set(a,c)},C=function(a,b){Yd.set(a,b);b=A.get(a)||[];for(var c=0;c<b.length;c++)b[c]();A.set(a,[])},B=function(a){var b=ae(M.location.href);if(D(a.url,"https://www.google-analytics.com/gtm/js?id="))return!0;if(a.query||0<=a.url.indexOf("?")||0<=a.path.indexOf("://"))return!1;if(a.host==b.host&&a.port==b.port)return!0;b="http:"==a.protocol?80:443;return"www.google-analytics.com"==
a.host&&(a.port||b)==b&&D(a.path,"/plugins/")?!0:!1},ae=function(a){function b(a){var b=(a.hostname||"").split(":")[0].toLowerCase(),c=(a.protocol||"").toLowerCase();c=1*a.port||("http:"==c?80:"https:"==c?443:"");a=a.pathname||"";D(a,"/")||(a="/"+a);return[b,""+c,a]}var c=M.createElement("a");c.href=M.location.href;var d=(c.protocol||"").toLowerCase(),e=b(c),g=c.search||"",ca=d+"//"+e[0]+(e[1]?":"+e[1]:"");D(a,"//")?a=d+a:D(a,"/")?a=ca+a:!a||D(a,"?")?a=ca+e[2]+(a||g):0>a.split("/")[0].indexOf(":")&&
(a=ca+e[2].substring(0,e[2].lastIndexOf("/"))+"/"+a);c.href=a;d=b(c);return{protocol:(c.protocol||"").toLowerCase(),host:d[0],port:d[1],path:d[2],query:c.search||"",url:a||""}};var Z={ga:function(){Z.f=[]}};Z.ga();Z.D=function(a){var b=Z.J.apply(Z,arguments);b=Z.f.concat(b);for(Z.f=[];0<b.length&&!Z.v(b[0])&&!(b.shift(),0<Z.f.length););Z.f=Z.f.concat(b)};Z.J=function(a){for(var b=[],c=0;c<arguments.length;c++)try{var d=new sc(arguments[c]);d.g?C(d.a[0],d.a[1]):(d.i&&(d.ha=y(d.c,d.a[0],d.X,d.W)),b.push(d))}catch(e){}return b};
Z.v=function(a){try{if(a.u)a.u.call(O,N.j("t0"));else{var b=a.c==gb?N:N.j(a.c);if(a.A){if("t0"==a.c&&(b=N.create.apply(N,a.a),null===b))return!0}else if(a.ba)N.remove(a.c);else if(b)if(a.i){if(a.ha&&(a.ha=y(a.c,a.a[0],a.X,a.W)),!u(a.a[0],b,a.W))return!0}else if(a.K){var c=a.C,d=a.a,e=b.plugins_.get(a.K);e[c].apply(e,d)}else b[a.C].apply(b,a.a)}}catch(g){}};var N=function(a){J(1);Z.D.apply(Z,[arguments])};N.h={};N.P=[];N.L=0;N.answer=42;var uc=[Na,W,V];
N.create=function(a){var b=za(uc,[].slice.call(arguments));b[V]||(b[V]="t0");var c=""+b[V];if(N.h[c])return N.h[c];a:{if(b[Kd]){J(67);if(b[ac]&&"cookie"!=b[ac]){var d=!1;break a}if(void 0!==Ab)b[Q]||(b[Q]=Ab);else{b:{d=String(b[W]||xa());var e=String(b[Yb]||"/"),g=Ca(String(b[U]||"_ga"));d=na(g,d,e);if(!d||jd.test(d))d=!0;else if(d=Ca("AMP_TOKEN"),0==d.length)d=!0;else{if(1==d.length&&(d=decodeURIComponent(d[0]),"$RETRIEVING"==d||"$OPT_OUT"==d||"$ERROR"==d||"$NOT_FOUND"==d)){d=!0;break b}d=!1}}if(d&&
tc(ic,String(b[Na]))){d=!0;break a}}}d=!1}if(d)return null;b=new pc(b);N.h[c]=b;N.P.push(b);return b};N.remove=function(a){for(var b=0;b<N.P.length;b++)if(N.P[b].get(V)==a){N.P.splice(b,1);N.h[a]=null;break}};N.j=function(a){return N.h[a]};N.getAll=function(){return N.P.slice(0)};
N.N=function(){"ga"!=gb&&J(49);var a=O[gb];if(!a||42!=a.answer){N.L=a&&a.l;N.loaded=!0;var b=O[gb]=N;X("create",b,b.create);X("remove",b,b.remove);X("getByName",b,b.j,5);X("getAll",b,b.getAll,6);b=pc.prototype;X("get",b,b.get,7);X("set",b,b.set,4);X("send",b,b.send);X("requireSync",b,b.ma);b=Ya.prototype;X("get",b,b.get);X("set",b,b.set);if("https:"!=M.location.protocol&&!Ba){a:{b=M.getElementsByTagName("script");for(var c=0;c<b.length&&100>c;c++){var d=b[c].src;if(d&&0==d.indexOf("https://www.google-analytics.com/analytics")){b=
!0;break a}}b=!1}b&&(Ba=!0)}(O.gaplugins=O.gaplugins||{}).Linker=Dc;b=Dc.prototype;C("linker",Dc);X("decorate",b,b.ca,20);X("autoLink",b,b.S,25);C("displayfeatures",fd);C("adfeatures",fd);a=a&&a.q;ka(a)?Z.D.apply(N,a):J(50)}};N.da=function(){for(var a=N.getAll(),b=0;b<a.length;b++)a[b].get(V)};var da=N.N,Nd=O[gb];Nd&&Nd.r?da():z(da);z(function(){Z.D(["provide","render",ua])});function La(a){var b=1,c;if(a)for(b=0,c=a.length-1;0<=c;c--){var d=a.charCodeAt(c);b=(b<<6&268435455)+d+(d<<14);d=b&266338304;b=0!=d?b^d>>21:b}return b};})(window);

View file

@ -9,12 +9,21 @@
"error": { "error": {
"message": "Error: {{errorMessage}}" "message": "Error: {{errorMessage}}"
}, },
"errorTitle": {
"message": "Error"
},
"warningTitle": { "warningTitle": {
"message": "Warning" "message": "Warning"
}, },
"noticeTitle": { "noticeTitle": {
"message": "Notice" "message": "Notice"
}, },
"operationNotSupported": {
"message": "This operation is not supported by your hardware."
},
"storageDeviceNotReady": {
"message": "The storage device is not ready. In the case of a microSD card, make sure it is properly recognised by your flight controller."
},
"options_title": { "options_title": {
"message": "Application Options" "message": "Application Options"
}, },
@ -51,6 +60,12 @@
"permanentExpertMode": { "permanentExpertMode": {
"message": "Permanently enable Expert Mode" "message": "Permanently enable Expert Mode"
}, },
"rememberLastTab": {
"message": "Reopen last tab on connect"
},
"analyticsOptOut": {
"message": "Opt out of the anonymised collection of statistics data"
},
"userLanguageSelect": { "userLanguageSelect": {
"message": "Language (need to restart the application for the changes to take effect)" "message": "Language (need to restart the application for the changes to take effect)"
}, },
@ -377,6 +392,15 @@
"message" : "Running - OS: <strong>{{operatingSystem}}</strong>, Chrome: <strong>{{chromeVersion}}</strong>, Configurator: <strong>{{configuratorVersion}}</strong>", "message" : "Running - OS: <strong>{{operatingSystem}}</strong>, Chrome: <strong>{{chromeVersion}}</strong>, Configurator: <strong>{{configuratorVersion}}</strong>",
"description": "Message that appears in the GUI log panel indicating operating system, Chrome version and Configurator version" "description": "Message that appears in the GUI log panel indicating operating system, Chrome version and Configurator version"
}, },
"buildServerLoaded": {
"message" : "Loaded builds information for $1 from build server."
},
"buildServerLoadFailed": {
"message" : "<b>Build server query for $1 releases failed, using cached information. Reason: <code>$2</code></b>"
},
"buildServerUsingCached": {
"message" : "Using cached builds information for $1."
},
"releaseCheckLoaded": { "releaseCheckLoaded": {
"message" : "Loaded release information for $1 from GitHub." "message" : "Loaded release information for $1 from GitHub."
}, },
@ -513,6 +537,9 @@
"defaultChangelogHead": { "defaultChangelogHead": {
"message": "Configurator - Changelog" "message": "Configurator - Changelog"
}, },
"defaultPrivacyPolicyAction": {
"message": "Privacy Policy"
},
"defaultButtonFirmwareFlasher": { "defaultButtonFirmwareFlasher": {
"message": "Firmware Flasher" "message": "Firmware Flasher"
}, },
@ -520,7 +547,7 @@
"message": "Open Source / Donation Notice" "message": "Open Source / Donation Notice"
}, },
"defaultDonateText": { "defaultDonateText": {
"message": "This utility is fully <strong>open source</strong> and is available free of charge to all <strong>Betaflight</strong> users.<br />If you found the Betaflight or Betaflight configurator useful, please consider <strong>supporting</strong> its development by donating." "message": "<strong>Betaflight</strong> is a flight controller software that is <strong>open source</strong> and is available free of charge <strong>without warranty</strong> to all users.<br />If you found the Betaflight or Betaflight configurator useful, please consider <strong>supporting</strong> its development by donating."
}, },
"defaultDonate": { "defaultDonate": {
"message": "Donate" "message": "Donate"
@ -598,8 +625,14 @@
"initialSetupButtonRestore": { "initialSetupButtonRestore": {
"message": "Restore" "message": "Restore"
}, },
"initialSetupButtonRebootBootloader": {
"message": "Activate Boot Loader / DFU"
},
"initialSetupBackupRestoreText": { "initialSetupBackupRestoreText": {
"message": "<strong>Backup</strong> your configuration in case of an accident, <strong>CLI</strong> settings are <span class=\"message-negative\">not</span> included - See 'dump' cli command" "message": "<strong>Backup</strong> your configuration in case of an accident, <strong>CLI</strong> settings are <span class=\"message-negative\">not</span> included - use the command 'diff all' in CLI for this."
},
"initialSetupRebootBootloaderText": {
"message": "Reboot into <strong>boot loader / DFU</strong> mode."
}, },
"initialSetupBackupSuccess": { "initialSetupBackupSuccess": {
"message": "Backup saved <span class=\"message-positive\">successfully</span>" "message": "Backup saved <span class=\"message-positive\">successfully</span>"
@ -1227,6 +1260,17 @@
"message": "Profile independent PID Controller Settings" "message": "Profile independent PID Controller Settings"
}, },
"pidTuningAntiGravityMode": {
"message": "Anti Gravity Mode"
},
"pidTuningAntiGravityModeOptionSmooth": {
"message": "Smooth",
"description": "One of the modes of anti gravity"
},
"pidTuningAntiGravityModeOptionStep": {
"message": "Step",
"description": "One of the modes of anti gravity"
},
"pidTuningAntiGravityGain": { "pidTuningAntiGravityGain": {
"message": "Anti Gravity Gain" "message": "Anti Gravity Gain"
}, },
@ -1348,6 +1392,12 @@
"receiverRcInterpolationInterval": { "receiverRcInterpolationInterval": {
"message": "RC Interpolation Interval [ms]" "message": "RC Interpolation Interval [ms]"
}, },
"pidTuningFeedforwardTransition": {
"message": "Feedforward transition"
},
"pidTuningFeedforwardTransitionHelp": {
"message": "With this parameter, the Feedforward term can be reduced near the center of the sticks, which results in smoother end of flips and rolls.<br> The value represents a point of stick deflection: 0 - stick centered, 1 - full deflection. When the stick is above that point, Feedforward is kept constant at its configured value. When the stick is positioned below that point, Feedforward is reduced proportionally, reaching 0 at the stick center position.<br> Value of 1 gives maximum smoothing effect, while value of 0 keeps the Feedforward fixed at its configured value over the whole stick range."
},
"pidTuningDtermSetpointTransition": { "pidTuningDtermSetpointTransition": {
"message": "D Setpoint transition" "message": "D Setpoint transition"
}, },
@ -1372,6 +1422,9 @@
"pidTuningDerivative": { "pidTuningDerivative": {
"message": "Derivative" "message": "Derivative"
}, },
"pidTuningFeedforward": {
"message": "Feedforward"
},
"pidTuningRcRate": { "pidTuningRcRate": {
"message": "RC Rate" "message": "RC Rate"
}, },
@ -1564,6 +1617,9 @@
"auxiliaryHelp": { "auxiliaryHelp": {
"message": "Use ranges to define the switches on your transmitter and corresponding mode assignments. A receiver channel that gives a reading between a range min/max will activate the mode. Remember to save your settings using the Save button." "message": "Use ranges to define the switches on your transmitter and corresponding mode assignments. A receiver channel that gives a reading between a range min/max will activate the mode. Remember to save your settings using the Save button."
}, },
"auxiliaryToggleUnused": {
"message": "Show/hide unused modes"
},
"auxiliaryMin": { "auxiliaryMin": {
"message": "Min" "message": "Min"
}, },
@ -1699,15 +1755,30 @@
"adjustmentsFunction22": { "adjustmentsFunction22": {
"message": "D Setpoint" "message": "D Setpoint"
}, },
"adjustmentsFunction22_2": {
"message": "Pitch & Roll F Adjustment"
},
"adjustmentsFunction23": { "adjustmentsFunction23": {
"message": "D Setpoint Transition" "message": "D Setpoint Transition"
}, },
"adjustmentsFunction23_2": {
"message": "Feedforward Transition"
},
"adjustmentsFunction24": { "adjustmentsFunction24": {
"message": "Horizon Strength Adjustment" "message": "Horizon Strength Adjustment"
}, },
"adjustmentsFunction25": { "adjustmentsFunction25": {
"message": "PID-Audio Selection" "message": "PID-Audio Selection"
}, },
"adjustmentsFunction26": {
"message": "Pitch F Adjustment"
},
"adjustmentsFunction27": {
"message": "Roll F Adjustment"
},
"adjustmentsFunction28": {
"message": "Yaw F Adjustment"
},
"adjustmentsSave": { "adjustmentsSave": {
"message": "Save" "message": "Save"
}, },
@ -2273,7 +2344,7 @@
"message": "Baud Rate" "message": "Baud Rate"
}, },
"firmwareFlasherShowDevelopmentReleases":{ "firmwareFlasherShowDevelopmentReleases":{
"message": "Show unstable releases" "message": "Show unstable and additional releases"
}, },
"firmwareFlasherShowDevelopmentReleasesDescription":{ "firmwareFlasherShowDevelopmentReleasesDescription":{
"message": "Show Release-Candidates and Development Releases." "message": "Show Release-Candidates and Development Releases."
@ -2813,6 +2884,67 @@
"pidTuningVbatPidCompensationHelp": { "pidTuningVbatPidCompensationHelp": {
"message": "Increases the PID values to compensate when Vbat gets lower. This will give more constant flight characteristics throughout the flight. The amount of compensation that is applied is calculated from the $t(powerBatteryMaximum.message) set in the $t(tabPower.message) page, so make sure that is set to something appropriate." "message": "Increases the PID values to compensate when Vbat gets lower. This will give more constant flight characteristics throughout the flight. The amount of compensation that is applied is calculated from the $t(powerBatteryMaximum.message) set in the $t(tabPower.message) page, so make sure that is set to something appropriate."
}, },
"pidTuningItermRotation": {
"message": "I Term Rotation"
},
"pidTuningItermRotationHelp": {
"message": "Rotates the current I Term vector properly to other axes as the quad rotates when yawing continuously during rolls and when performing funnels and other tricks. Very appreciated by LOS acro pilots."
},
"pidTuningSmartFeedforward": {
"message": "Smart Feedforward"
},
"pidTuningSmartFeedforwardHelp": {
"message": "Reduces the effect of the F Term in the PID. When both the P Term and the F Term are active at the same moment, it only uses the larger of the two, avoiding overshoots without the needing to raise D, but also reduces the responsiveness effect produced by the F term when added to P."
},
"pidTuningItermRelax": {
"message": "I Term Relax"
},
"pidTuningItermRelaxHelp": {
"message": "Limits the accumulation of I Term when fast movements happen. This helps specially to reduce the bounceback at the end of rolls and other fast movements. You can choose the axes in which this is active, and if the fast movement is detectd using the Gyro or the Setpoint (stick)."
},
"pidTuningItermRelaxAxes": {
"message": "Axes:"
},
"pidTuningItermRelaxAxesOptionRP": {
"message": "RP"
},
"pidTuningItermRelaxAxesOptionRPY": {
"message": "RPY"
},
"pidTuningItermRelaxAxesOptionRPInc": {
"message": "RP (increment only)"
},
"pidTuningItermRelaxAxesOptionRPYInc": {
"message": "RPY (increment only)"
},
"pidTuningItermRelaxType": {
"message": "Type:"
},
"pidTuningItermRelaxTypeOptionGyro": {
"message": "Gyro"
},
"pidTuningItermRelaxTypeOptionSetpoint": {
"message": "Setpoint"
},
"pidTuningAbsoluteControlGain": {
"message": "Absolute Control"
},
"pidTuningAbsoluteControlGainHelp": {
"message": "This feature solves some underlying problems of I Term Rotation and should hopefully replace it at some point. The feature accumulates the absolute gyro error in quad coordinates and mixes a proportional correction into the setpoint. It needs to have the iterm_relax enable for RPY."
},
"pidTuningThrottleBoost": {
"message": "Throttle Boost"
},
"pidTuningThrottleBoostHelp": {
"message": "This feature allows throttle to be temporarily boosted to the up to down-side to increase acceleration torque to the motors, providing a much faster throttle response."
},
"pidTuningAcroTrainerAngleLimit": {
"message": "Acro Trainer Angle Limit"
},
"pidTuningAcroTrainerAngleLimitHelp": {
"message": "Adds a new angle limiting mode for pilots who are learning to fly in acro mode. The range valid is 10-80 and must be activated with a switch in the $t(tabAuxiliary.message) tab."
},
"configHelp2": { "configHelp2": {
"message": "Arbitrary board rotation in degrees, to allow mounting it sideways / upside down / rotated etc. When running external sensors, use the sensor alignments (Gyro, Acc, Mag) to define sensor position independent from board orientation. " "message": "Arbitrary board rotation in degrees, to allow mounting it sideways / upside down / rotated etc. When running external sensors, use the sensor alignments (Gyro, Acc, Mag) to define sensor position independent from board orientation. "
}, },
@ -3182,6 +3314,12 @@
"osdSetupFontPresets": { "osdSetupFontPresets": {
"message": "Font presets:" "message": "Font presets:"
}, },
"osdSetupFontPresetsSelector": {
"message": "Select Font Presets:"
},
"osdSetupFontPresetsSelectorOr": {
"message": "or"
},
"osdSetupOpenFont": { "osdSetupOpenFont": {
"message": "Open Font File" "message": "Open Font File"
}, },
@ -3630,6 +3768,9 @@
"pidTuningPidTuningTip": { "pidTuningPidTuningTip": {
"message": "<b>Proportional:</b> You will notice a very strong resistant force to any attempts to move the MultiRotor<br><b>Integral:</b> Increase the ability to hold overall initial position and reduce drift, but also increase the delay in returning to initial position.<br><b>Derivative:</b> Improves the speed at which deviations are recovered, but increases noise<br><b>Rates and Expo</b>: Determine your stick feel based on these parameters. Use the graph and live 3D model to find your favourite rate setting" "message": "<b>Proportional:</b> You will notice a very strong resistant force to any attempts to move the MultiRotor<br><b>Integral:</b> Increase the ability to hold overall initial position and reduce drift, but also increase the delay in returning to initial position.<br><b>Derivative:</b> Improves the speed at which deviations are recovered, but increases noise<br><b>Rates and Expo</b>: Determine your stick feel based on these parameters. Use the graph and live 3D model to find your favourite rate setting"
}, },
"pidTuningPidTuningTipFeedforward": {
"message": "<b>Proportional:</b> You will notice a very strong resistant force to any attempts to move the MultiRotor<br><b>Integral:</b> Increase the ability to hold overall initial position and reduce drift, but also increase the delay in returning to initial position.<br><b>Derivative:</b> Improves the speed at which deviations are recovered, but increases noise<br><b>Feedforward:</b> Usually the PID controller reacts to the error. The Feedforward anticipates the error based on the stick input, giving responsiveness to the quad.<br><b>Rates and Expo</b>: Determine your stick feel based on these parameters. Use the graph and live 3D model to find your favourite rate setting"
},
"pidTuningRatesTip": { "pidTuningRatesTip": {
"message": "Play with the rates and see how those affect the stick curve" "message": "Play with the rates and see how those affect the stick curve"
}, },
@ -3663,6 +3804,18 @@
"onboardLoggingOnboardSDCard": { "onboardLoggingOnboardSDCard": {
"message": "Onboard SD card" "message": "Onboard SD card"
}, },
"onboardLoggingMsc": {
"message": "Mass Storage Mode"
},
"onboardLoggingMscNote": {
"message": "Reboot into <strong>mass storage device (MSC)</strong> mode. Once activated, the onboard flash or SD card on your flight controller will be recognised as a storage device by your computer, and allow you to download your log files. Eject and power cycle your flight controller to leave mass storage device mode."
},
"onboardLoggingRebootMscText": {
"message": "Activate Mass Storage Device Mode"
},
"onboardLoggingMscNotReady": {
"message": "Mass storage mode can not be activated because the storage device is not ready."
},
"dialogConfirmResetTitle": { "dialogConfirmResetTitle": {
"message": "Confirm" "message": "Confirm"
}, },

View file

@ -23,6 +23,8 @@
"https://*.githubusercontent.com/", "https://*.githubusercontent.com/",
"http://*.baseflight.net/", "http://*.baseflight.net/",
"https://*.amazonaws.com/", "https://*.amazonaws.com/",
"https://*.betaflight.tech/",
"https://www.google-analytics.com/",
"serial", "serial",
"usb", "usb",
"storage", "storage",
@ -50,7 +52,6 @@
} }
] ]
}, },
"icons": { "icons": {
"128": "images/bf_icon_128.png" "128": "images/bf_icon_128.png"
} }

17
package-lock.json generated
View file

@ -1,6 +1,6 @@
{ {
"name": "betaflight-configurator", "name": "betaflight-configurator",
"version": "10.3.0", "version": "10.4.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -5900,6 +5900,11 @@
} }
} }
}, },
"object-hash": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.0.tgz",
"integrity": "sha512-05KzQ70lSeGSrZJQXE5wNDiTkBJDlUT/myi6RX9dVIvz7a7Qh4oH93BQdiPMn27nldYvVQCKMUaM83AfizZlsQ=="
},
"object-keys": { "object-keys": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
@ -7237,6 +7242,11 @@
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
"dev": true "dev": true
}, },
"short-unique-id": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-1.1.1.tgz",
"integrity": "sha512-gyWJ4pknxpCx0WlUIdCIwHiQ/4sCy0i0YpDOniXP/TboMMRs6rkg6PPKR2nmNfZ9VYeAIm0x9qz/QmJysd9jNg=="
},
"shortid": { "shortid": {
"version": "2.2.8", "version": "2.2.8",
"resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.8.tgz", "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.8.tgz",
@ -8183,6 +8193,11 @@
"crypto-random-string": "^1.0.0" "crypto-random-string": "^1.0.0"
} }
}, },
"universal-ga": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/universal-ga/-/universal-ga-1.2.0.tgz",
"integrity": "sha512-VSkK/aeymj0XQxNJL+Y/kenQKk2EsfWqi3Cf0EKmmGSuPjpA/R6ofJ6xWfstVqRJPj5RiAnyLpHlcshHRVVHJQ=="
},
"universalify": { "universalify": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",

View file

@ -38,7 +38,10 @@
"i18next": "^10.3.0", "i18next": "^10.3.0",
"i18next-xhr-backend": "^1.5.1", "i18next-xhr-backend": "^1.5.1",
"lru_map": "^0.3.3", "lru_map": "^0.3.3",
"marked": "^0.3.12" "marked": "^0.3.12",
"object-hash": "^1.3.0",
"short-unique-id": "^1.1.1",
"universal-ga": "^1.2.0"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.1.2", "chai": "^4.1.2",

16385
resources/osd/vision.mcm Normal file

File diff suppressed because it is too large Load diff

View file

@ -255,3 +255,62 @@
#changelog .log p { #changelog .log p {
margin-bottom: 20px; margin-bottom: 20px;
} }
/* privacy policy block */
#privacy_policy {
width: 500px;
height: 100%;
position: fixed;
right: -495px;
top: 0px;
}
#privacy_policy .wrapper {
height: 100%;
padding: 0 20px;
border-left: 5px solid #ffbb00;
overflow-y: auto;
display: none;
}
#privacy_policy .button {
transform: rotate(270deg);
top: 170px;
right: 450px;
position: absolute;
background: #ffbb00;
border-radius: 5px 5px 0 0;
border-bottom: none;
height: 30px;
}
#privacy_policy .button a {
display: block;
padding: 5px 10px;
width: 100px;
text-align: center;
color: #000;
}
#content.policy_open #privacy_policy {
right: 0px;
background: white;
}
#content.policy_open #privacy_policy .wrapper {
display: block;
}
/* privacy policy content */
#privacy_policy .policy ul {
margin: 5px 0 20px 10px;
}
#privacy_policy .policy li {
font-weight: normal;
margin-bottom: 5px;
}
#privacy_policy .policy p {
margin-bottom: 20px;
}

View file

@ -206,6 +206,22 @@
display: none; display: none;
} }
.require-msc-supported {
display: none;
}
.tab-onboard_logging.msc-supported .require-msc-supported {
display: block;
}
.require-msc-not-ready {
display: none;
}
.tab-onboard_logging.msc-not-ready .require-msc-not-ready {
display: block;
}
@media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) { @media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) {
.tab-onboard_logging table thead tr:first-child { .tab-onboard_logging table thead tr:first-child {
font-size: 12px; font-size: 12px;

View file

@ -402,13 +402,17 @@ button {
cursor: pointer; cursor: pointer;
} }
.fontbuttons { .fontpresets_wrapper {
display: inline-block; display: inline-block;
position: absolute; position: absolute;
right: 1.2em; right: 1.2em;
top: .8em; top: .8em;
} }
.fontpresets {
border: 1px solid #cccccc;
}
.tab-osd .switchable-field { .tab-osd .switchable-field {
padding: 3px; padding: 3px;
border: 1px solid transparent; border: 1px solid transparent;

View file

@ -222,6 +222,7 @@
margin: 0px; margin: 0px;
border-collapse: collapse; border-collapse: collapse;
width: 100%; width: 100%;
table-layout: fixed;
} }
.tab-pid_tuning .gui_box { .tab-pid_tuning .gui_box {
@ -244,12 +245,10 @@
padding: 5px; padding: 5px;
text-align: left; text-align: left;
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
width: 12.5%;
} }
.tab-pid_tuning .pid_titlebar th:first-child { .tab-pid_tuning .pid_titlebar th:first-child {
text-align: left; text-align: left;
width: 12.5%;
} }
.tab-pid_tuning .pid_titlebar th:last-child { .tab-pid_tuning .pid_titlebar th:last-child {
@ -281,6 +280,10 @@
width: 33%; width: 33%;
} }
.tab-pid_tuning table.compensation tr {
height: 25px;
}
.tab-pid_tuning table.compensation td { .tab-pid_tuning table.compensation td {
width: 60%; width: 60%;
padding-left: 5px; padding-left: 5px;
@ -289,12 +292,24 @@
.tab-pid_tuning table.compensation td:first-child { .tab-pid_tuning table.compensation td:first-child {
width: 10%; width: 10%;
text-align: right;
} }
.tab-pid_tuning table.compensation td:last-child { .tab-pid_tuning table.compensation td:last-child {
width: 30%; width: 30%;
} }
.tab-pid_tuning table.compensation .suboption {
margin-left: 40px;
}
.tab-pid_tuning table.compensation .suboption select{
text-align-last: left;
font-size: 1.1em;
color: darkslategrey;
padding-left: 5px;
}
.tab-pid_tuning .pidTuningFeatures td { .tab-pid_tuning .pidTuningFeatures td {
padding: 5px; padding: 5px;
} }
@ -320,7 +335,6 @@
.tab-pid_tuning table td { .tab-pid_tuning table td {
padding: 1px; padding: 1px;
padding-left: 5px; padding-left: 5px;
width: 12.5%;
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
} }

156
src/js/Analytics.js Normal file
View file

@ -0,0 +1,156 @@
'use strict';
var Analytics = function (trackingId, userId, appName, appVersion, buildType, optOut, debugMode) {
this._trackingId = trackingId;
this.setOptOut(optOut);
this._googleAnalytics = googleAnalytics;
this._googleAnalytics.initialize(this._trackingId, {
storage: 'none',
clientId: userId,
debug: !!debugMode
});
// Make it work for the Chrome App:
this._googleAnalytics.set('forceSSL', true);
this._googleAnalytics.set('transport', 'xhr');
// Make it work for NW.js:
this._googleAnalytics.set('checkProtocolTask', null);
this._googleAnalytics.set('appName', appName);
this._googleAnalytics.set('appVersion', debugMode ? appVersion + '-debug' : appVersion);
this.EVENT_CATEGORIES = {
APPLICATION: 'Application',
FLIGHT_CONTROLLER: 'FlightController',
FIRMWARE: 'Firmware',
};
this.DATA = {
BOARD_TYPE: 'boardType',
API_VERSION: 'apiVersion',
FIRMWARE_TYPE: 'firmwareType',
FIRMWARE_VERSION: 'firmwareVersion',
FIRMWARE_NAME: 'firmwareName',
FIRMWARE_CHECKSUM: 'firmwareChecksum',
FIRMWARE_SOURCE: 'firmwareSource',
FIRMWARE_CHANNEL: 'firmwareChannel',
FIRMWARE_ERASE_ALL: 'firmwareEraseAll',
FIRMWARE_SIZE: 'firmwareSize',
MCU_ID: 'mcuId',
LOGGING_STATUS: 'loggingStatus',
LOG_SIZE: 'logSize',
};
this.DIMENSIONS = {
CONFIGURATOR_BUILD_TYPE: 1,
BOARD_TYPE: 2,
FIRMWARE_TYPE: 3,
FIRMWARE_VERSION: 4,
API_VERSION: 5,
FIRMWARE_NAME: 6,
FIRMWARE_SOURCE: 7,
FIRMWARE_ERASE_ALL: 8,
CONFIGURATOR_EXPERT_MODE: 9,
FIRMWARE_CHANNEL: 10,
LOGGING_STATUS: 11,
MCU_ID: 12,
};
this.METRICS = {
FIRMWARE_SIZE: 1,
LOG_SIZE: 2,
};
this.setDimension(this.DIMENSIONS.CONFIGURATOR_BUILD_TYPE, buildType);
this.resetFlightControllerData();
this.resetFirmwareData();
};
Analytics.prototype.setDimension = function (dimension, value) {
var dimensionName = 'dimension' + dimension;
this._googleAnalytics.custom(dimensionName, value);
}
Analytics.prototype.setMetric = function (metric, value) {
var metricName = 'metric' + metric;
this._googleAnalytics.custom(metricName, value);
}
Analytics.prototype.sendEvent = function (category, action, options) {
this._googleAnalytics.event(category, action, options);
}
Analytics.prototype.sendChangeEvents = function (category, changeList) {
for (var actionName in changeList) {
if (changeList.hasOwnProperty(actionName)) {
var actionValue = changeList[actionName];
if (actionValue !== undefined) {
this.sendEvent(category, actionName, { eventLabel: actionValue });
}
}
}
}
Analytics.prototype.sendAppView = function (viewName) {
this._googleAnalytics.screenview(viewName);
}
Analytics.prototype.sendTiming = function (category, timing, value) {
this._googleAnalytics.timing(category, timing, value);
}
Analytics.prototype.sendException = function (message) {
this._googleAnalytics.exception(message);
}
Analytics.prototype.setOptOut = function (optOut) {
window['ga-disable-' + this._trackingId] = !!optOut;
}
Analytics.prototype._rebuildFlightControllerEvent = function () {
this.setDimension(this.DIMENSIONS.BOARD_TYPE, this._flightControllerData[this.DATA.BOARD_TYPE]);
this.setDimension(this.DIMENSIONS.FIRMWARE_TYPE, this._flightControllerData[this.DATA.FIRMWARE_TYPE]);
this.setDimension(this.DIMENSIONS.FIRMWARE_VERSION, this._flightControllerData[this.DATA.FIRMWARE_VERSION]);
this.setDimension(this.DIMENSIONS.API_VERSION, this._flightControllerData[this.DATA.API_VERSION]);
this.setDimension(this.DIMENSIONS.LOGGING_STATUS, this._flightControllerData[this.DATA.LOGGING_STATUS]);
this.setDimension(this.DIMENSIONS.MCU_ID, this._flightControllerData[this.DATA.MCU_ID]);
this.setMetric(this.METRICS.LOG_SIZE, this._flightControllerData[this.DATA.LOG_SIZE]);
}
Analytics.prototype.setFlightControllerData = function (property, value) {
this._flightControllerData[property] = value;
this._rebuildFlightControllerEvent();
}
Analytics.prototype.resetFlightControllerData = function () {
this._flightControllerData = {};
this._rebuildFlightControllerEvent();
}
Analytics.prototype._rebuildFirmwareEvent = function () {
this.setDimension(this.DIMENSIONS.FIRMWARE_NAME, this._firmwareData[this.DATA.FIRMWARE_NAME]);
this.setDimension(this.DIMENSIONS.FIRMWARE_SOURCE, this._firmwareData[this.DATA.FIRMWARE_SOURCE]);
this.setDimension(this.DIMENSIONS.FIRMWARE_ERASE_ALL, this._firmwareData[this.DATA.FIRMWARE_ERASE_ALL]);
this.setDimension(this.DIMENSIONS.FIRMWARE_CHANNEL, this._firmwareData[this.DATA.FIRMWARE_CHANNEL]);
this.setMetric(this.METRICS.FIRMWARE_SIZE, this._firmwareData[this.DATA.FIRMWARE_SIZE]);
this._googleAnalytics.set('eventLabel', this._firmwareData[this.DATA.FIRMWARE_CHECKSUM]);
}
Analytics.prototype.setFirmwareData = function (property, value) {
this._firmwareData[property] = value;
this._rebuildFirmwareEvent();
}
Analytics.prototype.resetFirmwareData = function () {
this._firmwareData = {};
this._rebuildFirmwareEvent();
}

View file

@ -99,11 +99,16 @@ var Features = function (config) {
self._features = features; self._features = features;
self._featureMask = 0; self._featureMask = 0;
self._analyticsChanges = {};
}; };
Features.prototype.getMask = function () { Features.prototype.getMask = function () {
var self = this; var self = this;
analytics.sendChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self._analyticsChanges);
self._analyticsChanges = {};
return self._featureMask; return self._featureMask;
}; };
@ -127,6 +132,8 @@ Features.prototype.isEnabled = function (featureName) {
Features.prototype.generateElements = function (featuresElements) { Features.prototype.generateElements = function (featuresElements) {
var self = this; var self = this;
self._featureChanges = {};
var listElements = []; var listElements = [];
for (var i = 0; i < self._features.length; i++) { for (var i = 0; i < self._features.length; i++) {
@ -191,29 +198,48 @@ Features.prototype.generateElements = function (featuresElements) {
} }
}; };
Features.prototype.findFeatureByBit = function (bit) {
var self = this;
for (var i = 0; i < self._features.length; i++) {
if (self._features[i].bit == bit) {
return self._features[i];
}
}
}
Features.prototype.updateData = function (featureElement) { Features.prototype.updateData = function (featureElement) {
var self = this; var self = this;
if (featureElement.attr('type') === 'checkbox') { if (featureElement.attr('type') === 'checkbox') {
var bit = featureElement.data('bit'); var bit = featureElement.data('bit');
var featureValue;
if (featureElement.is(':checked')) { if (featureElement.is(':checked')) {
self._featureMask = bit_set(self._featureMask, bit); self._featureMask = bit_set(self._featureMask, bit);
featureValue = 'On';
} else { } else {
self._featureMask = bit_clear(self._featureMask, bit); self._featureMask = bit_clear(self._featureMask, bit);
featureValue = 'Off';
} }
self._analyticsChanges['Feature' + self.findFeatureByBit(bit).name] = featureValue;
} else if (featureElement.prop('localName') === 'select') { } else if (featureElement.prop('localName') === 'select') {
var controlElements = featureElement.children(); var controlElements = featureElement.children();
var selectedBit = featureElement.val(); var selectedBit = featureElement.val();
if (selectedBit !== -1) { if (selectedBit !== -1) {
var selectedFeature;
for (var i = 0; i < controlElements.length; i++) { for (var i = 0; i < controlElements.length; i++) {
var bit = controlElements[i].value; var bit = controlElements[i].value;
if (selectedBit === bit) { if (selectedBit === bit) {
self._featureMask = bit_set(self._featureMask, bit); self._featureMask = bit_set(self._featureMask, bit);
selectedFeature = self.findFeatureByBit(bit);
} else { } else {
self._featureMask = bit_clear(self._featureMask, bit); self._featureMask = bit_clear(self._featureMask, bit);
} }
} }
if (selectedFeature) {
self._analyticsChanges['FeatureGroup-' + selectedFeature.group] = selectedFeature.name;
}
} }
} }
}; };

View file

@ -26,7 +26,7 @@ var LogoManager = LogoManager || {
}, },
// config for logo image selection dialog // config for logo image selection dialog
acceptFileTypes: [ acceptFileTypes: [
{ extensions: ['png', 'bmp'] }, { description: 'images', extensions: ['png', 'bmp'] },
], ],
}; };

View file

@ -141,6 +141,11 @@ function configuration_backup(callback) {
configuration.SERIAL_CONFIG = jQuery.extend(true, {}, SERIAL_CONFIG); configuration.SERIAL_CONFIG = jQuery.extend(true, {}, SERIAL_CONFIG);
configuration.LED_STRIP = jQuery.extend(true, [], LED_STRIP); configuration.LED_STRIP = jQuery.extend(true, [], LED_STRIP);
configuration.LED_COLORS = jQuery.extend(true, [], LED_COLORS); configuration.LED_COLORS = jQuery.extend(true, [], LED_COLORS);
configuration.BOARD_ALIGNMENT_CONFIG = jQuery.extend(true, {}, BOARD_ALIGNMENT_CONFIG);
configuration.CRAFT_NAME = CONFIG.name;
configuration.MIXER_CONFIG = jQuery.extend(true, {}, MIXER_CONFIG);
configuration.SENSOR_CONFIG = jQuery.extend(true, {}, SENSOR_CONFIG);
configuration.PID_ADVANCED_CONFIG = jQuery.extend(true, {}, PID_ADVANCED_CONFIG);
if (semver.gte(CONFIG.apiVersion, "1.19.0")) { if (semver.gte(CONFIG.apiVersion, "1.19.0")) {
configuration.LED_MODE_COLORS = jQuery.extend(true, [], LED_MODE_COLORS); configuration.LED_MODE_COLORS = jQuery.extend(true, [], LED_MODE_COLORS);
@ -165,13 +170,31 @@ function configuration_backup(callback) {
configuration.GPS_CONFIG = jQuery.extend(true, {}, GPS_CONFIG); configuration.GPS_CONFIG = jQuery.extend(true, {}, GPS_CONFIG);
configuration.COMPASS_CONFIG = jQuery.extend(true, {}, COMPASS_CONFIG); configuration.COMPASS_CONFIG = jQuery.extend(true, {}, COMPASS_CONFIG);
} }
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
configuration.BEEPER_CONFIG = jQuery.extend(true, {}, BEEPER_CONFIG);
}
save(); save();
} }
} }
// start fetching if (GUI.configuration_loaded === true) {
fetch_unique_data_item(); return fetch_unique_data_item();
}
MSP.promise(MSPCodes.MSP_ADVANCED_CONFIG).then(function() {
return MSP.promise(MSPCodes.MSP_SENSOR_CONFIG);
}).then(function() {
return MSP.promise(MSPCodes.MSP_NAME);
}).then(function() {
return MSP.promise(MSPCodes.MSP_BOARD_ALIGNMENT_CONFIG);
}).then(function() {
return MSP.promise(MSPCodes.MSP_MIXER_CONFIG);
}).then(function() {
return MSP.promise(MSPCodes.MSP_BEEPER_CONFIG);
}).then(function() {
return fetch_unique_data_item();
});
} }
function save() { function save() {
@ -183,7 +206,7 @@ function configuration_backup(callback) {
var filename = generateFilename(prefix, suffix); var filename = generateFilename(prefix, suffix);
var accepts = [{ var accepts = [{
extensions: [suffix] description: suffix.toUpperCase() + ' files', extensions: [suffix]
}]; }];
// create or load the file // create or load the file
@ -231,6 +254,7 @@ function configuration_backup(callback) {
return; return;
} }
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Backup');
console.log('Write SUCCESSFUL'); console.log('Write SUCCESSFUL');
if (callback) callback(); if (callback) callback();
}; };
@ -254,7 +278,7 @@ function configuration_restore(callback) {
var chosenFileEntry = null; var chosenFileEntry = null;
var accepts = [{ var accepts = [{
extensions: ['json'] description: 'JSON files', extensions: ['json']
}]; }];
// load up the file // load up the file
@ -304,25 +328,22 @@ function configuration_restore(callback) {
// validate // validate
if (typeof configuration.generatedBy !== 'undefined' && compareVersions(configuration.generatedBy, CONFIGURATOR.backupFileMinVersionAccepted)) { if (typeof configuration.generatedBy !== 'undefined' && compareVersions(configuration.generatedBy, CONFIGURATOR.backupFileMinVersionAccepted)) {
if (!compareVersions(configuration.generatedBy, "1.14.0") && !migrate(configuration)) { if (!compareVersions(configuration.generatedBy, "1.14.0") && !migrate(configuration)) {
GUI.log(i18n.getMessage('backupFileUnmigratable')); GUI.log(i18n.getMessage('backupFileUnmigratable'));
return; return;
} }
if (configuration.FEATURE_CONFIG.features._featureMask) { if (configuration.FEATURE_CONFIG.features._featureMask) {
var features = new Features(CONFIG); var features = new Features(CONFIG);
features.setMask(configuration.FEATURE_CONFIG.features._featureMask); features.setMask(configuration.FEATURE_CONFIG.features._featureMask);
configuration.FEATURE_CONFIG.features = features; configuration.FEATURE_CONFIG.features = features;
} }
configuration_upload(configuration, callback); analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Restore');
configuration_upload(configuration, callback);
} else { } else {
GUI.log(i18n.getMessage('backupFileIncompatible')); GUI.log(i18n.getMessage('backupFileIncompatible'));
} }
} }
}; };
@ -763,6 +784,13 @@ function configuration_restore(callback) {
]; ];
function update_unique_data_list() { function update_unique_data_list() {
uniqueData.push(MSPCodes.MSP_SET_NAME);
uniqueData.push(MSPCodes.MSP_SET_SENSOR_CONFIG);
uniqueData.push(MSPCodes.MSP_SET_MIXER_CONFIG);
uniqueData.push(MSPCodes.MSP_SET_BEEPER_CONFIG);
uniqueData.push(MSPCodes.MSP_SET_BOARD_ALIGNMENT_CONFIG);
uniqueData.push(MSPCodes.MSP_SET_ADVANCED_CONFIG);
if (semver.gte(CONFIG.apiVersion, "1.8.0")) { if (semver.gte(CONFIG.apiVersion, "1.8.0")) {
uniqueData.push(MSPCodes.MSP_SET_LOOP_TIME); uniqueData.push(MSPCodes.MSP_SET_LOOP_TIME);
uniqueData.push(MSPCodes.MSP_SET_ARMING_CONFIG); uniqueData.push(MSPCodes.MSP_SET_ARMING_CONFIG);
@ -803,6 +831,17 @@ function configuration_restore(callback) {
GPS_CONFIG = configuration.GPS_CONFIG; GPS_CONFIG = configuration.GPS_CONFIG;
COMPASS_CONFIG = configuration.COMPASS_CONFIG; COMPASS_CONFIG = configuration.COMPASS_CONFIG;
RSSI_CONFIG = configuration.RSSI_CONFIG; RSSI_CONFIG = configuration.RSSI_CONFIG;
BOARD_ALIGNMENT_CONFIG = configuration.BOARD_ALIGNMENT_CONFIG;
CONFIG.name = configuration.CRAFT_NAME;
MIXER_CONFIG = configuration.MIXER_CONFIG;
SENSOR_CONFIG = configuration.SENSOR_CONFIG;
PID_ADVANCED_CONFIG = configuration.PID_ADVANCED_CONFIG;
BEEPER_CONFIG.beepers = new Beepers(CONFIG);
BEEPER_CONFIG.beepers.setMask(configuration.BEEPER_CONFIG.beepers._beeperMask);
BEEPER_CONFIG.dshotBeaconTone = configuration.BEEPER_CONFIG.dshotBeaconTone;
BEEPER_CONFIG.dshotBeaconConditions = new Beepers(CONFIG, [ "RX_LOST", "RX_SET" ]);
BEEPER_CONFIG.dshotBeaconConditions.setMask(configuration.BEEPER_CONFIG.dshotBeaconConditions._beeperMask);
} }
function send_unique_data_item() { function send_unique_data_item() {

View file

@ -369,6 +369,18 @@ var FC = {
levelSensitivity: 0, levelSensitivity: 0,
itermThrottleThreshold: 0, itermThrottleThreshold: 0,
itermAcceleratorGain: 0, itermAcceleratorGain: 0,
itermRotation: 0,
smartFeedforward: 0,
itermRelax: 0,
itermRelaxType: 0,
absoluteControlGain: 0,
throttleBoost: 0,
acroTrainerAngleLimit: 0,
feedforwardRoll: 0,
feedforwardPitch: 0,
feedforwardYaw: 0,
feedforwardTransition: 0,
antiGravityMode: 0,
}; };
SENSOR_CONFIG = { SENSOR_CONFIG = {

View file

@ -321,5 +321,17 @@ GUI_control.prototype.content_ready = function (callback) {
if (callback) callback(); if (callback) callback();
} }
GUI_control.prototype.selectDefaultTabWhenConnected = function() {
chrome.storage.local.get(['rememberLastTab', 'lastTab'], function (result) {
if (!(result.rememberLastTab
&& !!result.lastTab
&& result.lastTab.substring(4) != "cli")) {
$('#tabs ul.mode-connected .tab_setup a').click();
return;
}
$("#tabs ul.mode-connected ." + result.lastTab + " a").click();
});
};
// initialize object into GUI variable // initialize object into GUI variable
var GUI = new GUI_control(); var GUI = new GUI_control();

View file

@ -1,66 +1,124 @@
'use strict;' 'use strict;'
var JenkinsLoader = function (url, jobName) { var JenkinsLoader = function (url) {
var self = this; this._url = url;
this._jobs = [];
this._cacheExpirationPeriod = 3600 * 1000;
self._url = url; this._jobsRequest = '/api/json?tree=jobs[name]';
self._jobName = jobName; this._buildsRequest = '/api/json?tree=builds[number,result,timestamp,artifacts[relativePath],changeSet[items[commitId,msg]]]';
self._jobUrl = self._url + '/job/' + self._jobName;
self._buildsRequest = '/api/json?tree=builds[number,result,timestamp,artifacts[relativePath],changeSet[items[commitId,msg]]]';
self._builds = {};
self._buildsDataTag = `${self._jobUrl}BuildsData`;
self._cacheLastUpdateTag = `${self._jobUrl}BuildsLastUpdate`
} }
JenkinsLoader.prototype.loadBuilds = function (callback) { JenkinsLoader.prototype.loadJobs = function (viewName, callback) {
var self = this; var self = this;
chrome.storage.local.get([self._cacheLastUpdateTag, self._buildsDataTag], function (result) { var viewUrl = `${self._url}/view/${viewName}`;
var jobsDataTag = '${viewUrl}JobsData';
var cacheLastUpdateTag = '${viewUrl}JobsLastUpdate';
var wrappedCallback = jobs => {
self._jobs = jobs;
callback(jobs);
};
chrome.storage.local.get([cacheLastUpdateTag, jobsDataTag], function (result) {
var jobsDataTimestamp = $.now();
var cachedJobsData = result[jobsDataTag];
var cachedJobsLastUpdate = result[cacheLastUpdateTag];
var cachedCallback = () => {
if (cachedJobsData) {
GUI.log(i18n.getMessage('buildServerUsingCached', ['jobs']));
}
wrappedCallback(cachedJobsData ? cachedJobsData : []);
};
if (!cachedJobsData || !cachedJobsLastUpdate || jobsDataTimestamp - cachedJobsLastUpdate > self._cacheExpirationPeriod) {
var url = `${viewUrl}${self._jobsRequest}`;
$.get(url, jobsInfo => {
GUI.log(i18n.getMessage('buildServerLoaded', ['jobs']));
// remove Betaflight prefix, rename Betaflight job to Development
var jobs = jobsInfo.jobs.map(job => {
return { title: job.name.replace('Betaflight ', '').replace('Betaflight', 'Development'), name: job.name };
})
// cache loaded info
object = {}
object[jobsDataTag] = jobs;
object[cacheLastUpdateTag] = $.now();
chrome.storage.local.set(object);
wrappedCallback(jobs);
}).error(xhr => {
GUI.log(i18n.getMessage('buildServerLoadFailed', ['jobs', `HTTP ${xhr.status}`]));
}).fail(cachedCallback);
} else {
cachedCallback();
}
});
}
JenkinsLoader.prototype.loadBuilds = function (jobName, callback) {
var self = this;
var jobUrl = `${self._url}/job/${jobName}`;
var buildsDataTag = `${jobUrl}BuildsData`;
var cacheLastUpdateTag = `${jobUrl}BuildsLastUpdate`
chrome.storage.local.get([cacheLastUpdateTag, buildsDataTag], function (result) {
var buildsDataTimestamp = $.now(); var buildsDataTimestamp = $.now();
var cachedBuildsData = result[self._buildsDataTag]; var cachedBuildsData = result[buildsDataTag];
var cachedBuildsLastUpdate = result[self._cacheLastUpdateTag]; var cachedBuildsLastUpdate = result[cacheLastUpdateTag];
if (!cachedBuildsData || !cachedBuildsLastUpdate || buildsDataTimestamp - cachedBuildsLastUpdate > 3600 * 1000) { var cachedCallback = () => {
var request = self._jobUrl + self._buildsRequest; if (cachedBuildsData) {
GUI.log(i18n.getMessage('buildServerUsingCached', [jobName]));
}
self._parseBuilds(jobUrl, jobName, cachedBuildsData ? cachedBuildsData : [], callback);
};
if (!cachedBuildsData || !cachedBuildsLastUpdate || buildsDataTimestamp - cachedBuildsLastUpdate > self._cacheExpirationPeriod) {
var url = `${jobUrl}${self._buildsRequest}`;
$.get(url, function (buildsInfo) {
GUI.log(i18n.getMessage('buildServerLoaded', [jobName]));
$.get(request, function (buildsInfo) {
// filter successful builds // filter successful builds
self._builds = buildsInfo.builds.filter(build => build.result == 'SUCCESS') var builds = buildsInfo.builds.filter(build => build.result == 'SUCCESS')
.map(build => ({ .map(build => ({
number: build.number, number: build.number,
artifacts: build.artifacts.map(artifact => artifact.relativePath), artifacts: build.artifacts.map(artifact => artifact.relativePath),
changes: build.changeSet.items.map(item => '* ' + item.msg).join('<br>\n'), changes: build.changeSet.items.map(item => '* ' + item.msg).join('<br>\n'),
date: new Date(build.timestamp) timestamp: build.timestamp
})); }));
self._parseBuilds(callback); // cache loaded info
}).fail(function (data) { object = {}
GUI.log(i18n.getMessage('releaseCheckFailed', [self._jobName, 'failed to load builds'])); object[buildsDataTag] = builds;
object[cacheLastUpdateTag] = $.now();
chrome.storage.local.set(object);
self._builds = cachedBuildsData; self._parseBuilds(jobUrl, jobName, builds, callback);
self._parseBuilds(callback); }).error(xhr => {
}); GUI.log(i18n.getMessage('buildServerLoadFailed', [jobName, `HTTP ${xhr.status}`]));
}).fail(cachedCallback);
} else { } else {
if (cachedBuildsData) { cachedCallback();
GUI.log(i18n.getMessage('releaseCheckCached', [self._jobName]));
}
self._builds = cachedBuildsData;
self._parseBuilds(callback);
} }
}); });
} }
JenkinsLoader.prototype._parseBuilds = function (callback) { JenkinsLoader.prototype._parseBuilds = function (jobUrl, jobName, builds, callback) {
var self = this;
// convert from `build -> targets` to `target -> builds` mapping // convert from `build -> targets` to `target -> builds` mapping
var targetBuilds = {}; var targetBuilds = {};
var targetFromFilenameExpression = /betaflight_([\d.]+)?_?(\w+)(\-.*)?\.(.*)/; var targetFromFilenameExpression = /betaflight_([\d.]+)?_?(\w+)(\-.*)?\.(.*)/;
self._builds.forEach(build => { builds.forEach(build => {
build.artifacts.forEach(relativePath => { build.artifacts.forEach(relativePath => {
var match = targetFromFilenameExpression.exec(relativePath); var match = targetFromFilenameExpression.exec(relativePath);
@ -70,15 +128,16 @@ JenkinsLoader.prototype._parseBuilds = function (callback) {
var version = match[1]; var version = match[1];
var target = match[2]; var target = match[2];
var date = new Date(build.timestamp);
var formattedDate = ("0" + build.date.getDate()).slice(-2) + "-" + ("0" + (build.date.getMonth()+1)).slice(-2) + "-" + var formattedDate = ("0" + date.getDate()).slice(-2) + "-" + ("0" + (date.getMonth()+1)).slice(-2) + "-" +
build.date.getFullYear() + " " + ("0" + build.date.getHours()).slice(-2) + ":" + ("0" + build.date.getMinutes()).slice(-2); date.getFullYear() + " " + ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2);
var descriptor = { var descriptor = {
'releaseUrl': self._jobUrl + '/' + build.number, 'releaseUrl': jobUrl + '/' + build.number,
'name' : self._jobName + ' #' + build.number, 'name' : jobName + ' #' + build.number,
'version' : version + ' #' + build.number, 'version' : version + ' #' + build.number,
'url' : self._jobUrl + '/' + build.number + '/artifact/' + relativePath, 'url' : jobUrl + '/' + build.number + '/artifact/' + relativePath,
'file' : relativePath.split('/').slice(-1)[0], 'file' : relativePath.split('/').slice(-1)[0],
'target' : target, 'target' : target,
'date' : formattedDate, 'date' : formattedDate,

View file

@ -1,9 +1,10 @@
'use strict'; 'use strict';
var googleAnalytics = analytics;
var analytics = undefined;
openNewWindowsInExternalBrowser(); openNewWindowsInExternalBrowser();
//Asynchronous configuration to be done.
//When finish the startProcess() function must be called
$(document).ready(function () { $(document).ready(function () {
i18n.init(function() { i18n.init(function() {
startProcess(); startProcess();
@ -11,9 +12,67 @@ $(document).ready(function () {
}); });
}); });
function checkSetupAnalytics(callback) {
if (!analytics) {
setTimeout(function () {
chrome.storage.local.get(['userId', 'analyticsOptOut'], function (result) {
if (!analytics) {
setupAnalytics(result);
}
callback(analytics);
});
});
} else if (callback) {
callback(analytics);
}
};
function setupAnalytics(result) {
var userId;
if (result.userId) {
userId = result.userId;
} else {
var uid = new ShortUniqueId();
userId = uid.randomUUID(13);
chrome.storage.local.set({ 'userId': userId });
}
var optOut = !!result.analyticsOptOut;
var debugMode = process.versions['nw-flavor'] === 'sdk';
analytics = new Analytics('UA-123002063-1', userId, 'Betaflight Configurator', getManifestVersion(), GUI.operating_system, optOut, debugMode);
function logException(exception) {
analytics.sendException(exception.stack);
}
process.on('uncaughtException', logException);
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'AppStart', { sessionControl: 'start' });
function sendCloseEvent() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'AppClose', { sessionControl: 'end' })
}
try {
var gui = require('nw.gui');
var win = gui.Window.get();
win.on('close', function () {
sendCloseEvent();
this.close(true);
});
} catch (ex) {
// Looks like we're in Chrome - but the event does not actually get fired
chrome.runtime.onSuspend.addListener(sendCloseEvent);
}
}
//Process to execute to real start the app //Process to execute to real start the app
function startProcess() { function startProcess() {
// translate to user-selected language // translate to user-selected language
i18n.localizePage(); i18n.localizePage();
@ -86,6 +145,13 @@ function startProcess() {
return; return;
} }
$("#tabs ul.mode-connected li").click(function() {
// store the first class of the current tab (omit things like ".active")
chrome.storage.local.set({
lastTab: $(this).attr("class").split(' ')[0]
});
});
GUI.tab_switch_in_progress = true; GUI.tab_switch_in_progress = true;
GUI.tab_switch_cleanup(function () { GUI.tab_switch_cleanup(function () {
@ -106,6 +172,10 @@ function startProcess() {
GUI.tab_switch_in_progress = false; GUI.tab_switch_in_progress = false;
} }
checkSetupAnalytics(function (analytics) {
analytics.sendAppView(tab);
});
switch (tab) { switch (tab) {
case 'landing': case 'landing':
TABS.landing.initialize(content_ready); TABS.landing.initialize(content_ready);
@ -210,13 +280,16 @@ function startProcess() {
chrome.storage.local.set({'permanentExpertMode': checked}); chrome.storage.local.set({'permanentExpertMode': checked});
$('input[name="expertModeCheckbox"]').prop('checked', checked).change(); $('input[name="expertModeCheckbox"]').prop('checked', checked).change();
if (FEATURE_CONFIG) {
updateTabList(FEATURE_CONFIG.features);
}
}).change(); }).change();
}); });
chrome.storage.local.get('rememberLastTab', function (result) {
$('div.rememberLastTab input')
.prop('checked', !!result.rememberLastTab)
.change(function() { chrome.storage.local.set({rememberLastTab: $(this).is(':checked')}) })
.change();
});
if (GUI.operating_system !== 'ChromeOS') { if (GUI.operating_system !== 'ChromeOS') {
chrome.storage.local.get('checkForConfiguratorUnstableVersions', function (result) { chrome.storage.local.get('checkForConfiguratorUnstableVersions', function (result) {
if (result.checkForConfiguratorUnstableVersions) { if (result.checkForConfiguratorUnstableVersions) {
@ -235,6 +308,30 @@ function startProcess() {
$('div.checkForConfiguratorUnstableVersions').hide(); $('div.checkForConfiguratorUnstableVersions').hide();
} }
chrome.storage.local.get('analyticsOptOut', function (result) {
if (result.analyticsOptOut) {
$('div.analyticsOptOut input').prop('checked', true);
}
$('div.analyticsOptOut input').change(function () {
var checked = $(this).is(':checked');
chrome.storage.local.set({'analyticsOptOut': checked});
checkSetupAnalytics(function (analytics) {
if (checked) {
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'OptOut');
}
analytics.setOptOut(checked);
if (!checked) {
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'OptIn');
}
});
}).change();
});
chrome.storage.local.get('userLanguageSelect', function (result) { chrome.storage.local.get('userLanguageSelect', function (result) {
var userLanguage_e = $('div.userLanguage select'); var userLanguage_e = $('div.userLanguage select');
@ -385,6 +482,11 @@ function startProcess() {
} }
$('input[name="expertModeCheckbox"]').change(function () { $('input[name="expertModeCheckbox"]').change(function () {
var checked = $(this).is(':checked');
checkSetupAnalytics(function (analytics) {
analytics.setDimension(analytics.DIMENSIONS.CONFIGURATOR_EXPERT_MODE, checked ? 'On' : 'Off');
});
if (FEATURE_CONFIG) { if (FEATURE_CONFIG) {
updateTabList(FEATURE_CONFIG.features); updateTabList(FEATURE_CONFIG.features);
} }
@ -643,3 +745,15 @@ function openNewWindowsInExternalBrowser() {
console.log("require does not exist, maybe inside chrome"); console.log("require does not exist, maybe inside chrome");
} }
} }
function showErrorDialog(message) {
var dialog = $('.dialogError')[0];
$('.dialogError-content').html(message);
$('.dialogError-closebtn').click(function() {
dialog.close();
});
dialog.showModal();
}

View file

@ -27,6 +27,12 @@ function MspHelper () {
'RUNCAM_DEVICE_CONTROL': 14, // support communitate with RunCam Device 'RUNCAM_DEVICE_CONTROL': 14, // support communitate with RunCam Device
'LIDAR_TF': 15 'LIDAR_TF': 15
}; };
self.REBOOT_TYPES = {
FIRMWARE: 0,
BOOTLOADER: 1,
MSC: 2
};
} }
MspHelper.prototype.reorderPwmProtocols = function (protocol) { MspHelper.prototype.reorderPwmProtocols = function (protocol) {
@ -617,6 +623,17 @@ MspHelper.prototype.process_data = function(dataHandler) {
break; break;
case MSPCodes.MSP_SET_REBOOT: case MSPCodes.MSP_SET_REBOOT:
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
var rebootType = data.read8();
if (rebootType === self.REBOOT_TYPES.MSC) {
if (data.read8() === 0) {
console.log('Storage device not ready.');
showErrorDialog(i18n.getMessage('storageDeviceNotReady'));
break;
}
}
}
console.log('Reboot request accepted'); console.log('Reboot request accepted');
break; break;
@ -871,7 +888,11 @@ MspHelper.prototype.process_data = function(dataHandler) {
ADVANCED_TUNING.deltaMethod = data.readU8(); ADVANCED_TUNING.deltaMethod = data.readU8();
ADVANCED_TUNING.vbatPidCompensation = data.readU8(); ADVANCED_TUNING.vbatPidCompensation = data.readU8();
if (semver.gte(CONFIG.apiVersion, "1.20.0")) { if (semver.gte(CONFIG.apiVersion, "1.20.0")) {
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
ADVANCED_TUNING.feedforwardTransition = data.readU8();
} else {
ADVANCED_TUNING.dtermSetpointTransition = data.readU8(); ADVANCED_TUNING.dtermSetpointTransition = data.readU8();
}
ADVANCED_TUNING.dtermSetpointWeight = data.readU8(); ADVANCED_TUNING.dtermSetpointWeight = data.readU8();
ADVANCED_TUNING.toleranceBand = data.readU8(); ADVANCED_TUNING.toleranceBand = data.readU8();
ADVANCED_TUNING.toleranceBandReduction = data.readU8(); ADVANCED_TUNING.toleranceBandReduction = data.readU8();
@ -881,13 +902,29 @@ MspHelper.prototype.process_data = function(dataHandler) {
if (semver.gte(CONFIG.apiVersion, "1.24.0")) { if (semver.gte(CONFIG.apiVersion, "1.24.0")) {
ADVANCED_TUNING.levelAngleLimit = data.readU8(); ADVANCED_TUNING.levelAngleLimit = data.readU8();
ADVANCED_TUNING.levelSensitivity = data.readU8(); ADVANCED_TUNING.levelSensitivity = data.readU8();
}
if (semver.gte(CONFIG.apiVersion, "1.36.0")) { if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
ADVANCED_TUNING.itermThrottleThreshold = data.readU16(); ADVANCED_TUNING.itermThrottleThreshold = data.readU16();
ADVANCED_TUNING.itermAcceleratorGain = data.readU16(); ADVANCED_TUNING.itermAcceleratorGain = data.readU16();
}
if (semver.gte(CONFIG.apiVersion, "1.39.0")) { if (semver.gte(CONFIG.apiVersion, "1.39.0")) {
ADVANCED_TUNING.dtermSetpointWeight = data.readU16(); ADVANCED_TUNING.dtermSetpointWeight = data.readU16();
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
ADVANCED_TUNING.itermRotation = data.readU8();
ADVANCED_TUNING.smartFeedforward = data.readU8();
ADVANCED_TUNING.itermRelax = data.readU8();
ADVANCED_TUNING.itermRelaxType = data.readU8();
ADVANCED_TUNING.absoluteControlGain = data.readU8();
ADVANCED_TUNING.throttleBoost = data.readU8();
ADVANCED_TUNING.acroTrainerAngleLimit = data.readU8();
ADVANCED_TUNING.feedforwardRoll = data.readU16();
ADVANCED_TUNING.feedforwardPitch = data.readU16();
ADVANCED_TUNING.feedforwardYaw = data.readU16();
ADVANCED_TUNING.antiGravityMode = data.readU8();
}
}
}
} }
} }
break; break;
@ -1119,7 +1156,9 @@ MspHelper.prototype.process_data = function(dataHandler) {
case MSPCodes.MSP_SET_ADJUSTMENT_RANGE: case MSPCodes.MSP_SET_ADJUSTMENT_RANGE:
console.log('Adjustment range saved'); console.log('Adjustment range saved');
break; break;
case MSPCodes.MSP_SET_BOARD_ALIGNMENT_CONFIG:
console.log('Board alignment saved');
break;
case MSPCodes.MSP_PID_CONTROLLER: case MSPCodes.MSP_PID_CONTROLLER:
PID.controller = data.readU8(); PID.controller = data.readU8();
break; break;
@ -1195,6 +1234,11 @@ MspHelper.prototype.process_data = function(dataHandler) {
console.log('Unknown code detected: ' + code); console.log('Unknown code detected: ' + code);
} else { } else {
console.log('FC reports unsupported message error: ' + code); console.log('FC reports unsupported message error: ' + code);
switch (code) {
case MSPCodes.MSP_SET_REBOOT:
showErrorDialog(i18n.getMessage('operationNotSupported'));
}
} }
} }
// trigger callbacks, cleanup/remove callback after trigger // trigger callbacks, cleanup/remove callback after trigger
@ -1217,7 +1261,6 @@ MspHelper.prototype.process_data = function(dataHandler) {
} }
} }
/** /**
* Encode the request body for the MSP request with the given code and return it as an array of bytes. * Encode the request body for the MSP request with the given code and return it as an array of bytes.
*/ */
@ -1526,24 +1569,47 @@ MspHelper.prototype.crunch = function(code) {
.push16(ADVANCED_TUNING.yawItermIgnoreRate) .push16(ADVANCED_TUNING.yawItermIgnoreRate)
.push16(ADVANCED_TUNING.yaw_p_limit) .push16(ADVANCED_TUNING.yaw_p_limit)
.push8(ADVANCED_TUNING.deltaMethod) .push8(ADVANCED_TUNING.deltaMethod)
.push8(ADVANCED_TUNING.vbatPidCompensation) .push8(ADVANCED_TUNING.vbatPidCompensation);
.push8(ADVANCED_TUNING.dtermSetpointTransition)
.push8(Math.min(ADVANCED_TUNING.dtermSetpointWeight, 254)) if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
buffer.push8(ADVANCED_TUNING.feedforwardTransition);
} else {
buffer.push8(ADVANCED_TUNING.dtermSetpointTransition);
}
buffer.push8(Math.min(ADVANCED_TUNING.dtermSetpointWeight, 254))
.push8(ADVANCED_TUNING.toleranceBand) .push8(ADVANCED_TUNING.toleranceBand)
.push8(ADVANCED_TUNING.toleranceBandReduction) .push8(ADVANCED_TUNING.toleranceBandReduction)
.push8(ADVANCED_TUNING.itermThrottleGain) .push8(ADVANCED_TUNING.itermThrottleGain)
.push16(ADVANCED_TUNING.pidMaxVelocity) .push16(ADVANCED_TUNING.pidMaxVelocity)
.push16(ADVANCED_TUNING.pidMaxVelocityYaw); .push16(ADVANCED_TUNING.pidMaxVelocityYaw);
if (semver.gte(CONFIG.apiVersion, "1.24.0")) { if (semver.gte(CONFIG.apiVersion, "1.24.0")) {
buffer.push8(ADVANCED_TUNING.levelAngleLimit) buffer.push8(ADVANCED_TUNING.levelAngleLimit)
.push8(ADVANCED_TUNING.levelSensitivity); .push8(ADVANCED_TUNING.levelSensitivity);
}
if (semver.gte(CONFIG.apiVersion, "1.36.0")) { if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
buffer.push16(ADVANCED_TUNING.itermThrottleThreshold) buffer.push16(ADVANCED_TUNING.itermThrottleThreshold)
.push16(ADVANCED_TUNING.itermAcceleratorGain); .push16(ADVANCED_TUNING.itermAcceleratorGain);
}
if (semver.gte(CONFIG.apiVersion, "1.39.0")) { if (semver.gte(CONFIG.apiVersion, "1.39.0")) {
buffer.push16(ADVANCED_TUNING.dtermSetpointWeight); buffer.push16(ADVANCED_TUNING.dtermSetpointWeight);
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
buffer.push8(ADVANCED_TUNING.itermRotation)
.push8(ADVANCED_TUNING.smartFeedforward)
.push8(ADVANCED_TUNING.itermRelax)
.push8(ADVANCED_TUNING.itermRelaxType)
.push8(ADVANCED_TUNING.absoluteControlGain)
.push8(ADVANCED_TUNING.throttleBoost)
.push8(ADVANCED_TUNING.acroTrainerAngleLimit)
.push16(ADVANCED_TUNING.feedforwardRoll)
.push16(ADVANCED_TUNING.feedforwardPitch)
.push16(ADVANCED_TUNING.feedforwardYaw)
.push8(ADVANCED_TUNING.antiGravityMode);
}
}
}
} }
} }
// only supports 1 version pre bf 3.0 // only supports 1 version pre bf 3.0
@ -1894,7 +1960,6 @@ MspHelper.prototype.sendCurrentConfig = function(onCompleteCallback) {
} }
MspHelper.prototype.sendLedStripConfig = function(onCompleteCallback) { MspHelper.prototype.sendLedStripConfig = function(onCompleteCallback) {
var nextFunction = send_next_led_strip_config; var nextFunction = send_next_led_strip_config;

View file

@ -1,6 +1,8 @@
'use strict'; 'use strict';
var mspHelper; var mspHelper;
var connectionTimestamp;
function initializeSerialBackend() { function initializeSerialBackend() {
GUI.updateManualPortVisibility = function(){ GUI.updateManualPortVisibility = function(){
@ -43,6 +45,8 @@ function initializeSerialBackend() {
thisElement.data("clicks", !clicks); thisElement.data("clicks", !clicks);
}; };
GUI.configuration_loaded = false;
var selected_baud = parseInt($('div#port-picker #baud').val()); var selected_baud = parseInt($('div#port-picker #baud').val());
var selected_port = $('div#port-picker #port option:selected').data().isManual ? var selected_port = $('div#port-picker #port option:selected').data().isManual ?
$('#port-override').val() : $('#port-override').val() :
@ -122,6 +126,15 @@ function initializeSerialBackend() {
function finishClose(finishedCallback) { function finishClose(finishedCallback) {
var wasConnected = CONFIGURATOR.connectionValid; var wasConnected = CONFIGURATOR.connectionValid;
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Disconnected');
if (connectionTimestamp) {
var connectedTime = Date.now() - connectionTimestamp;
analytics.sendTiming(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Connected', connectedTime);
connectedTime = undefined;
}
analytics.resetFlightControllerData();
serial.disconnect(onClosed); serial.disconnect(onClosed);
MSP.disconnect_cleanup(); MSP.disconnect_cleanup();
@ -198,13 +211,17 @@ function onOpen(openInfo) {
// request configuration data // request configuration data
MSP.send_message(MSPCodes.MSP_API_VERSION, false, false, function () { MSP.send_message(MSPCodes.MSP_API_VERSION, false, false, function () {
analytics.setFlightControllerData(analytics.DATA.API_VERSION, CONFIG.apiVersion);
GUI.log(i18n.getMessage('apiVersionReceived', [CONFIG.apiVersion])); GUI.log(i18n.getMessage('apiVersionReceived', [CONFIG.apiVersion]));
if (semver.gte(CONFIG.apiVersion, CONFIGURATOR.apiVersionAccepted)) { if (semver.gte(CONFIG.apiVersion, CONFIGURATOR.apiVersionAccepted)) {
MSP.send_message(MSPCodes.MSP_FC_VARIANT, false, false, function () { MSP.send_message(MSPCodes.MSP_FC_VARIANT, false, false, function () {
analytics.setFlightControllerData(analytics.DATA.FIRMWARE_TYPE, CONFIG.flightControllerIdentifier);
if (CONFIG.flightControllerIdentifier === 'BTFL') { if (CONFIG.flightControllerIdentifier === 'BTFL') {
MSP.send_message(MSPCodes.MSP_FC_VERSION, false, false, function () { MSP.send_message(MSPCodes.MSP_FC_VERSION, false, false, function () {
analytics.setFlightControllerData(analytics.DATA.FIRMWARE_VERSION, CONFIG.flightControllerVersion);
GUI.log(i18n.getMessage('fcInfoReceived', [CONFIG.flightControllerIdentifier, CONFIG.flightControllerVersion])); GUI.log(i18n.getMessage('fcInfoReceived', [CONFIG.flightControllerIdentifier, CONFIG.flightControllerVersion]));
updateStatusBarVersion(CONFIG.flightControllerVersion, CONFIG.flightControllerIdentifier); updateStatusBarVersion(CONFIG.flightControllerVersion, CONFIG.flightControllerIdentifier);
@ -215,13 +232,19 @@ function onOpen(openInfo) {
GUI.log(i18n.getMessage('buildInfoReceived', [CONFIG.buildInfo])); GUI.log(i18n.getMessage('buildInfoReceived', [CONFIG.buildInfo]));
MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, function () { MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, function () {
analytics.setFlightControllerData(analytics.DATA.BOARD_TYPE, CONFIG.boardIdentifier);
GUI.log(i18n.getMessage('boardInfoReceived', [CONFIG.boardIdentifier, CONFIG.boardVersion])); GUI.log(i18n.getMessage('boardInfoReceived', [CONFIG.boardIdentifier, CONFIG.boardVersion]));
updateStatusBarVersion(CONFIG.flightControllerVersion, CONFIG.flightControllerIdentifier, CONFIG.boardIdentifier); updateStatusBarVersion(CONFIG.flightControllerVersion, CONFIG.flightControllerIdentifier, CONFIG.boardIdentifier);
updateTopBarVersion(CONFIG.flightControllerVersion, CONFIG.flightControllerIdentifier, CONFIG.boardIdentifier); updateTopBarVersion(CONFIG.flightControllerVersion, CONFIG.flightControllerIdentifier, CONFIG.boardIdentifier);
MSP.send_message(MSPCodes.MSP_UID, false, false, function () { MSP.send_message(MSPCodes.MSP_UID, false, false, function () {
GUI.log(i18n.getMessage('uniqueDeviceIdReceived', [CONFIG.uid[0].toString(16) + CONFIG.uid[1].toString(16) + CONFIG.uid[2].toString(16)])); var uniqueDeviceIdentifier = CONFIG.uid[0].toString(16) + CONFIG.uid[1].toString(16) + CONFIG.uid[2].toString(16);
analytics.setFlightControllerData(analytics.DATA.MCU_ID, objectHash.sha1(uniqueDeviceIdentifier));
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Connected');
connectionTimestamp = Date.now();
GUI.log(i18n.getMessage('uniqueDeviceIdReceived', [uniqueDeviceIdentifier]));
if (semver.gte(CONFIG.apiVersion, "1.20.0")) { if (semver.gte(CONFIG.apiVersion, "1.20.0")) {
MSP.send_message(MSPCodes.MSP_NAME, false, false, function () { MSP.send_message(MSPCodes.MSP_NAME, false, false, function () {
@ -238,6 +261,8 @@ function onOpen(openInfo) {
}); });
}); });
} else { } else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'ConnectionRefused');
var dialog = $('.dialogConnectWarning')[0]; var dialog = $('.dialogConnectWarning')[0];
$('.dialogConnectWarning-content').html(i18n.getMessage('firmwareTypeNotSupported')); $('.dialogConnectWarning-content').html(i18n.getMessage('firmwareTypeNotSupported'));
@ -252,6 +277,8 @@ function onOpen(openInfo) {
} }
}); });
} else { } else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'ConnectionRefused');
var dialog = $('.dialogConnectWarning')[0]; var dialog = $('.dialogConnectWarning')[0];
$('.dialogConnectWarning-content').html(i18n.getMessage('firmwareVersionNotSupported', [CONFIGURATOR.apiVersionAccepted])); $('.dialogConnectWarning-content').html(i18n.getMessage('firmwareVersionNotSupported', [CONFIGURATOR.apiVersionAccepted]));
@ -266,6 +293,8 @@ function onOpen(openInfo) {
} }
}); });
} else { } else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'SerialPortFailed');
console.log('Failed to open serial port'); console.log('Failed to open serial port');
GUI.log(i18n.getMessage('serialPortOpenFail')); GUI.log(i18n.getMessage('serialPortOpenFail'));
@ -297,7 +326,7 @@ function finishOpen() {
onConnect(); onConnect();
$('#tabs ul.mode-connected .tab_setup a').click(); GUI.selectDefaultTabWhenConnected();
} }
function connectCli() { function connectCli() {

View file

@ -20,7 +20,6 @@ TABS.adjustments.initialize = function (callback) {
} }
function load_html() { function load_html() {
self.adjust_template();
$('#content').load("./tabs/adjustments.html", process_html); $('#content').load("./tabs/adjustments.html", process_html);
} }
@ -153,6 +152,8 @@ TABS.adjustments.initialize = function (callback) {
function process_html() { function process_html() {
self.adjust_template();
var auxChannelCount = RC.active_channels - 4; var auxChannelCount = RC.active_channels - 4;
var modeTableBodyElement = $('.tab-adjustments .adjustments tbody'); var modeTableBodyElement = $('.tab-adjustments .adjustments tbody');
@ -272,14 +273,36 @@ TABS.adjustments.cleanup = function (callback) {
}; };
TABS.adjustments.adjust_template = function () { TABS.adjustments.adjust_template = function () {
var availableFunctionCount;
if (semver.lt(CONFIG.apiVersion, "1.31.0")) { var selectFunction = $('#functionSelectionSelect');
availableFunctionCount = 21; // Available in betaflight 2.9 var elementsNumber;
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
elementsNumber = 29; // PID Audio
} else if (semver.gte(CONFIG.apiVersion, "1.39.0")) {
elementsNumber = 26; // PID Audio
} else if (semver.gte(CONFIG.apiVersion, "1.37.0")) {
elementsNumber = 25; // Horizon Strength
} else { } else {
availableFunctionCount = 24; // RC rate Yaw / D setpoint / D setpoint transition added to 3.1.0 elementsNumber = 24; // Setpoint transition
}
for (let i = 0; i < elementsNumber; i++) {
selectFunction.append(new Option(i18n.getMessage('adjustmentsFunction' + i), i));
}
// For 1.40, the D Setpoint has been replaced, so we replace it with the correct values
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
var element22 = selectFunction.find("option[value='22']");
var element23 = selectFunction.find("option[value='23']");
// Change the "text"
element22.text(i18n.getMessage('adjustmentsFunction22_2'));
element23.text(i18n.getMessage('adjustmentsFunction23_2'));
// Reorder, we insert it with the other FF elements to be coherent...
element22.insertAfter(selectFunction.find("option[value='25']"));
element23.insertAfter(selectFunction.find("option[value='28']"));
} }
var template = $('#tab-adjustments-templates .adjustments .adjustment');
var functionList = $(template).find('.functionSelection .function');
var functionListOptions = $(functionList).find('option').slice(0,availableFunctionCount);
functionList.empty().append(functionListOptions);
}; };

View file

@ -244,8 +244,9 @@ TABS.auxiliary.initialize = function (callback) {
} }
function update_ui() { function update_ui() {
for (var i = 0; i < AUX_CONFIG.length; i++) { let hasUsedMode = false;
var modeElement = $('#mode-' + i); for (let i = 0; i < AUX_CONFIG.length; i++) {
let modeElement = $('#mode-' + i);
if (modeElement.find(' .range').length == 0) { if (modeElement.find(' .range').length == 0) {
// if the mode is unused, skip it // if the mode is unused, skip it
modeElement.removeClass('off').removeClass('on'); modeElement.removeClass('off').removeClass('on');
@ -257,6 +258,15 @@ TABS.auxiliary.initialize = function (callback) {
} else { } else {
$('.mode .name').eq(i).data('modeElement').removeClass('on').addClass('off'); $('.mode .name').eq(i).data('modeElement').removeClass('on').addClass('off');
} }
hasUsedMode = true;
}
let hideUnused = hideUnusedModes && hasUsedMode;
for (let i = 0; i < AUX_CONFIG.length; i++) {
let modeElement = $('#mode-' + i);
if (modeElement.find(' .range').length == 0) {
modeElement.toggle(!hideUnused);
}
} }
auto_select_channel(RC.channels); auto_select_channel(RC.channels);
@ -306,6 +316,18 @@ TABS.auxiliary.initialize = function (callback) {
return fillPrevChannelsValues(); return fillPrevChannelsValues();
} }
let hideUnusedModes = false;
chrome.storage.local.get('hideUnusedModes', function (result) {
$("input#switch-toggle-unused")
.change(function() {
hideUnusedModes = $(this).prop("checked");
chrome.storage.local.set({ hideUnusedModes: hideUnusedModes });
update_ui();
})
.prop("checked", !!result.hideUnusedModes)
.change();
});
// update ui instantly on first load // update ui instantly on first load
update_ui(); update_ui();

View file

@ -60,14 +60,13 @@ TABS.cli.initialize = function (callback) {
var textarea = $('.tab-cli textarea'); var textarea = $('.tab-cli textarea');
$('.tab-cli .save').click(function() { $('.tab-cli .save').click(function() {
var prefix = 'cli'; var prefix = 'cli';
var suffix = 'txt'; var suffix = 'txt';
var filename = generateFilename(prefix, suffix); var filename = generateFilename(prefix, suffix);
var accepts = [{ var accepts = [{
extensions: [suffix], description: suffix.toUpperCase() + ' files', extensions: [suffix],
}]; }];
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename, accepts: accepts}, function(entry) { chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename, accepts: accepts}, function(entry) {
@ -87,9 +86,11 @@ TABS.cli.initialize = function (callback) {
}; };
writer.onwriteend = function () { writer.onwriteend = function () {
if (writer.length === 0) { if (self.outputHistory.length > 0 && writer.length === 0) {
writer.write(new Blob([self.outputHistory], {type: 'text/plain'})); writer.write(new Blob([self.outputHistory], {type: 'text/plain'}));
} else { } else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'CliSave', self.outputHistory.length);
console.log('write complete'); console.log('write complete');
} }
}; };

View file

@ -2,7 +2,8 @@
TABS.configuration = { TABS.configuration = {
DSHOT_PROTOCOL_MIN_VALUE: 5, DSHOT_PROTOCOL_MIN_VALUE: 5,
SHOW_OLD_BATTERY_CONFIG: false SHOW_OLD_BATTERY_CONFIG: false,
analyticsChanges: {},
}; };
TABS.configuration.initialize = function (callback, scrollPosition) { TABS.configuration.initialize = function (callback, scrollPosition) {
@ -10,6 +11,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
if (GUI.active_tab != 'configuration') { if (GUI.active_tab != 'configuration') {
GUI.active_tab = 'configuration'; GUI.active_tab = 'configuration';
GUI.configuration_loaded = true;
} }
if (semver.lt(CONFIG.apiVersion, "1.36.0")) { if (semver.lt(CONFIG.apiVersion, "1.36.0")) {
@ -194,6 +196,10 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
load_config(); load_config();
function process_html() { function process_html() {
var self = this;
self.analyticsChanges = {};
var mixer_list_e = $('select.mixerList'); var mixer_list_e = $('select.mixerList');
for (var selectIndex = 0; selectIndex < mixerList.length; selectIndex++) { for (var selectIndex = 0; selectIndex < mixerList.length; selectIndex++) {
mixerList.forEach(function (mixerEntry, mixerIndex) { mixerList.forEach(function (mixerEntry, mixerIndex) {
@ -224,7 +230,15 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
reverseMotorSwitch_e.prop('checked', MIXER_CONFIG.reverseMotorDir != 0).change(); reverseMotorSwitch_e.prop('checked', MIXER_CONFIG.reverseMotorDir != 0).change();
mixer_list_e.change(function () { mixer_list_e.change(function () {
MIXER_CONFIG.mixer = parseInt($(this).val()); var mixerValue = parseInt($(this).val());
var newValue;
if (mixerValue !== MIXER_CONFIG.mixer) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['Mixer'] = newValue;
MIXER_CONFIG.mixer = mixerValue;
refreshMixerPreview(); refreshMixerPreview();
}); });
@ -355,11 +369,31 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
esc_protocol_e.append('<option value="' + (i + 1) + '">'+ escprotocols[i] + '</option>'); esc_protocol_e.append('<option value="' + (i + 1) + '">'+ escprotocols[i] + '</option>');
} }
esc_protocol_e.val(PID_ADVANCED_CONFIG.fast_pwm_protocol + 1); $("input[id='unsyncedPWMSwitch']").change(function() {
if ($(this).is(':checked')) {
$('div.unsyncedpwmfreq').show();
} else {
$('div.unsyncedpwmfreq').hide();
}
});
$('input[id="unsyncedPWMSwitch"]').prop('checked', PID_ADVANCED_CONFIG.use_unsyncedPwm !== 0).change();
$('input[name="unsyncedpwmfreq"]').val(PID_ADVANCED_CONFIG.motor_pwm_rate);
$('input[name="digitalIdlePercent"]').val(PID_ADVANCED_CONFIG.digitalIdlePercent);
esc_protocol_e.val(PID_ADVANCED_CONFIG.fast_pwm_protocol + 1);
esc_protocol_e.change(function () { esc_protocol_e.change(function () {
var escProtocolValue = parseInt($(this).val()) - 1;
var newValue;
if (escProtocolValue !== PID_ADVANCED_CONFIG.fast_pwm_protocol) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['EscProtocol'] = newValue;
//hide not used setting for DSHOT protocol //hide not used setting for DSHOT protocol
if ($(this).val() - 1 >= self.DSHOT_PROTOCOL_MIN_VALUE) { if (escProtocolValue >= self.DSHOT_PROTOCOL_MIN_VALUE) {
$('div.minthrottle').hide(); $('div.minthrottle').hide();
$('div.maxthrottle').hide(); $('div.maxthrottle').hide();
$('div.mincommand').hide(); $('div.mincommand').hide();
@ -379,9 +413,6 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
} }
}).change(); }).change();
$('input[id="unsyncedPWMSwitch"]').prop('checked', PID_ADVANCED_CONFIG.use_unsyncedPwm !== 0);
$('input[name="unsyncedpwmfreq"]').val(PID_ADVANCED_CONFIG.motor_pwm_rate);
$('input[name="digitalIdlePercent"]').val(PID_ADVANCED_CONFIG.digitalIdlePercent);
// Gyro and PID update // Gyro and PID update
var gyroUse32kHz_e = $('input[id="gyroUse32kHz"]'); var gyroUse32kHz_e = $('input[id="gyroUse32kHz"]');
@ -611,7 +642,15 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
} }
serialRX_e.change(function () { serialRX_e.change(function () {
RX_CONFIG.serialrx_provider = parseInt($(this).val()); var serialRxValue = parseInt($(this).val());
var newValue;
if (serialRxValue !== RX_CONFIG.serialrx_provider) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['SerialRx'] = newValue;
RX_CONFIG.serialrx_provider = serialRxValue;
}); });
// select current serial RX type // select current serial RX type
@ -921,14 +960,6 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
checkUpdateCurrentControls(); checkUpdateCurrentControls();
} }
$("input[id='unsyncedPWMSwitch']").change(function() {
if ($(this).is(':checked')) {
$('div.unsyncedpwmfreq').show();
} else {
$('div.unsyncedpwmfreq').hide();
}
}).change();
$('a.save').click(function () { $('a.save').click(function () {
// gather data that doesn't have automatic change event bound // gather data that doesn't have automatic change event bound
BOARD_ALIGNMENT_CONFIG.roll = parseInt($('input[name="board_align_roll"]').val()); BOARD_ALIGNMENT_CONFIG.roll = parseInt($('input[name="board_align_roll"]').val());
@ -987,6 +1018,9 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
RX_CONFIG.fpvCamAngleDegrees = parseInt($('input[name="fpvCamAngleDegrees"]').val()); RX_CONFIG.fpvCamAngleDegrees = parseInt($('input[name="fpvCamAngleDegrees"]').val());
} }
analytics.sendChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges);
self.analyticsChanges = {};
function save_serial_config() { function save_serial_config() {
var next_callback = save_feature_config; var next_callback = save_feature_config;
MSP.send_message(MSPCodes.MSP_SET_CF_SERIAL_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_CF_SERIAL_CONFIG), false, next_callback); MSP.send_message(MSPCodes.MSP_SET_CF_SERIAL_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_CF_SERIAL_CONFIG), false, next_callback);

View file

@ -2,7 +2,8 @@
TABS.firmware_flasher = { TABS.firmware_flasher = {
releases: null, releases: null,
releaseChecker: new ReleaseChecker('firmware', 'https://api.github.com/repos/betaflight/betaflight/releases') releaseChecker: new ReleaseChecker('firmware', 'https://api.github.com/repos/betaflight/betaflight/releases'),
jenkinsLoader: new JenkinsLoader('https://ci.betaflight.tech')
}; };
TABS.firmware_flasher.initialize = function (callback) { TABS.firmware_flasher.initialize = function (callback) {
@ -28,7 +29,7 @@ TABS.firmware_flasher.initialize = function (callback) {
: "normal"); : "normal");
} }
$('#content').load("./tabs/firmware_flasher.html", function () { function onDocumentLoad() {
FirmwareCache.load(); FirmwareCache.load();
FirmwareCache.onPutToCache(onFirmwareCacheUpdate); FirmwareCache.onPutToCache(onFirmwareCacheUpdate);
FirmwareCache.onRemoveFromCache(onFirmwareCacheUpdate); FirmwareCache.onRemoveFromCache(onFirmwareCacheUpdate);
@ -49,16 +50,18 @@ TABS.firmware_flasher.initialize = function (callback) {
function process_hex(data, summary) { function process_hex(data, summary) {
intel_hex = data; intel_hex = data;
analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHECKSUM, objectHash.sha1(intel_hex));
parse_hex(intel_hex, function (data) { parse_hex(intel_hex, function (data) {
parsed_hex = data; parsed_hex = data;
if (parsed_hex) { if (parsed_hex) {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SIZE, parsed_hex.bytes_total);
if (!FirmwareCache.has(summary)) { if (!FirmwareCache.has(summary)) {
FirmwareCache.put(summary, intel_hex); FirmwareCache.put(summary, intel_hex);
} }
var url;
$('span.progressLabel').html('<a class="save_firmware" href="#" title="Save Firmware">Loaded Online Firmware: (' + parsed_hex.bytes_total + ' bytes)</a>'); $('span.progressLabel').html('<a class="save_firmware" href="#" title="Save Firmware">Loaded Online Firmware: (' + parsed_hex.bytes_total + ' bytes)</a>');
$('a.flash_firmware').removeClass('disabled'); $('a.flash_firmware').removeClass('disabled');
@ -243,24 +246,21 @@ TABS.firmware_flasher.initialize = function (callback) {
{ {
tag: 'firmwareFlasherOptionLabelBuildTypeReleaseCandidate', tag: 'firmwareFlasherOptionLabelBuildTypeReleaseCandidate',
loader: () => self.releaseChecker.loadReleaseData(releaseData => buildBoardOptions(releaseData, true)) loader: () => self.releaseChecker.loadReleaseData(releaseData => buildBoardOptions(releaseData, true))
},
{
tag: 'firmwareFlasherOptionLabelBuildTypeDevelopment',
loader: () => new JenkinsLoader('https://ci.betaflight.tech', 'Betaflight').loadBuilds(buildJenkinsBoardOptions)
},
{
tag: 'firmwareFlasherOptionLabelBuildTypeAKK3_3',
loader: () => new JenkinsLoader('https://ci.betaflight.tech', 'Betaflight Maintenance 3.3 (AKK - RDQ VTX Patch)').loadBuilds(buildJenkinsBoardOptions)
},
{
tag: 'firmwareFlasherOptionLabelBuildTypeAKK3_4',
loader: () => new JenkinsLoader('https://ci.betaflight.tech', 'Betaflight Maintenance 3.4 (AKK - RDQ VTX Patch)').loadBuilds(buildJenkinsBoardOptions)
} }
]; ];
var ciBuildsTypes = self.jenkinsLoader._jobs.map(job => {
return {
title: job.title,
loader: () => self.jenkinsLoader.loadBuilds(job.name, buildJenkinsBoardOptions)
};
})
buildTypes = buildTypes.concat(ciBuildsTypes);
var buildType_e = $('select[name="build_type"]'); var buildType_e = $('select[name="build_type"]');
buildTypes.forEach((build, index) => { buildTypes.forEach((build, index) => {
buildType_e.append($("<option value='{0}' selected>{1}</option>".format(index, i18n.getMessage(build.tag)))) buildType_e.append($("<option value='{0}' selected>{1}</option>".format(index, build.tag ? i18n.getMessage(build.tag) : build.title)))
}); });
showOrHideBuildTypeSelect(); showOrHideBuildTypeSelect();
@ -270,6 +270,8 @@ TABS.firmware_flasher.initialize = function (callback) {
i18n.localizePage(); i18n.localizePage();
buildType_e.change(function() { buildType_e.change(function() {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHANNEL, $(this).find('option:selected').text());
$("a.load_remote_file").addClass('disabled'); $("a.load_remote_file").addClass('disabled');
var build_type = $(this).val(); var build_type = $(this).val();
@ -325,7 +327,10 @@ TABS.firmware_flasher.initialize = function (callback) {
// UI Hooks // UI Hooks
$('a.load_file').click(function () { $('a.load_file').click(function () {
chrome.fileSystem.chooseEntry({type: 'openFile', accepts: [{extensions: ['hex']}]}, function (fileEntry) { analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHANNEL, undefined);
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SOURCE, 'file');
chrome.fileSystem.chooseEntry({type: 'openFile', accepts: [{description: 'HEX files', extensions: ['hex']}]}, function (fileEntry) {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message); console.error(chrome.runtime.lastError.message);
@ -339,6 +344,7 @@ TABS.firmware_flasher.initialize = function (callback) {
console.log('Loading file from: ' + path); console.log('Loading file from: ' + path);
fileEntry.file(function (file) { fileEntry.file(function (file) {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_NAME, file.name);
var reader = new FileReader(); var reader = new FileReader();
reader.onprogress = function (e) { reader.onprogress = function (e) {
@ -355,10 +361,14 @@ TABS.firmware_flasher.initialize = function (callback) {
intel_hex = e.target.result; intel_hex = e.target.result;
analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHECKSUM, objectHash.sha1(intel_hex));
parse_hex(intel_hex, function (data) { parse_hex(intel_hex, function (data) {
parsed_hex = data; parsed_hex = data;
if (parsed_hex) { if (parsed_hex) {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SIZE, parsed_hex.bytes_total);
$('a.flash_firmware').removeClass('disabled'); $('a.flash_firmware').removeClass('disabled');
$('span.progressLabel').text(i18n.getMessage('firmwareFlasherFirmwareLocalLoaded', parsed_hex.bytes_total)); $('span.progressLabel').text(i18n.getMessage('firmwareFlasherFirmwareLocalLoaded', parsed_hex.bytes_total));
@ -385,7 +395,10 @@ TABS.firmware_flasher.initialize = function (callback) {
let isCached = FirmwareCache.has(release); let isCached = FirmwareCache.has(release);
if (evt.target.value=="0" || isCached) { if (evt.target.value=="0" || isCached) {
if (isCached) { if (isCached) {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SOURCE, 'cache');
FirmwareCache.get(release, cached => { FirmwareCache.get(release, cached => {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_NAME, release.file);
console.info("Release found in cache: " + release.file); console.info("Release found in cache: " + release.file);
onLoadSuccess(cached.hexdata, release); onLoadSuccess(cached.hexdata, release);
}); });
@ -398,6 +411,7 @@ TABS.firmware_flasher.initialize = function (callback) {
}); });
$('a.load_remote_file').click(function (evt) { $('a.load_remote_file').click(function (evt) {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SOURCE, 'http');
if ($('select[name="firmware_version"]').val() == "0") { if ($('select[name="firmware_version"]').val() == "0") {
GUI.log(i18n.getMessage('firmwareFlasherNoFirmwareSelected')); GUI.log(i18n.getMessage('firmwareFlasherNoFirmwareSelected'));
@ -413,6 +427,7 @@ TABS.firmware_flasher.initialize = function (callback) {
var summary = $('select[name="firmware_version"] option:selected').data('summary'); var summary = $('select[name="firmware_version"] option:selected').data('summary');
if (summary) { // undefined while list is loading or while running offline if (summary) { // undefined while list is loading or while running offline
analytics.setFirmwareData(analytics.DATA.FIRMWARE_NAME, summary.file);
$("a.load_remote_file").text(i18n.getMessage('firmwareFlasherButtonDownloading')); $("a.load_remote_file").text(i18n.getMessage('firmwareFlasherButtonDownloading'));
$("a.load_remote_file").addClass('disabled'); $("a.load_remote_file").addClass('disabled');
$.get(summary.url, onLoadSuccess).fail(failed_to_load); $.get(summary.url, onLoadSuccess).fail(failed_to_load);
@ -427,14 +442,17 @@ TABS.firmware_flasher.initialize = function (callback) {
if (parsed_hex != false) { if (parsed_hex != false) {
var options = {}; var options = {};
var eraseAll = false;
if ($('input.erase_chip').is(':checked')) { if ($('input.erase_chip').is(':checked')) {
options.erase_chip = true; options.erase_chip = true;
eraseAll = true
} }
analytics.setFirmwareData(analytics.DATA.FIRMWARE_ERASE_ALL, eraseAll.toString());
if (String($('div#port-picker #port').val()) != 'DFU') { if (String($('div#port-picker #port').val()) != 'DFU') {
if (String($('div#port-picker #port').val()) != '0') { if (String($('div#port-picker #port').val()) != '0') {
var port = String($('div#port-picker #port').val()), var port = String($('div#port-picker #port').val()), baud;
baud;
baud = 115200; baud = 115200;
if ($('input.updating').is(':checked')) { if ($('input.updating').is(':checked')) {
@ -447,6 +465,7 @@ TABS.firmware_flasher.initialize = function (callback) {
baud = parseInt($('#flash_manual_baud_rate').val()); baud = parseInt($('#flash_manual_baud_rate').val());
} }
analytics.sendEvent(analytics.EVENT_CATEGORIES.FIRMWARE, 'Flashing');
STM32.connect(port, baud, parsed_hex, options); STM32.connect(port, baud, parsed_hex, options);
} else { } else {
@ -454,6 +473,8 @@ TABS.firmware_flasher.initialize = function (callback) {
GUI.log(i18n.getMessage('firmwareFlasherNoValidPort')); GUI.log(i18n.getMessage('firmwareFlasherNoValidPort'));
} }
} else { } else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FIRMWARE, 'Flashing');
STM32DFU.connect(usbDevices.STM32DFU, parsed_hex, options); STM32DFU.connect(usbDevices.STM32DFU, parsed_hex, options);
} }
} else { } else {
@ -465,7 +486,7 @@ TABS.firmware_flasher.initialize = function (callback) {
$(document).on('click', 'span.progressLabel a.save_firmware', function () { $(document).on('click', 'span.progressLabel a.save_firmware', function () {
var summary = $('select[name="firmware_version"] option:selected').data('summary'); var summary = $('select[name="firmware_version"] option:selected').data('summary');
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: summary.file, accepts: [{extensions: ['hex']}]}, function (fileEntry) { chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: summary.file, accepts: [{description: 'HEX files', extensions: ['hex']}]}, function (fileEntry) {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message); console.error(chrome.runtime.lastError.message);
return; return;
@ -626,6 +647,10 @@ TABS.firmware_flasher.initialize = function (callback) {
}); });
GUI.content_ready(callback); GUI.content_ready(callback);
}
self.jenkinsLoader.loadJobs('Firmware', () => {
$('#content').load("./tabs/firmware_flasher.html", onDocumentLoad);
}); });
}; };
@ -637,5 +662,7 @@ TABS.firmware_flasher.cleanup = function (callback) {
$(document).unbind('keypress'); $(document).unbind('keypress');
$(document).off('click', 'span.progressLabel a'); $(document).off('click', 'span.progressLabel a');
analytics.resetFirmwareData();
if (callback) callback(); if (callback) callback();
}; };

View file

@ -32,6 +32,26 @@ TABS.landing.initialize = function (callback) {
$(this).data('state2', state); $(this).data('state2', state);
}); });
// load privacy policy content
$('#privacy_policy .policy').load('./tabs/privacy_policy.html');
/** changelog trigger **/
$("#privacy_policy_toggle").on('click', function() {
var state = $(this).data('state2');
if (state) {
$("#privacy_policy").animate({right: -495}, 200, function () {
$("#content").removeClass('policy_open');
});
state = false;
} else {
$("#privacy_policy").animate({right: 0}, 200);
$("#content").addClass('policy_open');
state = true;
}
$(this).text(state ? i18n.getMessage('close') : i18n.getMessage('defaultPrivacyPolicyAction'));
$(this).data('state2', state);
});
GUI.content_ready(callback); GUI.content_ready(callback);
}); });

View file

@ -236,7 +236,7 @@ TABS.logging.initialize = function (callback) {
var filename = generateFilename(prefix, suffix); var filename = generateFilename(prefix, suffix);
var accepts = [{ var accepts = [{
extensions: [suffix], description: suffix.toUpperCase() + ' files', extensions: [suffix],
}]; }];
// create or load the file // create or load the file

View file

@ -45,7 +45,11 @@ TABS.motors.initialize = function (callback) {
} }
function load_motor_data() { function load_motor_data() {
MSP.send_message(MSPCodes.MSP_MOTOR, false, false, load_html); MSP.send_message(MSPCodes.MSP_MOTOR, false, false, load_mixer_config);
}
function load_mixer_config() {
MSP.send_message(MSPCodes.MSP_MIXER_CONFIG, false, false, load_html);
} }
function load_html() { function load_html() {

View file

@ -146,6 +146,22 @@ TABS.onboard_logging.initialize = function (callback) {
} }
}).change(); }).change();
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
if (SDCARD.supported || DATAFLASH.supported) {
$(".tab-onboard_logging")
.toggleClass("msc-supported", true);
$('a.onboardLoggingRebootMsc').click(function () {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'RebootMsc');
var buffer = [];
buffer.push(2);
MSP.send_message(MSPCodes.MSP_SET_REBOOT, buffer, false);
});
}
}
update_html(); update_html();
GUI.content_ready(callback); GUI.content_ready(callback);
@ -284,6 +300,8 @@ TABS.onboard_logging.initialize = function (callback) {
} }
function update_html() { function update_html() {
var dataflashPresent = DATAFLASH.totalSize > 0;
update_bar_width($(".tab-onboard_logging .dataflash-used"), DATAFLASH.usedSize, DATAFLASH.totalSize, i18n.getMessage('dataflashUsedSpace'), false); update_bar_width($(".tab-onboard_logging .dataflash-used"), DATAFLASH.usedSize, DATAFLASH.totalSize, i18n.getMessage('dataflashUsedSpace'), false);
update_bar_width($(".tab-onboard_logging .dataflash-free"), DATAFLASH.totalSize - DATAFLASH.usedSize, DATAFLASH.totalSize, i18n.getMessage('dataflashFreeSpace'), false); update_bar_width($(".tab-onboard_logging .dataflash-free"), DATAFLASH.totalSize - DATAFLASH.usedSize, DATAFLASH.totalSize, i18n.getMessage('dataflashFreeSpace'), false);
@ -297,26 +315,50 @@ TABS.onboard_logging.initialize = function (callback) {
.toggleClass("sdcard-initializing", SDCARD.state === MSP.SDCARD_STATE_CARD_INIT || SDCARD.state === MSP.SDCARD_STATE_FS_INIT) .toggleClass("sdcard-initializing", SDCARD.state === MSP.SDCARD_STATE_CARD_INIT || SDCARD.state === MSP.SDCARD_STATE_FS_INIT)
.toggleClass("sdcard-ready", SDCARD.state === MSP.SDCARD_STATE_READY); .toggleClass("sdcard-ready", SDCARD.state === MSP.SDCARD_STATE_READY);
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
var mscIsReady = dataflashPresent || (SDCARD.state === MSP.SDCARD_STATE_READY);
$(".tab-onboard_logging")
.toggleClass("msc-not-ready", !mscIsReady);
if (!mscIsReady) {
$('a.onboardLoggingRebootMsc').addClass('disabled');
} else {
$('a.onboardLoggingRebootMsc').removeClass('disabled');
}
}
var loggingStatus
switch (SDCARD.state) { switch (SDCARD.state) {
case MSP.SDCARD_STATE_NOT_PRESENT: case MSP.SDCARD_STATE_NOT_PRESENT:
$(".sdcard-status").text(i18n.getMessage('sdcardStatusNoCard')); $(".sdcard-status").text(i18n.getMessage('sdcardStatusNoCard'));
loggingStatus = 'SdCard: NotPresent';
break; break;
case MSP.SDCARD_STATE_FATAL: case MSP.SDCARD_STATE_FATAL:
$(".sdcard-status").html(i18n.getMessage('sdcardStatusReboot')); $(".sdcard-status").html(i18n.getMessage('sdcardStatusReboot'));
loggingStatus = 'SdCard: Error';
break; break;
case MSP.SDCARD_STATE_READY: case MSP.SDCARD_STATE_READY:
$(".sdcard-status").text(i18n.getMessage('sdcardStatusReady')); $(".sdcard-status").text(i18n.getMessage('sdcardStatusReady'));
loggingStatus = 'SdCard: Ready';
break; break;
case MSP.SDCARD_STATE_CARD_INIT: case MSP.SDCARD_STATE_CARD_INIT:
$(".sdcard-status").text(i18n.getMessage('sdcardStatusStarting')); $(".sdcard-status").text(i18n.getMessage('sdcardStatusStarting'));
loggingStatus = 'SdCard: Init';
break; break;
case MSP.SDCARD_STATE_FS_INIT: case MSP.SDCARD_STATE_FS_INIT:
$(".sdcard-status").text(i18n.getMessage('sdcardStatusFileSystem')); $(".sdcard-status").text(i18n.getMessage('sdcardStatusFileSystem'));
loggingStatus = 'SdCard: FsInit';
break; break;
default: default:
$(".sdcard-status").text(i18n.getMessage('sdcardStatusUnknown',[SDCARD.state])); $(".sdcard-status").text(i18n.getMessage('sdcardStatusUnknown',[SDCARD.state]));
} }
if (dataflashPresent && SDCARD.state === MSP.SDCARD_STATE_NOT_PRESENT) {
loggingStatus = 'Dataflash';
analytics.setFlightControllerData(analytics.DATA.LOG_SIZE, DATAFLASH.usedSize);
}
analytics.setFlightControllerData(analytics.DATA.LOGGING_STATUS, loggingStatus);
if (SDCARD.supported && !sdcardTimer) { if (SDCARD.supported && !sdcardTimer) {
// Poll for changes in SD card status // Poll for changes in SD card status
sdcardTimer = setTimeout(function() { sdcardTimer = setTimeout(function() {
@ -348,6 +390,8 @@ TABS.onboard_logging.initialize = function (callback) {
} }
function mark_saving_dialog_done(startTime, totalBytes, totalBytesCompressed) { function mark_saving_dialog_done(startTime, totalBytes, totalBytesCompressed) {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'SaveDataflash');
var totalTime = (new Date().getTime() - startTime) / 1000; var totalTime = (new Date().getTime() - startTime) / 1000;
console.log('Received ' + totalBytes + ' bytes in ' + totalTime.toFixed(2) + 's (' console.log('Received ' + totalBytes + ' bytes in ' + totalTime.toFixed(2) + 's ('
+ (totalBytes / totalTime / 1024).toFixed(2) + 'kB / s) with block size ' + self.blockSize + '.'); + (totalBytes / totalTime / 1024).toFixed(2) + 'kB / s) with block size ' + self.blockSize + '.');
@ -445,7 +489,7 @@ TABS.onboard_logging.initialize = function (callback) {
var filename = generateFilename(prefix, suffix); var filename = generateFilename(prefix, suffix);
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename, chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename,
accepts: [{extensions: [suffix]}]}, function(fileEntry) { accepts: [{description: suffix.toUpperCase() + ' files', extensions: [suffix]}]}, function(fileEntry) {
var error = chrome.runtime.lastError; var error = chrome.runtime.lastError;
if (error) { if (error) {
@ -513,6 +557,9 @@ TABS.onboard_logging.initialize = function (callback) {
}; };
TABS.onboard_logging.cleanup = function (callback) { TABS.onboard_logging.cleanup = function (callback) {
analytics.setFlightControllerData(analytics.DATA.LOGGING_STATUS, undefined);
analytics.setFlightControllerData(analytics.DATA.LOG_SIZE, undefined);
if (sdcardTimer) { if (sdcardTimer) {
clearTimeout(sdcardTimer); clearTimeout(sdcardTimer);
sdcardTimer = false; sdcardTimer = false;

View file

@ -129,7 +129,7 @@ FONT.parseMCMFontFile = function(data) {
FONT.openFontFile = function($preview) { FONT.openFontFile = function($preview) {
return new Promise(function(resolve) { return new Promise(function(resolve) {
chrome.fileSystem.chooseEntry({type: 'openFile', accepts: [{extensions: ['mcm']}]}, function (fileEntry) { chrome.fileSystem.chooseEntry({type: 'openFile', accepts: [{description: 'MCM files', extensions: ['mcm']}]}, function (fileEntry) {
FONT.data.loaded_font_file = fileEntry.name; FONT.data.loaded_font_file = fileEntry.name;
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message); console.error(chrome.runtime.lastError.message);
@ -342,7 +342,7 @@ OSD.constants = {
default_position: -9, default_position: -9,
draw_order: 110, draw_order: 110,
positionable: true, positionable: true,
preview: FONT.symbol(SYM.THR) + FONT.symbol(SYM.THR1) + '69' preview: FONT.symbol(SYM.THR) + FONT.symbol(SYM.THR1) + ' 69'
}, },
CPU_LOAD: { CPU_LOAD: {
name: 'CPU_LOAD', name: 'CPU_LOAD',
@ -913,7 +913,8 @@ OSD.constants = {
{ file: "extra_large", name: "Extra Large" }, { file: "extra_large", name: "Extra Large" },
{ file: "betaflight", name: "Betaflight" }, { file: "betaflight", name: "Betaflight" },
{ file: "digital", name: "Digital" }, { file: "digital", name: "Digital" },
{ file: "clarity", name: "Clarity" } { file: "clarity", name: "Clarity" },
{ file: "vision", name: "Vision" }
] ]
}; };
@ -1470,17 +1471,18 @@ TABS.osd.initialize = function (callback) {
} }
$('#content').load("./tabs/osd.html", function () { $('#content').load("./tabs/osd.html", function () {
// Generate font type select element
// Generate font type buttons var fontselect = $('.fontpresets');
var fontbuttons = $('.fontbuttons');
OSD.constants.FONT_TYPES.forEach(function(e, i) { OSD.constants.FONT_TYPES.forEach(function(e, i) {
var button = $('<button>', { var option = $('<option>', {
"data-font-file": e.file, "data-font-file": e.file,
value: e.file,
text: e.name text: e.name
}); });
fontbuttons.append($(button)); fontselect.append($(option));
}); });
var fontbuttons = $('.fontpresets_wrapper');
fontbuttons.append($('<button>', { class: "load_font_file", i18n: "osdSetupOpenFont" })); fontbuttons.append($('<button>', { class: "load_font_file", i18n: "osdSetupOpenFont" }));
// must invoke before i18n.localizePage() since it adds translation keys for expected logo size // must invoke before i18n.localizePage() since it adds translation keys for expected logo size
@ -1974,12 +1976,10 @@ TABS.osd.initialize = function (callback) {
// init structs once, also clears current font // init structs once, also clears current font
FONT.initData(); FONT.initData();
var $fontPicker = $('.fontbuttons button'); var $fontpresets = $('.fontpresets')
$fontPicker.click(function(e) { $fontpresets.change(function(e) {
if (!$(this).data('font-file')) { return; } var $font = $('.fontpresets option:selected');
$fontPicker.removeClass('active'); $.get('./resources/osd/' + $font.data('font-file') + '.mcm', function(data) {
$(this).addClass('active');
$.get('./resources/osd/' + $(this).data('font-file') + '.mcm', function(data) {
FONT.parseMCMFontFile(data); FONT.parseMCMFontFile(data);
FONT.preview($preview); FONT.preview($preview);
LogoManager.drawPreview(); LogoManager.drawPreview();
@ -1988,10 +1988,15 @@ TABS.osd.initialize = function (callback) {
}); });
// load the first font when we change tabs // load the first font when we change tabs
$fontPicker.first().click(); var $font = $('.fontpresets option:selected');
$.get('./resources/osd/' + $font.data('font-file') + '.mcm', function(data) {
FONT.parseMCMFontFile(data);
FONT.preview($preview);
LogoManager.drawPreview();
updateOsdView();
});
$('button.load_font_file').click(function() { $('button.load_font_file').click(function() {
$fontPicker.removeClass('active');
FONT.openFontFile().then(function() { FONT.openFontFile().then(function() {
FONT.preview($preview); FONT.preview($preview);
LogoManager.drawPreview(); LogoManager.drawPreview();
@ -2047,7 +2052,7 @@ TABS.osd.initialize = function (callback) {
}) })
$(document).on('click', 'span.progressLabel a.save_font', function () { $(document).on('click', 'span.progressLabel a.save_font', function () {
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: 'baseflight', accepts: [{extensions: ['mcm']}]}, function (fileEntry) { chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: 'baseflight', accepts: [{description: 'MCM files', extensions: ['mcm']}]}, function (fileEntry) {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message); console.error(chrome.runtime.lastError.message);
return; return;

425
src/js/tabs/pid_tuning.js Executable file → Normal file
View file

@ -39,9 +39,13 @@ TABS.pid_tuning.initialize = function (callback) {
}).then(function() { }).then(function() {
return MSP.promise(MSPCodes.MSP_RC_DEADBAND); return MSP.promise(MSPCodes.MSP_RC_DEADBAND);
}).then(function() { }).then(function() {
$('#content').load("./tabs/pid_tuning.html", process_html); MSP.send_message(MSPCodes.MSP_MIXER_CONFIG, false, false, load_html);
}); });
function load_html() {
$('#content').load("./tabs/pid_tuning.html", process_html);
}
function pid_and_rc_to_form() { function pid_and_rc_to_form() {
self.setProfile(); self.setProfile();
if (semver.gte(CONFIG.apiVersion, "1.20.0")) { if (semver.gte(CONFIG.apiVersion, "1.20.0")) {
@ -49,142 +53,19 @@ TABS.pid_tuning.initialize = function (callback) {
} }
// Fill in the data from PIDs array // Fill in the data from PIDs array
var i = 0;
$('.pid_tuning .ROLL input').each(function () {
switch (i) {
case 0:
$(this).val(PIDs[0][i++]);
break;
case 1:
$(this).val(PIDs[0][i++]);
break;
case 2:
$(this).val(PIDs[0][i++]);
break;
}
});
i = 0; // For each pid name
$('.pid_tuning .PITCH input').each(function () { PID_names.forEach(function(elementPid, indexPid) {
switch (i) {
case 0:
$(this).val(PIDs[1][i++]);
break;
case 1:
$(this).val(PIDs[1][i++]);
break;
case 2:
$(this).val(PIDs[1][i++]);
break;
}
});
i = 0; // Look into the PID table to a row with the name of the pid
$('.pid_tuning .YAW input').each(function () { var searchRow = $('.pid_tuning .' + elementPid + ' input');
switch (i) {
case 0:
$(this).val(PIDs[2][i++]);
break;
case 1:
$(this).val(PIDs[2][i++]);
break;
}
});
$('.pid_tuning .YAW_JUMP_PREVENTION input').each(function () {
switch (i) {
case 2:
$(this).val(PIDs[2][i++]);
break;
}
});
i = 0; // Assign each value
$('.pid_tuning .ALT input').each(function () { searchRow.each(function (indexInput) {
switch (i) { if (PIDs[indexPid][indexInput] !== undefined) {
case 0: $(this).val(PIDs[indexPid][indexInput]);
$(this).val(PIDs[3][i++]);
break;
case 1:
$(this).val(PIDs[3][i++]);
break;
case 2:
$(this).val(PIDs[3][i++]);
break;
} }
}); });
i = 0;
$('.pid_tuning .Pos input').each(function () {
$(this).val(PIDs[4][i++]);
});
i = 0;
$('.pid_tuning .PosR input').each(function () {
switch (i) {
case 0:
$(this).val(PIDs[5][i++]);
break;
case 1:
$(this).val(PIDs[5][i++]);
break;
case 2:
$(this).val(PIDs[5][i++]);
break;
}
});
i = 0;
$('.pid_tuning .NavR input').each(function () {
switch (i) {
case 0:
$(this).val(PIDs[6][i++]);
break;
case 1:
$(this).val(PIDs[6][i++]);
break;
case 2:
$(this).val(PIDs[6][i++]);
break;
}
});
i = 0;
$('.pid_tuning .ANGLE input').each(function () {
switch (i) {
case 0:
$(this).val(PIDs[7][i++]);
break;
}
});
$('.pid_tuning .HORIZON input').each(function () {
switch (i) {
case 1:
$(this).val(PIDs[7][i++]);
break;
case 2:
$(this).val(PIDs[7][i++]);
break;
}
});
i = 0;
$('.pid_tuning .MAG input').each(function () {
$(this).val(PIDs[8][i++]);
});
i = 0;
$('.pid_tuning .Vario input').each(function () {
switch (i) {
case 0:
$(this).val(PIDs[9][i++]);
break;
case 1:
$(this).val(PIDs[9][i++]);
break;
case 2:
$(this).val(PIDs[9][i++]);
break;
}
}); });
// Fill in data from RC_tuning object // Fill in data from RC_tuning object
@ -299,6 +180,129 @@ TABS.pid_tuning.initialize = function (callback) {
$('.dtermLowpass2').hide(); $('.dtermLowpass2').hide();
} }
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
// I Term Rotation
$('input[id="itermrotation"]').prop('checked', ADVANCED_TUNING.itermRotation !== 0);
// Smart Feed Forward
$('input[id="smartfeedforward"]').prop('checked', ADVANCED_TUNING.smartFeedforward !== 0);
// I Term Relax
var itermRelaxCheck = $('input[id="itermrelax"]');
itermRelaxCheck.prop('checked', ADVANCED_TUNING.itermRelax !== 0);
$('select[id="itermrelaxAxes"]').val(ADVANCED_TUNING.itermRelax > 0 ? ADVANCED_TUNING.itermRelax : 1);
$('select[id="itermrelaxType"]').val(ADVANCED_TUNING.itermRelaxType);
itermRelaxCheck.change(function() {
var checked = $(this).is(':checked');
if (checked) {
$('.itermrelax .suboption').show();
} else {
$('.itermrelax .suboption').hide();
}
});
itermRelaxCheck.change();
// Absolute Control
var absoluteControlGainNumberElement = $('input[name="absoluteControlGain-number"]');
var absoluteControlGainRangeElement = $('input[name="absoluteControlGain-range"]');
absoluteControlGainNumberElement.change(function () {
absoluteControlGainRangeElement.val($(this).val());
});
absoluteControlGainRangeElement.change(function () {
absoluteControlGainNumberElement.val($(this).val());
});
absoluteControlGainNumberElement.val(ADVANCED_TUNING.absoluteControlGain).change();
// Throttle Boost
var throttleBoostNumberElement = $('input[name="throttleBoost-number"]');
var throttleBoostRangeElement = $('input[name="throttleBoost-range"]');
throttleBoostNumberElement.change(function () {
throttleBoostRangeElement.val($(this).val());
});
throttleBoostRangeElement.change(function () {
throttleBoostNumberElement.val($(this).val());
});
throttleBoostNumberElement.val(ADVANCED_TUNING.throttleBoost).change();
// Acro Trainer
var acroTrainerAngleLimitNumberElement = $('input[name="acroTrainerAngleLimit-number"]');
var acroTrainerAngleLimitRangeElement = $('input[name="acroTrainerAngleLimit-range"]');
acroTrainerAngleLimitNumberElement.change(function () {
acroTrainerAngleLimitRangeElement.val($(this).val());
});
acroTrainerAngleLimitRangeElement.change(function () {
acroTrainerAngleLimitNumberElement.val($(this).val());
});
acroTrainerAngleLimitNumberElement.val(ADVANCED_TUNING.acroTrainerAngleLimit).change();
// Yaw D
$('.pid_tuning .YAW input[name="d"]').val(PIDs[2][2]); // PID Yaw D
// Feedforward
$('.pid_tuning .ROLL input[name="f"]').val(ADVANCED_TUNING.feedforwardRoll);
$('.pid_tuning .PITCH input[name="f"]').val(ADVANCED_TUNING.feedforwardPitch);
$('.pid_tuning .YAW input[name="f"]').val(ADVANCED_TUNING.feedforwardYaw);
var feedforwardTransitionNumberElement = $('input[name="feedforwardTransition-number"]');
var feedforwardTransitionRangeElement = $('input[name="feedforwardTransition-range"]');
feedforwardTransitionNumberElement.val(ADVANCED_TUNING.feedforwardTransition / 100);
feedforwardTransitionRangeElement.val(ADVANCED_TUNING.feedforwardTransition / 100);
feedforwardTransitionNumberElement.change(function () {
feedforwardTransitionRangeElement.val($(this).val());
});
feedforwardTransitionRangeElement.change(function () {
feedforwardTransitionNumberElement.val($(this).val());
});
$('.helpicon[i18n_title="pidTuningPidTuningTip"]').hide();
// AntiGravity Mode
var antiGravityModeSelect = $('.antigravity select[id="antiGravityMode"]');
antiGravityModeSelect.change(function () {
var antiGravityModeValue = $('.antigravity select[id="antiGravityMode"]').val();
// Smooth
if (antiGravityModeValue == 0) {
$('.antigravity table th:nth-child(3)').hide();
$('.antigravity table td:nth-child(3)').hide();
} else {
$('.antigravity table th:nth-child(3)').show();
$('.antigravity table td:nth-child(3)').show();
}
});
antiGravityModeSelect.val(ADVANCED_TUNING.antiGravityMode).change();
} else {
$('.itermrotation').hide();
$('.smartfeedforward').hide();
$('.itermrelax').hide();
$('.absoluteControlGain').hide();
$('.throttleBoost').hide();
$('.acroTrainerAngleLimit').hide();
$('.pid_tuning .YAW input[name="d"]').hide();
// Feedforward column
$('#pid_main tr :nth-child(5)').hide();
$('#pid_main .pid_titlebar2 th').attr("colspan", 8);
$('.helpicon[i18n_title="pidTuningPidTuningTipFeedforward"]').hide();
$('#pid-tuning .feedforwardTransition').hide();
$('.antigravity table th:first-child').hide();
$('.antigravity table td:first-child').hide();
}
$('input[id="gyroNotch1Enabled"]').change(function() { $('input[id="gyroNotch1Enabled"]').change(function() {
var checked = $(this).is(':checked'); var checked = $(this).is(':checked');
var hz = FILTER_CONFIG.gyro_notch_hz > 0 ? FILTER_CONFIG.gyro_notch_hz : DEFAULT.gyro_notch_hz; var hz = FILTER_CONFIG.gyro_notch_hz > 0 ? FILTER_CONFIG.gyro_notch_hz : DEFAULT.gyro_notch_hz;
@ -409,60 +413,19 @@ TABS.pid_tuning.initialize = function (callback) {
function form_to_pid_and_rc() { function form_to_pid_and_rc() {
// Fill in the data from PIDs array // Fill in the data from PIDs array
// Catch all the changes and stuff the inside PIDs array // Catch all the changes and stuff the inside PIDs array
var i = 0;
$('table.pid_tuning tr.ROLL .pid_data input').each(function () {
PIDs[0][i++] = parseFloat($(this).val());
});
i = 0; // For each pid name
$('table.pid_tuning tr.PITCH .pid_data input').each(function () { PID_names.forEach(function(elementPid, indexPid) {
PIDs[1][i++] = parseFloat($(this).val());
});
i = 0; // Look into the PID table to a row with the name of the pid
$('table.pid_tuning tr.YAW .pid_data input').each(function () { var searchRow = $('.pid_tuning .' + elementPid + ' input');
PIDs[2][i++] = parseFloat($(this).val());
});
$('table.pid_tuning tr.YAW_JUMP_PREVENTION .pid_data input').each(function () {
PIDs[2][i++] = parseFloat($(this).val());
});
i = 0; // Assign each value
$('table.pid_tuning tr.ALT input').each(function () { searchRow.each(function (indexInput) {
PIDs[3][i++] = parseFloat($(this).val()); if ($(this).val()) {
PIDs[indexPid][indexInput] = parseFloat($(this).val());
}
}); });
i = 0;
$('table.pid_tuning tr.Vario input').each(function () {
PIDs[9][i++] = parseFloat($(this).val());
});
i = 0;
$('table.pid_tuning tr.Pos input').each(function () {
PIDs[4][i++] = parseFloat($(this).val());
});
i = 0;
$('table.pid_tuning tr.PosR input').each(function () {
PIDs[5][i++] = parseFloat($(this).val());
});
i = 0;
$('table.pid_tuning tr.NavR input').each(function () {
PIDs[6][i++] = parseFloat($(this).val());
});
i = 0;
$('div.pid_tuning tr.ANGLE input').each(function () {
PIDs[7][i++] = parseFloat($(this).val());
});
$('div.pid_tuning tr.HORIZON input').each(function () {
PIDs[7][i++] = parseFloat($(this).val());
});
i = 0;
$('div.pid_tuning tr.MAG input').each(function () {
PIDs[8][i++] = parseFloat($(this).val());
}); });
// catch RC_tuning changes // catch RC_tuning changes
@ -527,40 +490,80 @@ TABS.pid_tuning.initialize = function (callback) {
FILTER_CONFIG.gyro_lowpass2_type = parseInt($('.pid_filter select[name="gyroLowpass2Type"]').val()); FILTER_CONFIG.gyro_lowpass2_type = parseInt($('.pid_filter select[name="gyroLowpass2Type"]').val());
FILTER_CONFIG.dterm_lowpass2_hz = parseInt($('.pid_filter input[name="dtermLowpass2Frequency"]').val()); FILTER_CONFIG.dterm_lowpass2_hz = parseInt($('.pid_filter input[name="dtermLowpass2Frequency"]').val());
} }
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
ADVANCED_TUNING.itermRotation = $('input[id="itermrotation"]').is(':checked') ? 1 : 0;
ADVANCED_TUNING.smartFeedforward = $('input[id="smartfeedforward"]').is(':checked') ? 1 : 0;
ADVANCED_TUNING.itermRelax = $('input[id="itermrelax"]').is(':checked') ? $('select[id="itermrelaxAxes"]').val() : 0;
ADVANCED_TUNING.itermRelaxType = $('input[id="itermrelax"]').is(':checked') ? $('select[id="itermrelaxType"]').val() : 0;
ADVANCED_TUNING.absoluteControlGain = $('input[name="absoluteControlGain-number"]').val();
ADVANCED_TUNING.throttleBoost = $('input[name="throttleBoost-number"]').val();
ADVANCED_TUNING.acroTrainerAngleLimit = $('input[name="acroTrainerAngleLimit-number"]').val();
ADVANCED_TUNING.feedforwardRoll = parseInt($('.pid_tuning .ROLL input[name="f"]').val());
ADVANCED_TUNING.feedforwardPitch = parseInt($('.pid_tuning .PITCH input[name="f"]').val());
ADVANCED_TUNING.feedforwardYaw = parseInt($('.pid_tuning .YAW input[name="f"]').val());
ADVANCED_TUNING.feedforwardTransition = parseInt($('input[name="feedforwardTransition-number"]').val() * 100);
ADVANCED_TUNING.antiGravityMode = $('select[id="antiGravityMode"]').val();
}
} }
function showAllPids() { function showAllPids() {
$('.tab-pid_tuning .pid_tuning').show();
// Hide all optional elements
$('.pid_optional tr').hide(); // Hide all rows
$('.pid_optional table').hide(); // Hide tables
$('.pid_optional').hide(); // Hide general div
// Only show rows supported by the firmware
PID_names.forEach(function(elementPid) {
// Show rows for the PID
$('.pid_tuning .' + elementPid).show();
// Show titles and other elements needed by the PID
$('.needed_by_' + elementPid).show();
});
// Special case
if (semver.lt(CONFIG.apiVersion, "1.24.0")) {
$('#pid_sensitivity').hide();
}
} }
function hideUnusedPids() { function hideUnusedPids() {
$('.tab-pid_tuning .pid_tuning').hide();
$('#pid_main').show(); if (!have_sensor(CONFIG.activeSensors, 'acc')) {
$('#pid_accel').hide();
if (have_sensor(CONFIG.activeSensors, 'acc')) {
$('#pid_accel').show();
$('#pid_level').show();
$('#pid_sensitivity').show();
} }
var showTitle = false; var hideSensorPid = function(element, sensorReady) {
if (have_sensor(CONFIG.activeSensors, 'baro') || var isVisible = element.is(":visible");
have_sensor(CONFIG.activeSensors, 'sonar')) { if (!isVisible || !sensorReady) {
$('#pid_baro').show(); element.hide();
showTitle = true; isVisible = false;
}
if (have_sensor(CONFIG.activeSensors, 'mag')) {
$('#pid_mag').show();
showTitle = true;
}
if (FEATURE_CONFIG.features.isEnabled('GPS')) {
$('#pid_gps').show();
showTitle = true;
} }
if (showTitle) { return isVisible;
$('#pid_optional').show(); }
var isVisibleBaroMagGps = false;
isVisibleBaroMagGps |= hideSensorPid($('#pid_baro'), have_sensor(CONFIG.activeSensors, 'baro') || have_sensor(CONFIG.activeSensors, 'sonar'));
isVisibleBaroMagGps |= hideSensorPid($('#pid_mag'), have_sensor(CONFIG.activeSensors, 'mag'));
isVisibleBaroMagGps |= hideSensorPid($('#pid_gps'), have_sensor(CONFIG.activeSensors, 'GPS'));
if (!isVisibleBaroMagGps) {
$('#pid_baro_mag_gps').hide();
} }
} }
@ -742,6 +745,7 @@ TABS.pid_tuning.initialize = function (callback) {
} }
} }
showAllPids();
updatePidDisplay(); updatePidDisplay();
showAllButton.on('click', function(){ showAllButton.on('click', function(){
@ -850,10 +854,14 @@ TABS.pid_tuning.initialize = function (callback) {
$('.tab-pid_tuning .note').hide(); $('.tab-pid_tuning .note').hide();
} }
// Add a name to each row of PIDs if empty
$('.pid_tuning tr').each(function(){ $('.pid_tuning tr').each(function(){
for(i = 0; i < PID_names.length; i++) { for(i = 0; i < PID_names.length; i++) {
if($(this).hasClass(PID_names[i])) { if($(this).hasClass(PID_names[i])) {
$(this).find('td:first').text(PID_names[i]); var firstColumn = $(this).find('td:first');
if (!firstColumn.text()) {
firstColumn.text(PID_names[i]);
}
} }
} }
}); });
@ -1372,8 +1380,13 @@ TABS.pid_tuning.updatePidControllerParameters = function () {
} else { } else {
$('.pid_tuning .YAW_JUMP_PREVENTION').hide(); $('.pid_tuning .YAW_JUMP_PREVENTION').hide();
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
$('#pid-tuning .dtermSetpointTransition').hide();
$('#pid-tuning .dtermSetpoint').hide();
} else {
$('#pid-tuning .dtermSetpointTransition').show(); $('#pid-tuning .dtermSetpointTransition').show();
$('#pid-tuning .dtermSetpoint').show(); $('#pid-tuning .dtermSetpoint').show();
}
$('#pid-tuning .delta').hide(); $('#pid-tuning .delta').hide();
} }

View file

@ -1,6 +1,8 @@
'use strict'; 'use strict';
TABS.ports = {}; TABS.ports = {
analyticsChanges: {},
};
TABS.ports.initialize = function (callback, scrollPosition) { TABS.ports.initialize = function (callback, scrollPosition) {
var self = this; var self = this;
@ -117,6 +119,9 @@ TABS.ports.initialize = function (callback, scrollPosition) {
} }
function update_ui() { function update_ui() {
self.analyticsChanges = {};
self.foo.bar = 1;
if (semver.lt(CONFIG.apiVersion, "1.6.0")) { if (semver.lt(CONFIG.apiVersion, "1.6.0")) {
@ -240,6 +245,19 @@ TABS.ports.initialize = function (callback, scrollPosition) {
if (serialPort.functions.indexOf(functionName) >= 0) { if (serialPort.functions.indexOf(functionName) >= 0) {
select_e.val(functionName); select_e.val(functionName);
} }
if (column === 'telemetry') {
var initialValue = functionName;
select_e.change(function () {
var telemetryValue = $(this).val();
var newValue;
if (telemetryValue !== initialValue) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['Telemetry'] = newValue;
});
}
} }
} }
} }
@ -249,6 +267,7 @@ TABS.ports.initialize = function (callback, scrollPosition) {
} }
function on_tab_loaded_handler() { function on_tab_loaded_handler() {
var self = this;
i18n.localizePage(); i18n.localizePage();
@ -265,6 +284,9 @@ TABS.ports.initialize = function (callback, scrollPosition) {
} }
function on_save_handler() { function on_save_handler() {
analytics.sendChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges);
self.analyticsChanges = {};
// update configuration based on current ui state // update configuration based on current ui state
SERIAL_CONFIG.ports = []; SERIAL_CONFIG.ports = [];

View file

@ -40,7 +40,7 @@ TABS.receiver.initialize = function (callback) {
} }
function load_rx_config() { function load_rx_config() {
var next_callback = load_html; var next_callback = load_mixer_config;
if (semver.gte(CONFIG.apiVersion, "1.20.0")) { if (semver.gte(CONFIG.apiVersion, "1.20.0")) {
MSP.send_message(MSPCodes.MSP_RX_CONFIG, false, false, next_callback); MSP.send_message(MSPCodes.MSP_RX_CONFIG, false, false, next_callback);
} else { } else {
@ -48,6 +48,10 @@ TABS.receiver.initialize = function (callback) {
} }
} }
function load_mixer_config() {
MSP.send_message(MSPCodes.MSP_MIXER_CONFIG, false, false, load_html);
}
function load_html() { function load_html() {
$('#content').load("./tabs/receiver.html", process_html); $('#content').load("./tabs/receiver.html", process_html);
} }

View file

@ -59,9 +59,24 @@ TABS.setup.initialize = function (callback) {
self.initializeInstruments(); self.initializeInstruments();
$('#arming-disable-flag-row').attr('title', i18n.getMessage('initialSetupArmingDisableFlagsTooltip')); $('#arming-disable-flag-row').attr('title', i18n.getMessage('initialSetupArmingDisableFlagsTooltip'));
if (semver.gte(CONFIG.apiVersion, "1.40.0")) {
if (isExpertModeEnabled()) {
$('.initialSetupRebootBootloader').show();
} else {
$('.initialSetupRebootBootloader').hide();
}
$('a.rebootBootloader').click(function () {
var buffer = [];
buffer.push(1);
MSP.send_message(MSPCodes.MSP_SET_REBOOT, buffer, false);
});
} else {
$('.initialSetupRebootBootloader').hide();
}
// UI Hooks // UI Hooks
$('a.calibrateAccel').click(function () { $('a.calibrateAccel').click(function () {
var self = $(this); var self = $(this);

View file

@ -39,6 +39,9 @@
<script type="text/javascript" src="./node_modules/i18next/i18next.js"></script> <script type="text/javascript" src="./node_modules/i18next/i18next.js"></script>
<script type="text/javascript" src="./node_modules/i18next-xhr-backend/i18nextXHRBackend.js"></script> <script type="text/javascript" src="./node_modules/i18next-xhr-backend/i18nextXHRBackend.js"></script>
<script type="text/javascript" src="./node_modules/marked/marked.min.js"></script> <script type="text/javascript" src="./node_modules/marked/marked.min.js"></script>
<script type="text/javascript" src="./node_modules/universal-ga/lib/analytics.min.js"></script>
<script type="text/javascript" src="./node_modules/short-unique-id/dist/short-unique-id.min.js"></script>
<script type="text/javascript" src="./node_modules/object-hash/dist/object_hash.js"></script>
<script type="text/javascript" src="./js/libraries/q.js"></script> <script type="text/javascript" src="./js/libraries/q.js"></script>
<script type="text/javascript" src="./js/libraries/jquery-2.1.4.min.js"></script> <script type="text/javascript" src="./js/libraries/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="./js/libraries/jquery-ui-1.11.4.min.js"></script> <script type="text/javascript" src="./js/libraries/jquery-ui-1.11.4.min.js"></script>
@ -54,6 +57,7 @@
<script type="text/javascript" src="./js/libraries/bluebird.min.js"></script> <script type="text/javascript" src="./js/libraries/bluebird.min.js"></script>
<script type="text/javascript" src="./js/libraries/jquery.ba-throttle-debounce.min.js"></script> <script type="text/javascript" src="./js/libraries/jquery.ba-throttle-debounce.min.js"></script>
<script type="text/javascript" src="./js/libraries/inflection.min.js"></script> <script type="text/javascript" src="./js/libraries/inflection.min.js"></script>
<script type="text/javascript" src="./js/libraries/analytics.js"></script>
<script type="text/javascript" src="./js/injected_methods.js"></script> <script type="text/javascript" src="./js/injected_methods.js"></script>
<script type="text/javascript" src="./js/data_storage.js"></script> <script type="text/javascript" src="./js/data_storage.js"></script>
<script type="text/javascript" src="./js/fc.js"></script> <script type="text/javascript" src="./js/fc.js"></script>
@ -79,6 +83,7 @@
<script type="text/javascript" src="./js/Beepers.js"></script> <script type="text/javascript" src="./js/Beepers.js"></script>
<script type="text/javascript" src="./js/release_checker.js"></script> <script type="text/javascript" src="./js/release_checker.js"></script>
<script type="text/javascript" src="./js/jenkins_loader.js"></script> <script type="text/javascript" src="./js/jenkins_loader.js"></script>
<script type="text/javascript" src="./js/Analytics.js"></script>
<script type="text/javascript" src="./js/main.js"></script> <script type="text/javascript" src="./js/main.js"></script>
<script type="text/javascript" src="./js/tabs/landing.js"></script> <script type="text/javascript" src="./js/tabs/landing.js"></script>
<script type="text/javascript" src="./js/tabs/setup.js"></script> <script type="text/javascript" src="./js/tabs/setup.js"></script>
@ -268,7 +273,9 @@
--> -->
</ul> </ul>
<ul class="mode-connected mode-connected-cli"> <ul class="mode-connected mode-connected-cli">
<li class="tab_cli"><a href="#" i18n="tabCLI" class="tabicon ic_cli" i18n_title="tabCLI"></a></li> <li class="tab_cli">
<a href="#" i18n="tabCLI" class="tabicon ic_cli" i18n_title="tabCLI"></a>
</li>
</ul> </ul>
</div> </div>
<div class="clear-both"></div> <div class="clear-both"></div>
@ -323,5 +330,15 @@
</div> </div>
</dialog> </dialog>
<dialog class="dialogError">
<h3 i18n="errorTitle"></h3>
<div class="content">
<div class="dialogError-content" style="margin-top: 10px"></div>
</div>
<div class="buttons">
<a href="#" class="dialogError-closebtn regular-button" i18n="close"></a>
</div>
</dialog>
</body> </body>
</html> </html>

View file

@ -66,34 +66,11 @@
<div class="marker"></div> <div class="marker"></div>
</div> </div>
</td> </td>
<td class="functionSelection"><select class="function"> <td class="functionSelection">
<option value="0" i18n="adjustmentsFunction0"></option> <select id="functionSelectionSelect" class="function">
<option value="1" i18n="adjustmentsFunction1"></option> <!-- Generated values go here -->
<option value="2" i18n="adjustmentsFunction2"></option> </select>
<option value="3" i18n="adjustmentsFunction3"></option> </td>
<option value="4" i18n="adjustmentsFunction4"></option>
<option value="5" i18n="adjustmentsFunction5"></option>
<option value="6" i18n="adjustmentsFunction6"></option>
<option value="7" i18n="adjustmentsFunction7"></option>
<option value="8" i18n="adjustmentsFunction8"></option>
<option value="9" i18n="adjustmentsFunction9"></option>
<option value="10" i18n="adjustmentsFunction10"></option>
<option value="11" i18n="adjustmentsFunction11"></option>
<option value="12" i18n="adjustmentsFunction12"></option>
<option value="13" i18n="adjustmentsFunction13"></option>
<option value="14" i18n="adjustmentsFunction14"></option>
<option value="15" i18n="adjustmentsFunction15"></option>
<option value="16" i18n="adjustmentsFunction16"></option>
<option value="17" i18n="adjustmentsFunction17"></option>
<option value="18" i18n="adjustmentsFunction18"></option>
<option value="19" i18n="adjustmentsFunction19"></option>
<option value="20" i18n="adjustmentsFunction20"></option>
<option value="21" i18n="adjustmentsFunction21"></option>
<option value="22" i18n="adjustmentsFunction22"></option>
<option value="23" i18n="adjustmentsFunction23"></option>
<option value="24" i18n="adjustmentsFunction24"></option>
<option value="25" i18n="adjustmentsFunction25"></option>
</select></td>
<td class="adjustmentSlot"><select class="slot"> <td class="adjustmentSlot"><select class="slot">
<option value="0" i18n="adjustmentsSlot0"></option> <option value="0" i18n="adjustmentsSlot0"></option>
<option value="1" i18n="adjustmentsSlot1"></option> <option value="1" i18n="adjustmentsSlot1"></option>

View file

@ -7,6 +7,12 @@
<div class="note spacebottom"> <div class="note spacebottom">
<div class="note_spacer"> <div class="note_spacer">
<p i18n="auxiliaryHelp"></p> <p i18n="auxiliaryHelp"></p>
<p>
<form>
<input type="checkbox" id="switch-toggle-unused" name="switch-toggle-unused" class="togglesmall"></input>
<label for="switch-toggle-unused"><span i18n="auxiliaryToggleUnused" /></label>
</form>
</p>
</div> </div>
</div> </div>
<table class="modes"> <table class="modes">

View file

@ -54,4 +54,14 @@
</div> </div>
</div> </div>
</div> </div>
<div id="privacy_policy">
<div class="button">
<a id="privacy_policy_toggle" href="#" i18n="defaultPrivacyPolicyAction"></a>
</div>
<div class="wrapper">
<div class="policy">
<!-- privacy policy content will be loaded here -->
</div>
</div>
</div>
</div> </div>

View file

@ -133,6 +133,23 @@
</div> </div>
</div> </div>
</div> </div>
<div class="gui_box grey require-msc-supported">
<div class="gui_box_titlebar" align="left">
<div class="spacer_box_title" i18n="onboardLoggingMsc">
</div>
</div>
<div class="spacer_box">
<div class="require-msc-supported">
<p i18n="onboardLoggingMscNote"></p>
<div>
<a class="require-msc-ready regular-button onboardLoggingRebootMsc" href="#" i18n="onboardLoggingRebootMscText"></a>
</div>
</div>
<p class="require-msc-not-ready" i18n="onboardLoggingMscNotReady"></p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -4,6 +4,12 @@
<div class="checkForConfiguratorUnstableVersions"> <div class="checkForConfiguratorUnstableVersions">
<label><input type="checkbox" /><span i18n="checkForConfiguratorUnstableVersions"></span></label> <label><input type="checkbox" /><span i18n="checkForConfiguratorUnstableVersions"></span></label>
</div> </div>
<div class="rememberLastTab">
<label><input type="checkbox" /><span i18n="rememberLastTab"></span></label>
</div>
<div class="analyticsOptOut">
<label><input type="checkbox" /><span i18n="analyticsOptOut"></span></label>
</div>
<div class="separator"></div> <div class="separator"></div>
<div class="userLanguage"> <div class="userLanguage">
<label> <label>

View file

@ -124,7 +124,11 @@
<h1 class="tab_title" i18n="osdSetupFontPresets" /> <h1 class="tab_title" i18n="osdSetupFontPresets" />
<!-- Font preview and list --> <!-- Font preview and list -->
<div class="content_wrapper font-preview"></div> <div class="content_wrapper font-preview"></div>
<div class="fontbuttons"></div> <div class="fontpresets_wrapper">
<label id="font-selector-label" i18n="osdSetupFontPresetsSelector"></label>
<select class="fontpresets"></select>
<span id="font-selector-span" i18n="osdSetupFontPresetsSelectorOr"> Or </span>
</div>
<!-- Boot logo setup --> <!-- Boot logo setup -->
<h1 class="tab_title" i18n="osdSetupCustomLogoTitle" /> <h1 class="tab_title" i18n="osdSetupCustomLogoTitle" />
<div id="font-logo-preview-container" class="content_wrapper"> <div id="font-logo-preview-container" class="content_wrapper">

200
src/tabs/pid_tuning.html Executable file → Normal file
View file

@ -33,7 +33,7 @@
</div> </div>
</div> </div>
<div class="cf_column right"> <div class="cf_column right">
<div class="default_btn show"> <div class="default_btn show showAllPids">
<a href="#" id="showAllPids" i18n="pidTuningShowAllPids"></a> <a href="#" id="showAllPids" i18n="pidTuningShowAllPids"></a>
</div> </div>
<div class="default_btn resetbt"> <div class="default_btn resetbt">
@ -66,24 +66,24 @@
<div class="clear-both"></div> <div class="clear-both"></div>
<div class="cf_column twothird"> <div class="cf_column twothird">
<div class="gui_box grey topspacer" style="margin-top: 0px;"> <div class="gui_box grey topspacer" style="margin-top: 0px;">
<table class="pid_titlebar"> <table id="pid_main" class="pid_tuning">
<tr> <tr class="pid_titlebar">
<th class="name"></th> <th class="name"></th>
<th class="proportional" i18n="pidTuningProportional"></th> <th class="proportional" i18n="pidTuningProportional"></th>
<th class="integral" i18n="pidTuningIntegral"></th> <th class="integral" i18n="pidTuningIntegral"></th>
<th class="derivative" i18n="pidTuningDerivative"></th> <th class="derivative" i18n="pidTuningDerivative"></th>
<th class="feedforward" i18n="pidTuningFeedforward"></th>
<th class="rc_rate" i18n="pidTuningRcRate"></th> <th class="rc_rate" i18n="pidTuningRcRate"></th>
<th class="rate" i18n="pidTuningRate"></th> <th class="rate" i18n="pidTuningRate"></th>
<th class="new_rates maxVel" i18n="pidTuningMaxVel"></th> <th class="new_rates maxVel" i18n="pidTuningMaxVel"></th>
<th class="rc_expo" i18n="pidTuningRcExpo"></th> <th class="rc_expo" i18n="pidTuningRcExpo"></th>
</tr> </tr>
</table> <tr class="pid_titlebar2">
<table id="pid_main" class="pid_tuning"> <th colspan="9">
<tr>
<th colspan="8">
<div class="pid_mode"> <div class="pid_mode">
<div i18n="pidTuningBasic" style="float:left;"></div> <div i18n="pidTuningBasic" style="float:left;"></div>
<div class="helpicon cf_tip" i18n_title="pidTuningPidTuningTip"></div> <div class="helpicon cf_tip" i18n_title="pidTuningPidTuningTip"></div>
<div class="helpicon cf_tip" i18n_title="pidTuningPidTuningTipFeedforward"></div>
</div> </div>
</th> </th>
</tr> </tr>
@ -93,6 +93,7 @@
<td class="pid_data"><input type="number" name="p" step="1" min="0" max="255" /></td> <td class="pid_data"><input type="number" name="p" step="1" min="0" max="255" /></td>
<td class="pid_data"><input type="number" name="i" step="1" min="0" max="255" /></td> <td class="pid_data"><input type="number" name="i" step="1" min="0" max="255" /></td>
<td class="pid_data"><input type="number" name="d" step="1" min="0" max="255" /></td> <td class="pid_data"><input type="number" name="d" step="1" min="0" max="255" /></td>
<td class="pid_data"><input type="number" name="f" step="1" min="0" max="255" /></td>
<td rowspan="2" style="background-color:white;"> <td rowspan="2" style="background-color:white;">
<input type="number" name="rc_rate" step="0.01" min="0" max="2.55" /> <input type="number" name="rc_rate" step="0.01" min="0" max="2.55" />
<div class="bracket"></div> <div class="bracket"></div>
@ -111,6 +112,7 @@
<td class="pid_data"><input type="number" name="p" step="1" min="0" max="255" /></td> <td class="pid_data"><input type="number" name="p" step="1" min="0" max="255" /></td>
<td class="pid_data"><input type="number" name="i" step="1" min="0" max="255" /></td> <td class="pid_data"><input type="number" name="i" step="1" min="0" max="255" /></td>
<td class="pid_data"><input type="number" name="d" step="1" min="0" max="255" /></td> <td class="pid_data"><input type="number" name="d" step="1" min="0" max="255" /></td>
<td class="pid_data"><input type="number" name="f" step="1" min="0" max="255" /></td>
<td class="pitch_rate"><input type="number" name="pitch_rate" step="0.01" min="0" max="1.00" /></td> <td class="pitch_rate"><input type="number" name="pitch_rate" step="0.01" min="0" max="1.00" /></td>
<td class="new_rates maxAngularVelPitch"></td> <td class="new_rates maxAngularVelPitch"></td>
</tr> </tr>
@ -119,7 +121,8 @@
<td bgcolor="#8080FF"></td> <td bgcolor="#8080FF"></td>
<td class="pid_data"><input type="number" name="p" step="1" min="0" max="255" /></td> <td class="pid_data"><input type="number" name="p" step="1" min="0" max="255" /></td>
<td class="pid_data"><input type="number" name="i" step="1" min="0" max="255" /></td> <td class="pid_data"><input type="number" name="i" step="1" min="0" max="255" /></td>
<td></td> <td class="pid_data"><input type="number" name="d" step="1" min="0" max="255" /></td>
<td class="pid_data"><input type="number" name="f" step="1" min="0" max="255" /></td>
<td rowspan="1"><input type="number" name="rc_rate_yaw" step="0.01" min="0" max="2.55" /></td> <td rowspan="1"><input type="number" name="rc_rate_yaw" step="0.01" min="0" max="2.55" /></td>
<td><input type="number" name="yaw_rate" step="0.01" min="0" max="2.55" /></td> <td><input type="number" name="yaw_rate" step="0.01" min="0" max="2.55" /></td>
<td class="new_rates maxAngularVelYaw"></td> <td class="new_rates maxAngularVelYaw"></td>
@ -137,17 +140,17 @@
</tr> </tr>
</table> </table>
</div> </div>
<div id="pid_optional" class="gui_box grey topspacer pid_tuning"> <div id="pid_baro_mag_gps" class="pid_optional needed_by_ALT needed_by_VEL needed_by_MAG needed_by_Pos needed_by_PosR needed_by_NavR gui_box grey topspacer pid_tuning">
<table class="pid_titlebar"> <table class="pid_titlebar needed_by_ALT needed_by_VEL needed_by_MAG">
<tr> <tr class="needed_by_ALT needed_by_VEL needed_by_MAG">
<th class="name"></th> <th class="name"></th>
<th class="proportional" i18n="pidTuningProportional"></th> <th class="proportional" i18n="pidTuningProportional"></th>
<th class="integral" i18n="pidTuningIntegral"></th> <th class="integral" i18n="pidTuningIntegral"></th>
<th class="derivative" i18n="pidTuningDerivative"></th> <th class="derivative" i18n="pidTuningDerivative"></th>
</tr> </tr>
</table> </table>
<table id="pid_baro" class="pid_tuning"> <table id="pid_baro" class="pid_tuning needed_by_ALT needed_by_VEL">
<tr> <tr class="needed_by_ALT needed_by_VEL">
<th colspan="4"> <th colspan="4">
<div class="pid_mode" i18n="pidTuningAltitude"></div> <div class="pid_mode" i18n="pidTuningAltitude"></div>
</th> </th>
@ -159,16 +162,16 @@
<td><input type="number" name="i" step="1" min="0" max="255" /></td> <td><input type="number" name="i" step="1" min="0" max="255" /></td>
<td><input type="number" name="d" step="1" min="0" max="255" /></td> <td><input type="number" name="d" step="1" min="0" max="255" /></td>
</tr> </tr>
<tr class="Vario"> <tr class="VEL">
<!-- 9 --> <!-- 9 -->
<td>VEL</td> <td></td>
<td><input type="number" name="p" step="1" min="0" max="255" /></td> <td><input type="number" name="p" step="1" min="0" max="255" /></td>
<td><input type="number" name="i" step="1" min="0" max="255" /></td> <td><input type="number" name="i" step="1" min="0" max="255" /></td>
<td><input type="number" name="d" step="1" min="0" max="255" /></td> <td><input type="number" name="d" step="1" min="0" max="255" /></td>
</tr> </tr>
</table> </table>
<table id="pid_mag" class="pid_tuning"> <table id="pid_mag" class="pid_tuning needed_by_MAG">
<tr> <tr class="needed_by_MAG">
<th colspan="4"> <th colspan="4">
<div class="pid_mode" i18n="pidTuningMag"></div> <div class="pid_mode" i18n="pidTuningMag"></div>
</th> </th>
@ -181,8 +184,8 @@
<td></td> <td></td>
</tr> </tr>
</table> </table>
<table id="pid_gps" class="pid_tuning"> <table id="pid_gps" class="pid_tuning needed_by_Pos needed_by_PosR needed_by_NavR">
<tr> <tr class="needed_by_Pos needed_by_PosR needed_by_NavR">
<th colspan="4"> <th colspan="4">
<div class="pid_mode" i18n="pidTuningGps"></div> <div class="pid_mode" i18n="pidTuningGps"></div>
</th> </th>
@ -211,9 +214,9 @@
</tr> </tr>
</table> </table>
</div> </div>
<div id="pid_accel" class="gui_box grey topspacer pid_tuning"> <div id="pid_accel" class="pid_optional needed_by_LEVEL gui_box grey topspacer pid_tuning">
<table id="pid_level" class="pid_tuning"> <table id="pid_level" class="pid_tuning needed_by_LEVEL">
<tr> <tr class="needed_by_LEVEL">
<th colspan="3"> <th colspan="3">
<div class="pid_mode"> <div class="pid_mode">
<div i18n="pidTuningLevel" style="float:left;"></div> <div i18n="pidTuningLevel" style="float:left;"></div>
@ -222,42 +225,43 @@
</th> </th>
</tr> </tr>
</table> </table>
<table class="pid_titlebar"> <table class="pid_titlebar needed_by_LEVEL">
<tr> <tr class="needed_by_LEVEL">
<th class="third"></th> <th class="third"></th>
<th class="third" i18n="pidTuningStrength" style="width: 33%;"></th> <th class="third" i18n="pidTuningStrength" style="width: 33%;"></th>
<th class="third" i18n="pidTuningTransition" style="width: 33%;"></th> <th class="third" i18n="pidTuningTransition" style="width: 33%;"></th>
</tr> </tr>
</table> </table>
<table> <table class="needed_by_LEVEL">
<tr class="ANGLE"> <tr class="LEVEL">
<!-- 7 --> <!-- 7 -->
<td class="third" i18n="pidTuningAngle"></td> <td class="third" i18n="pidTuningAngle"></td>
<td class="third"><input type="number" name="p" step="1" min="0" max="255" /></td> <td class="third"><input type="number" name="p" step="1" min="0" max="255" /></td>
<td class="third"></td> <td class="third"></td>
</tr> </tr>
<tr class="HORIZON"> <tr class="LEVEL">
<!-- 7 --> <!-- 7 -->
<td class="third" i18n="pidTuningHorizon"></td> <td class="third" i18n="pidTuningHorizon"></td>
<td class="third"><input type="number" name="i" step="1" min="0" max="255" /></td> <td class="third"><input type="number" name="i" step="1" min="0" max="255" /></td>
<td class="third"><input type="number" name="d" step="1" min="0" max="255" /></td> <td class="third"><input type="number" name="d" step="1" min="0" max="255" /></td>
</tr> </tr>
</table> </table>
<table class="pid_titlebar pid_sensitivity"> <table class="needed_by_LEVEL pid_titlebar pid_sensitivity">
<tr> <tr class="needed_by_LEVEL">
<th class="third"></th> <th class="third"></th>
<th class="third" i18n="pidTuningLevelAngleLimit" style="width: 33%;"></th> <th class="third" i18n="pidTuningLevelAngleLimit" style="width: 33%;"></th>
<th class="third levelSensitivityHeader" i18n="pidTuningLevelSensitivity" style="width: 33%;"></th> <th class="third levelSensitivityHeader" i18n="pidTuningLevelSensitivity" style="width: 33%;"></th>
</tr> </tr>
</table> </table>
<table id="pid_sensitivity" class="pid_tuning pid_sensitivity"> <table id="pid_sensitivity" class="needed_by_LEVEL pid_tuning pid_sensitivity">
<tr> <tr class="needed_by_LEVEL">
<td class="third"></td> <td class="third"></td>
<td class="third"><input type="number" name="angleLimit" step="1" min="10" max="200" /></td> <td class="third"><input type="number" name="angleLimit" step="1" min="10" max="200" /></td>
<td class="third"><input type="number" name="sensitivity" step="1" min="10" max="120" /></td> <td class="third"><input type="number" name="sensitivity" step="1" min="10" max="120" /></td>
</tr> </tr>
</table> </table>
</div> </div>
<div class="gui_box grey topspacer pidTuningFeatures"> <div class="gui_box grey topspacer pidTuningFeatures">
<table class="pid_titlebar new_rates"> <table class="pid_titlebar new_rates">
<tr> <tr>
@ -322,8 +326,73 @@
<span i18n="pidTuningDtermSetpointTransitionWarning"></span> <span i18n="pidTuningDtermSetpointTransitionWarning"></span>
</td> </td>
</tr> </tr>
<tr class="feedforwardTransition" style="height:30px;">
<td><input type="number" name="feedforwardTransition-number" step="0.01" min="0.00" max="1.00"/></td>
<td class="slider"><input type="range" name="feedforwardTransition-range" step="0.01" min="0.00" max="1.00"/></td>
<td>
<div>
<label>
<span i18n="pidTuningFeedforwardTransition"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningFeedforwardTransitionHelp"></div>
</div>
</td>
</tr>
<tr class="acroTrainerAngleLimit">
<td><input type="number" name="acroTrainerAngleLimit-number" step="1" min="10" max="80"/></td>
<td class="slider"><input type="range" name="acroTrainerAngleLimit-range" step="1" min="10" max="80"/></td>
<td colspan="2">
<div>
<label>
<span i18n="pidTuningAcroTrainerAngleLimit"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningAcroTrainerAngleLimitHelp"></div>
</div>
</td>
</tr>
<tr class="throttleBoost">
<td><input type="number" name="throttleBoost-number" step="1" min="0" max="100"/></td>
<td class="slider"><input type="range" name="throttleBoost-range" step="1" min="0" max="100"/></td>
<td colspan="2">
<div>
<label>
<span i18n="pidTuningThrottleBoost"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningThrottleBoostHelp"></div>
</div>
</td>
</tr>
<tr class="absoluteControlGain">
<td><input type="number" name="absoluteControlGain-number" step="1" min="0" max="20"/></td>
<td class="slider"><input type="range" name="absoluteControlGain-range" step="1" min="0" max="20"/></td>
<td colspan="2">
<div>
<label>
<span i18n="pidTuningAbsoluteControlGain"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningAbsoluteControlGainHelp"></div>
</div>
</td>
</tr>
<tr class="itermrotation">
<td><input type="checkbox" id="itermrotation" class="toggle" /></td>
<td colspan="2">
<div>
<label for="itermrotation">
<span i18n="pidTuningItermRotation"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningItermRotationHelp"></div>
</div>
</td>
</tr>
<tr> <tr>
<td style="height:30px;"><input type="checkbox" id="vbatpidcompensation" class="toggle" /></td> <td><input type="checkbox" id="vbatpidcompensation" class="toggle" /></td>
<td colspan="2"> <td colspan="2">
<div> <div>
<label for="vbatpidcompensation"> <label for="vbatpidcompensation">
@ -333,6 +402,54 @@
</div> </div>
</td> </td>
</tr> </tr>
<tr class="smartfeedforward">
<td><input type="checkbox" id="smartfeedforward" class="toggle" /></td>
<td colspan="2">
<div>
<label for="smartfeedforward">
<span i18n="pidTuningSmartFeedforward"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningSmartFeedforwardHelp"></div>
</div>
</td>
</tr>
<tr class="itermrelax">
<td><input type="checkbox" id="itermrelax" class="toggle" /></td>
<td colspan="2">
<span>
<label for="itermrelax">
<span i18n="pidTuningItermRelax" />
</label>
</span>
<span class="suboption">
<label for="itermrelaxAxes">
<span i18n="pidTuningItermRelaxAxes" />
</label>
<select id="itermrelaxAxes">
<option i18n="pidTuningItermRelaxAxesOptionRP" value="1">
<option i18n="pidTuningItermRelaxAxesOptionRPY" value="2"/>
<option i18n="pidTuningItermRelaxAxesOptionRPInc" value="3"/>
<option i18n="pidTuningItermRelaxAxesOptionRPYInc" value="4"/>
</select>
</span>
<span class="suboption">
<label for="itermrelaxType">
<span i18n="pidTuningItermRelaxType" />
</label>
<select id="itermrelaxType">
<option i18n="pidTuningItermRelaxTypeOptionGyro" value="0"/>
<option i18n="pidTuningItermRelaxTypeOptionSetpoint" value="1"/>
</select>
</span>
<div class="helpicon cf_tip" i18n_title="pidTuningItermRelaxHelp"></div>
</td>
</tr>
</table> </table>
</div> </div>
@ -340,14 +457,25 @@
<table class="cf"> <table class="cf">
<thead> <thead>
<tr> <tr>
<th i18n="pidTuningAntiGravityMode"></th>
<th i18n="pidTuningAntiGravityGain"></th> <th i18n="pidTuningAntiGravityGain"></th>
<th i18n="pidTuningAntiGravityThres"></th> <th i18n="pidTuningAntiGravityThres"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr style="height: 35px;"> <tr style="height: 35px;">
<td><input type="number" name="itermAcceleratorGain" step="0.1" min="1" max="30" /></td> <td>
<td><input type="number" name="itermThrottleThreshold" step="10" min="20" max="1000" /></td> <select id="antiGravityMode">
<option i18n="pidTuningAntiGravityModeOptionSmooth" value="0">
<option i18n="pidTuningAntiGravityModeOptionStep" value="1">
</select>
</td>
<td>
<input type="number" name="itermAcceleratorGain" step="0.1" min="1" max="30" />
</td>
<td>
<input type="number" name="itermThrottleThreshold" step="10" min="20" max="1000" />
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -375,7 +503,7 @@
<div class="cf_column third_right"> <div class="cf_column third_right">
<div class="spacer_left rc_curve"> <div class="spacer_left rc_curve">
<div class="rc_curve_bg"> <div class="rc_curve_bg">
<table class="cf"> <table class="cf rc_curve">
<thead> <thead>
<tr> <tr>
<th colspan="2"> <th colspan="2">
@ -388,7 +516,7 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td> <td colspan="2">
<div class="spacer" style="margin-top: 10px; margin-bottom: 8px;"> <div class="spacer" style="margin-top: 10px; margin-bottom: 8px;">
<div class="rate_curve" style="position:relative;" > <div class="rate_curve" style="position:relative;" >
<canvas id="rate_curve_layer0" height="120px" style="position:absolute; top: 0; left: 0; z-index: 0; height:100%; width:100%;"></canvas> <canvas id="rate_curve_layer0" height="120px" style="position:absolute; top: 0; left: 0; z-index: 0; height:100%; width:100%;"></canvas>

View file

@ -0,0 +1,104 @@
<h1>Privacy Policy</h1>
<p>Effective date: August 06, 2018</p>
<p>The Betaflight Group ("us", "we", or "our") operates the website and the Betaflight Configurator application (the "Service").</p>
<p>This page informs you of our policies regarding the collection, use, and disclosure of personal data when you use our Service and the choices you have associated with that data. This Privacy Policy for The Betaflight Group is powered by <a href="https://www.freeprivacypolicy.com/free-privacy-policy-generator.php">FreePrivacyPolicy.com</a>.</p>
<p>We use your data to provide and improve the Service. By using the Service, you agree to the collection and use of information in accordance with this policy. Unless otherwise defined in this Privacy Policy, terms used in this Privacy Policy have the same meanings as in our Terms and Conditions.</p>
<h2>Information Collection And Use</h2>
<p>We collect several different types of information for various purposes to provide and improve our Service to you.</p>
<h3>Types of Data Collected</h3>
<h4>Usage Data</h4>
<p>We may also collect information that your browser sends whenever you visit our Service or when you access the Service by or through a device ("Usage Data").</p>
<p>This Usage Data may include information such as your computer's Internet Protocol address (e.g. IP address), browser type, browser version, the pages of our Service that you visit, the time and date of your visit, the time spent on those pages, unique device identifiers and other diagnostic data.</p>
<p>When you access the Service by or through a device, this Usage Data may include information such as the type of device you use, your device unique ID, the IP address of your device, your operating system, the type of Internet browser you use, unique device identifiers and other diagnostic data.</p>
<h4>Tracking & Cookies Data</h4>
<p>We use cookies and similar tracking technologies to track the activity on our Service and hold certain information.</p>
<p>Cookies are files with small amount of data which may include an anonymous unique identifier. Cookies are sent to your browser from a website and stored on your device. Tracking technologies also used are beacons, tags, and scripts to collect and track information and to improve and analyze our Service.</p>
<p>Examples of Cookies we use:</p>
<ul>
<li><strong>Session Cookies.</strong> We use Session Cookies to operate our Service.</li>
<li><strong>Preference Cookies.</strong> We use Preference Cookies to remember your preferences and various settings.</li>
</ul>
<h2>Use of Data</h2>
<p>The Betaflight Group uses the collected data for various purposes:</p>
<ul>
<li>To provide and maintain the Service</li>
<li>To notify you about changes to our Service</li>
<li>To allow you to participate in interactive features of our Service when you choose to do so</li>
<li>To provide customer care and support</li>
<li>To provide analysis or valuable information so that we can improve the Service</li>
<li>To monitor the usage of the Service</li>
<li>To detect, prevent and address technical issues</li>
</ul>
<h2>Transfer Of Data</h2>
<p>Your information, including Personal Data, may be transferred to — and maintained on — computers located outside of your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from your jurisdiction.</p>
<p>If you are located outside United States and choose to provide information to us, please note that we transfer the data, including Personal Data, to United States and process it there.</p>
<p>Your consent to this Privacy Policy followed by your submission of such information represents your agreement to that transfer.</p>
<p>The Betaflight Group will take all steps reasonably necessary to ensure that your data is treated securely and in accordance with this Privacy Policy and no transfer of your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of your data and other personal information.</p>
<h2>Disclosure Of Data</h2>
<h3>Legal Requirements</h3>
<p>The Betaflight Group may disclose your Personal Data in the good faith belief that such action is necessary to:</p>
<ul>
<li>To comply with a legal obligation</li>
<li>To protect and defend the rights or property of The Betaflight Group</li>
<li>To prevent or investigate possible wrongdoing in connection with the Service</li>
<li>To protect the personal safety of users of the Service or the public</li>
<li>To protect against legal liability</li>
</ul>
<h2>Security Of Data</h2>
<p>The security of your data is important to us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While we strive to use commercially acceptable means to protect your Personal Data, we cannot guarantee its absolute security.</p>
<h2>Service Providers</h2>
<p>We may employ third party companies and individuals to facilitate our Service ("Service Providers"), to provide the Service on our behalf, to perform Service-related services or to assist us in analyzing how our Service is used.</p>
<p>These third parties have access to your Personal Data only to perform these tasks on our behalf and are obligated not to disclose or use it for any other purpose.</p>
<h3>Analytics</h3>
<p>We may use third-party Service Providers to monitor and analyze the use of our Service.</p>
<ul>
<li>
<p><strong>Google Analytics</strong></p>
<p>Google Analytics is a web analytics service offered by Google that tracks and reports website traffic. Google uses the data collected to track and monitor the use of our Service. This data is shared with other Google services. Google may use the collected data to contextualize and personalize the ads of its own advertising network.</p>
<p>For more information on the privacy practices of Google, please visit the Google Privacy & Terms web page: <a href="https://policies.google.com/privacy?hl=en">https://policies.google.com/privacy?hl=en</a></p>
</li>
</ul>
<h2>Links To Other Sites</h2>
<p>Our Service may contain links to other sites that are not operated by us. If you click on a third party link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit.</p>
<p>We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.</p>
<h2>Children's Privacy</h2>
<p>Our Service does not address anyone under the age of 18 ("Children").</p>
<p>We do not knowingly collect personally identifiable information from anyone under the age of 18. If you are a parent or guardian and you are aware that your Children has provided us with Personal Data, please contact us. If we become aware that we have collected Personal Data from children without verification of parental consent, we take steps to remove that information from our servers.</p>
<h2>Changes To This Privacy Policy</h2>
<p>We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.</p>
<p>We will let you know via email and/or a prominent notice on our Service, prior to the change becoming effective and update the "effective date" at the top of this Privacy Policy.</p>
<p>You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.</p>
<h2>Contact Us</h2>
<p>If you have any questions about this Privacy Policy, please contact us:</p>
<ul>
<li>By visiting this page on our website: https://github.com/betaflight/betaflight-configurator/issues</li>
</ul>

View file

@ -46,6 +46,9 @@
<a class="restore" href="#" i18n="initialSetupButtonRestore"></a> <a class="restore" href="#" i18n="initialSetupButtonRestore"></a>
</div> </div>
</div> </div>
<div class="default_btn initialSetupRebootBootloader">
<a class="rebootBootloader" href="#" i18n="initialSetupButtonRebootBootloader"></a>
</div>
</div> </div>
</div> </div>
<div class="threefourth_right setupinfo"> <div class="threefourth_right setupinfo">
@ -61,6 +64,9 @@
<div class="cell_setup"> <div class="cell_setup">
<span i18n="initialSetupBackupRestoreText"></span> <span i18n="initialSetupBackupRestoreText"></span>
</div> </div>
<div class="cell_setup initialSetupRebootBootloader">
<span i18n="initialSetupRebootBootloaderText"></span>
</div>
</div> </div>
</div> </div>
<div class="modelwrapper"></div> <div class="modelwrapper"></div>