Bringing Google stats back - Reverting #3148 (#3204)

10.9-maintenance
Ivan Efimov 2023-01-06 17:05:34 -06:00 committed by GitHub
parent e0209c6b59
commit 8e7aa89ee5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 745 additions and 30 deletions

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

@ -697,6 +697,9 @@
"defaultDiscordText": { "defaultDiscordText": {
"message": "Betaflight <a href=\"https://discord.betaflight.com/invite\" target=\"_blank\" rel=\"noopener noreferrer\">Discord Server</a>.<br />Share your flight experience, talk about Betaflight, help other people or get some help for yourself from the community." "message": "Betaflight <a href=\"https://discord.betaflight.com/invite\" target=\"_blank\" rel=\"noopener noreferrer\">Discord Server</a>.<br />Share your flight experience, talk about Betaflight, help other people or get some help for yourself from the community."
}, },
"statisticsDisclaimer": {
"message": "Betaflight Configurator collects anonymous usage statistics. For example, this data includes (but is not limited to) the number of launches, geographical region of the users, types of flight controllers, firmware versions, usage of UI elements and tabs, etc. The summary of this data is shared <a href=\"https://datastudio.google.com/u/0/reporting/1pK1PFw0eWmsgl2npvuzwAqscVzlhJvuF\" target=\"_blank\" rel=\"noopener noreferrer\">here</a>. Collection is done in order to better understand how Betaflight Configurator is being used, to understand community trends, and for possible UI improvements. Users can opt-out of data collection in the Options tab."
},
"defaultChangelogHead": { "defaultChangelogHead": {
"message": "Configurator - Changelog" "message": "Configurator - Changelog"
}, },

180
src/js/Analytics.js Normal file
View File

@ -0,0 +1,180 @@
'use strict';
const Analytics = function (trackingId, userId, appName, appVersion, gitRevision, os, checkForDebugVersions, optOut, debugMode, buildType) {
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',
FLASHING: 'Flashing',
};
this.DATA = {
BOARD_TYPE: 'boardType',
API_VERSION: 'apiVersion',
FIRMWARE_TYPE: 'firmwareType',
FIRMWARE_VERSION: 'firmwareVersion',
FIRMWARE_NAME: 'firmwareName',
FIRMWARE_SOURCE: 'firmwareSource',
FIRMWARE_CHANNEL: 'firmwareChannel',
FIRMWARE_ERASE_ALL: 'firmwareEraseAll',
FIRMWARE_SIZE: 'firmwareSize',
MCU_ID: 'mcuId',
LOGGING_STATUS: 'loggingStatus',
LOG_SIZE: 'logSize',
TARGET_NAME: 'targetName',
BOARD_NAME: 'boardName',
MANUFACTURER_ID: 'manufacturerId',
MCU_TYPE: 'mcuType',
};
this.DIMENSIONS = {
CONFIGURATOR_OS: 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,
CONFIGURATOR_CHANGESET_ID: 13,
CONFIGURATOR_USE_DEBUG_VERSIONS: 14,
TARGET_NAME: 15,
BOARD_NAME: 16,
MANUFACTURER_ID: 17,
MCU_TYPE: 18,
CONFIGURATOR_BUILD_TYPE: 19,
};
this.METRICS = {
FIRMWARE_SIZE: 1,
LOG_SIZE: 2,
};
this.setDimension(this.DIMENSIONS.CONFIGURATOR_OS, os);
this.setDimension(this.DIMENSIONS.CONFIGURATOR_CHANGESET_ID, gitRevision);
this.setDimension(this.DIMENSIONS.CONFIGURATOR_USE_DEBUG_VERSIONS, checkForDebugVersions);
this.setDimension(this.DIMENSIONS.CONFIGURATOR_BUILD_TYPE, buildType);
this.resetFlightControllerData();
this.resetFirmwareData();
};
Analytics.prototype.setDimension = function (dimension, value) {
const dimensionName = `dimension${dimension}`;
this._googleAnalytics.custom(dimensionName, value);
};
Analytics.prototype.setMetric = function (metric, value) {
const 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 (const actionName in changeList) {
if (changeList.hasOwnProperty(actionName)) {
const actionValue = changeList[actionName];
if (actionValue !== undefined) {
this.sendEvent(category, actionName, { eventLabel: actionValue });
}
}
}
};
Analytics.prototype.sendSaveAndChangeEvents = function (category, changeList, tabName) {
this.sendEvent(category, 'Save', {
eventLabel: tabName,
eventValue: Object.keys(changeList).length,
});
this.sendChangeEvents(category, changeList);
};
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]);
this.setDimension(this.DIMENSIONS.TARGET_NAME, this._flightControllerData[this.DATA.TARGET_NAME]);
this.setDimension(this.DIMENSIONS.BOARD_NAME, this._flightControllerData[this.DATA.BOARD_NAME]);
this.setDimension(this.DIMENSIONS.MANUFACTURER_ID, this._flightControllerData[this.DATA.MANUFACTURER_ID]);
this.setDimension(this.DIMENSIONS.MCU_TYPE, this._flightControllerData[this.DATA.MCU_TYPE]);
};
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]);
};
Analytics.prototype.setFirmwareData = function (property, value) {
this._firmwareData[property] = value;
this._rebuildFirmwareEvent();
};
Analytics.prototype.resetFirmwareData = function () {
this._firmwareData = {};
this._rebuildFirmwareEvent();
};

View File

@ -51,6 +51,8 @@ CliAutoComplete.setEnabled = function(enable) {
}; };
CliAutoComplete.initialize = function($textarea, sendLine, writeToOutput) { CliAutoComplete.initialize = function($textarea, sendLine, writeToOutput) {
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'CliAutoComplete', this.configEnabled);
this.$textarea = $textarea; this.$textarea = $textarea;
this.forceOpen = false; this.forceOpen = false;
this.sendLine = sendLine; this.sendLine = sendLine;

View File

@ -39,14 +39,23 @@ const 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 () {
return this._featureMask; const self = this;
analytics.sendChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self._analyticsChanges);
self._analyticsChanges = {};
return self._featureMask;
}; };
Features.prototype.setMask = function (featureMask) { Features.prototype.setMask = function (featureMask) {
this._featureMask = featureMask; const self = this;
self._featureMask = featureMask;
}; };
Features.prototype.isEnabled = function (featureName) { Features.prototype.isEnabled = function (featureName) {
@ -159,25 +168,33 @@ Features.prototype.updateData = function (featureElement) {
if (featureElement.attr('type') === 'checkbox') { if (featureElement.attr('type') === 'checkbox') {
const bit = featureElement.data('bit'); const bit = featureElement.data('bit');
let 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') {
const controlElements = featureElement.children(); const controlElements = featureElement.children();
const selectedBit = featureElement.val(); const selectedBit = featureElement.val();
if (selectedBit !== -1) { if (selectedBit !== -1) {
let selectedFeature;
for (const controlElement of controlElements) { for (const controlElement of controlElements) {
const bit = controlElement.value; const bit = controlElement.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

@ -225,6 +225,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();
}; };
@ -307,6 +308,8 @@ function configuration_restore(callback) {
configuration.FEATURE_CONFIG.features = features; configuration.FEATURE_CONFIG.features = features;
} }
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Restore');
configuration_upload(configuration, callback); configuration_upload(configuration, callback);
} else { } else {
GUI.log(i18n.getMessage('backupFileIncompatible')); GUI.log(i18n.getMessage('backupFileIncompatible'));

View File

@ -67,11 +67,64 @@ function appReady() {
}, },
}); });
checkSetupAnalytics(function (analyticsService) {
analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, 'SelectedLanguage', i18n.selectedLanguage);
});
initializeSerialBackend(); initializeSerialBackend();
});
}
function checkSetupAnalytics(callback) {
if (!analytics) {
setTimeout(function () {
const result = ConfigStorage.get(['userId', 'analyticsOptOut', 'checkForConfiguratorUnstableVersions' ]);
if (!analytics) {
setupAnalytics(result);
}
callback(analytics);
});
} else if (callback) {
callback(analytics);
}
}
function getBuildType() {
return GUI.Mode;
}
function setupAnalytics(result) {
let userId;
if (result.userId) {
userId = result.userId;
} else {
const uid = new ShortUniqueId();
userId = uid.randomUUID(13);
ConfigStorage.set({ 'userId': userId });
}
const optOut = !!result.analyticsOptOut;
const checkForDebugVersions = !!result.checkForConfiguratorUnstableVersions;
const debugMode = typeof process === "object" && process.versions['nw-flavor'] === 'sdk';
window.analytics = new Analytics('UA-123002063-1', userId, CONFIGURATOR.productName, CONFIGURATOR.version, CONFIGURATOR.gitRevision, GUI.operating_system,
checkForDebugVersions, optOut, debugMode, getBuildType());
function logException(exception) {
analytics.sendException(exception.stack);
}
if (typeof process === "object") {
process.on('uncaughtException', logException);
}
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'AppStart', { sessionControl: 'start' });
$('.connect_b a.connect').removeClass('disabled'); $('.connect_b a.connect').removeClass('disabled');
$('.firmware_b a.flash').removeClass('disabled'); $('.firmware_b a.flash').removeClass('disabled');
});
} }
function closeSerial() { function closeSerial() {
@ -134,6 +187,8 @@ function closeHandler() {
this.hide(); this.hide();
} }
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'AppClose', { sessionControl: 'end' });
closeSerial(); closeSerial();
if (!GUI.isCordova()) { if (!GUI.isCordova()) {
@ -268,6 +323,10 @@ function startProcess() {
GUI.tab_switch_in_progress = false; GUI.tab_switch_in_progress = false;
} }
checkSetupAnalytics(function (analyticsService) {
analyticsService.sendAppView(tab);
});
switch (tab) { switch (tab) {
case 'landing': case 'landing':
import("./tabs/landing").then(({ landing }) => import("./tabs/landing").then(({ landing }) =>
@ -524,6 +583,9 @@ function startProcess() {
$(expertModeCheckbox).on("change", () => { $(expertModeCheckbox).on("change", () => {
const checked = $(expertModeCheckbox).is(':checked'); const checked = $(expertModeCheckbox).is(':checked');
checkSetupAnalytics(function (analyticsService) {
analyticsService.setDimension(analyticsService.DIMENSIONS.CONFIGURATOR_EXPERT_MODE, checked ? 'On' : 'Off');
});
if (FC.FEATURE_CONFIG && FC.FEATURE_CONFIG.features !== 0) { if (FC.FEATURE_CONFIG && FC.FEATURE_CONFIG.features !== 0) {
updateTabList(FC.FEATURE_CONFIG.features); updateTabList(FC.FEATURE_CONFIG.features);
@ -567,6 +629,10 @@ function startProcess() {
function setDarkTheme(enabled) { function setDarkTheme(enabled) {
DarkTheme.setConfig(enabled); DarkTheme.setConfig(enabled);
checkSetupAnalytics(function (analyticsService) {
analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, 'DarkTheme', enabled);
});
} }
@ -732,6 +798,8 @@ function showErrorDialog(message) {
// TODO: all of these are used as globals in other parts. // TODO: all of these are used as globals in other parts.
// once moved to modules extract to own module. // once moved to modules extract to own module.
window.googleAnalytics = analytics;
window.analytics = null;
window.showErrorDialog = showErrorDialog; window.showErrorDialog = showErrorDialog;
window.generateFilename = generateFilename; window.generateFilename = generateFilename;
window.updateTabList = updateTabList; window.updateTabList = updateTabList;
@ -739,3 +807,4 @@ window.isExpertModeEnabled = isExpertModeEnabled;
window.checkForConfiguratorUpdates = checkForConfiguratorUpdates; window.checkForConfiguratorUpdates = checkForConfiguratorUpdates;
window.setDarkTheme = setDarkTheme; window.setDarkTheme = setDarkTheme;
window.appReady = appReady; window.appReady = appReady;
window.checkSetupAnalytics = checkSetupAnalytics;

View File

@ -157,9 +157,16 @@ function finishClose(finishedCallback) {
const wasConnected = CONFIGURATOR.connectionValid; const wasConnected = CONFIGURATOR.connectionValid;
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Disconnected');
if (connectionTimestamp) {
const connectedTime = Date.now() - connectionTimestamp;
analytics.sendTiming(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Connected', connectedTime);
}
// close reset to custom defaults dialog // close reset to custom defaults dialog
$('#dialogResetToCustomDefaults')[0].close(); $('#dialogResetToCustomDefaults')[0].close();
analytics.resetFlightControllerData();
serial.disconnect(onClosed); serial.disconnect(onClosed);
MSP.disconnect_cleanup(); MSP.disconnect_cleanup();
@ -242,13 +249,18 @@ function onOpen(openInfo) {
console.log(`Requesting configuration data`); console.log(`Requesting 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, FC.CONFIG.apiVersion);
GUI.log(i18n.getMessage('apiVersionReceived', [FC.CONFIG.apiVersion])); GUI.log(i18n.getMessage('apiVersionReceived', [FC.CONFIG.apiVersion]));
if (semver.gte(FC.CONFIG.apiVersion, CONFIGURATOR.API_VERSION_ACCEPTED)) { if (semver.gte(FC.CONFIG.apiVersion, CONFIGURATOR.API_VERSION_ACCEPTED)) {
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, FC.CONFIG.flightControllerIdentifier);
if (FC.CONFIG.flightControllerIdentifier === 'BTFL') { if (FC.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, FC.CONFIG.flightControllerVersion);
GUI.log(i18n.getMessage('fcInfoReceived', [FC.CONFIG.flightControllerIdentifier, FC.CONFIG.flightControllerVersion])); GUI.log(i18n.getMessage('fcInfoReceived', [FC.CONFIG.flightControllerIdentifier, FC.CONFIG.flightControllerVersion]));
MSP.send_message(MSPCodes.MSP_BUILD_INFO, false, false, function () { MSP.send_message(MSPCodes.MSP_BUILD_INFO, false, false, function () {
@ -259,6 +271,8 @@ function onOpen(openInfo) {
}); });
}); });
} else { } else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'ConnectionRefusedFirmwareType', FC.CONFIG.flightControllerIdentifier);
const dialog = $('.dialogConnectWarning')[0]; const dialog = $('.dialogConnectWarning')[0];
$('.dialogConnectWarning-content').html(i18n.getMessage('firmwareTypeNotSupported')); $('.dialogConnectWarning-content').html(i18n.getMessage('firmwareTypeNotSupported'));
@ -273,6 +287,8 @@ function onOpen(openInfo) {
} }
}); });
} else { } else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'ConnectionRefusedFirmwareVersion', FC.CONFIG.apiVersion);
const dialog = $('.dialogConnectWarning')[0]; const dialog = $('.dialogConnectWarning')[0];
$('.dialogConnectWarning-content').html(i18n.getMessage('firmwareVersionNotSupported', [CONFIGURATOR.API_VERSION_ACCEPTED])); $('.dialogConnectWarning-content').html(i18n.getMessage('firmwareVersionNotSupported', [CONFIGURATOR.API_VERSION_ACCEPTED]));
@ -287,6 +303,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'));
@ -323,12 +341,20 @@ function abortConnect() {
} }
function processBoardInfo() { function processBoardInfo() {
analytics.setFlightControllerData(analytics.DATA.BOARD_TYPE, FC.CONFIG.boardIdentifier);
analytics.setFlightControllerData(analytics.DATA.TARGET_NAME, FC.CONFIG.targetName);
analytics.setFlightControllerData(analytics.DATA.BOARD_NAME, FC.CONFIG.boardName);
analytics.setFlightControllerData(analytics.DATA.MANUFACTURER_ID, FC.CONFIG.manufacturerId);
analytics.setFlightControllerData(analytics.DATA.MCU_TYPE, FC.getMcuType());
GUI.log(i18n.getMessage('boardInfoReceived', [FC.getHardwareName(), FC.CONFIG.boardVersion])); GUI.log(i18n.getMessage('boardInfoReceived', [FC.getHardwareName(), FC.CONFIG.boardVersion]));
if (bit_check(FC.CONFIG.targetCapabilities, FC.TARGET_CAPABILITIES_FLAGS.SUPPORTS_CUSTOM_DEFAULTS) && bit_check(FC.CONFIG.targetCapabilities, FC.TARGET_CAPABILITIES_FLAGS.HAS_CUSTOM_DEFAULTS) && FC.CONFIG.configurationState === FC.CONFIGURATION_STATES.DEFAULTS_BARE) { if (bit_check(FC.CONFIG.targetCapabilities, FC.TARGET_CAPABILITIES_FLAGS.SUPPORTS_CUSTOM_DEFAULTS) && bit_check(FC.CONFIG.targetCapabilities, FC.TARGET_CAPABILITIES_FLAGS.HAS_CUSTOM_DEFAULTS) && FC.CONFIG.configurationState === FC.CONFIGURATION_STATES.DEFAULTS_BARE) {
const dialog = $('#dialogResetToCustomDefaults')[0]; const dialog = $('#dialogResetToCustomDefaults')[0];
$('#dialogResetToCustomDefaults-acceptbtn').click(function() { $('#dialogResetToCustomDefaults-acceptbtn').click(function() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'AcceptResetToCustomDefaults');
const buffer = []; const buffer = [];
buffer.push(mspHelper.RESET_TYPES.CUSTOM_DEFAULTS); buffer.push(mspHelper.RESET_TYPES.CUSTOM_DEFAULTS);
MSP.send_message(MSPCodes.MSP_RESET_CONF, buffer, false); MSP.send_message(MSPCodes.MSP_RESET_CONF, buffer, false);
@ -341,6 +367,8 @@ function processBoardInfo() {
}); });
$('#dialogResetToCustomDefaults-cancelbtn').click(function() { $('#dialogResetToCustomDefaults-cancelbtn').click(function() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'CancelResetToCustomDefaults');
dialog.close(); dialog.close();
setConnectionTimeout(); setConnectionTimeout();
@ -357,12 +385,15 @@ function processBoardInfo() {
} }
function checkReportProblems() { function checkReportProblems() {
const PROBLEM_ANALYTICS_EVENT = 'ProblemFound';
const problemItemTemplate = $('#dialogReportProblems-listItemTemplate'); const problemItemTemplate = $('#dialogReportProblems-listItemTemplate');
function checkReportProblem(problemName, problemDialogList) { function checkReportProblem(problemName, problemDialogList) {
if (bit_check(FC.CONFIG.configurationProblems, FC.CONFIGURATION_PROBLEM_FLAGS[problemName])) { if (bit_check(FC.CONFIG.configurationProblems, FC.CONFIGURATION_PROBLEM_FLAGS[problemName])) {
problemItemTemplate.clone().html(i18n.getMessage(`reportProblemsDialog${problemName}`)).appendTo(problemDialogList); problemItemTemplate.clone().html(i18n.getMessage(`reportProblemsDialog${problemName}`)).appendTo(problemDialogList);
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, PROBLEM_ANALYTICS_EVENT, problemName);
return true; return true;
} }
@ -379,6 +410,9 @@ function checkReportProblems() {
problemItemTemplate.clone().html(i18n.getMessage(`reportProblemsDialog${problemName}`, problemItemTemplate.clone().html(i18n.getMessage(`reportProblemsDialog${problemName}`,
[CONFIGURATOR.latestVersion, CONFIGURATOR.latestVersionReleaseUrl, CONFIGURATOR.getDisplayVersion(), FC.CONFIG.flightControllerVersion])).appendTo(problemDialogList); [CONFIGURATOR.latestVersion, CONFIGURATOR.latestVersionReleaseUrl, CONFIGURATOR.getDisplayVersion(), FC.CONFIG.flightControllerVersion])).appendTo(problemDialogList);
needsProblemReportingDialog = true; needsProblemReportingDialog = true;
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, PROBLEM_ANALYTICS_EVENT,
`${problemName};${CONFIGURATOR.API_VERSION_MAX_SUPPORTED};${FC.CONFIG.apiVersion}`);
} }
needsProblemReportingDialog = checkReportProblem('MOTOR_PROTOCOL_DISABLED', problemDialogList) || needsProblemReportingDialog; needsProblemReportingDialog = checkReportProblem('MOTOR_PROTOCOL_DISABLED', problemDialogList) || needsProblemReportingDialog;
@ -406,6 +440,8 @@ function processUid() {
MSP.send_message(MSPCodes.MSP_UID, false, false, function () { MSP.send_message(MSPCodes.MSP_UID, false, false, function () {
const deviceIdentifier = FC.CONFIG.deviceIdentifier; const deviceIdentifier = FC.CONFIG.deviceIdentifier;
analytics.setFlightControllerData(analytics.DATA.MCU_ID, objectHash.sha1(deviceIdentifier));
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Connected');
connectionTimestamp = Date.now(); connectionTimestamp = Date.now();
GUI.log(i18n.getMessage('uniqueDeviceIdReceived', [deviceIdentifier])); GUI.log(i18n.getMessage('uniqueDeviceIdReceived', [deviceIdentifier]));

View File

@ -54,6 +54,7 @@ function getCliCommand(command, cliBuffer) {
function copyToClipboard(text) { function copyToClipboard(text) {
function onCopySuccessful() { function onCopySuccessful() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'CliCopyToClipboard', text.length);
const button = TABS.cli.GUI.copyButton; const button = TABS.cli.GUI.copyButton;
const origText = button.text(); const origText = button.text();
const origWidth = button.css("width"); const origWidth = button.css("width");
@ -176,6 +177,8 @@ cli.initialize = function (callback) {
if (self.outputHistory.length > 0 && 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');
} }
}; };
@ -219,6 +222,8 @@ cli.initialize = function (callback) {
function executeSnippet(fileName) { function executeSnippet(fileName) {
const commands = previewArea.val(); const commands = previewArea.val();
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'CliExecuteFromFile', fileName);
executeCommands(commands); executeCommands(commands);
self.GUI.snippetPreviewWindow.close(); self.GUI.snippetPreviewWindow.close();
} }

View File

@ -3,10 +3,12 @@ import { i18n } from '../localization';
import GUI from '../gui'; import GUI from '../gui';
const configuration = { const configuration = {
// intended analyticsChanges: {},
}; };
configuration.initialize = function (callback) { configuration.initialize = function (callback) {
const self = this;
if (GUI.active_tab != 'configuration') { if (GUI.active_tab != 'configuration') {
GUI.active_tab = 'configuration'; GUI.active_tab = 'configuration';
GUI.configuration_loaded = true; GUI.configuration_loaded = true;
@ -48,6 +50,8 @@ configuration.initialize = function (callback) {
load_serial_config(); load_serial_config();
function process_html() { function process_html() {
self.analyticsChanges = {};
const features_e = $('.tab-configuration .features'); const features_e = $('.tab-configuration .features');
FC.FEATURE_CONFIG.features.generateElements(features_e); FC.FEATURE_CONFIG.features.generateElements(features_e);
@ -126,15 +130,39 @@ configuration.initialize = function (callback) {
orientation_mag_e.val(FC.SENSOR_ALIGNMENT.align_mag); orientation_mag_e.val(FC.SENSOR_ALIGNMENT.align_mag);
orientation_gyro_e.change(function () { orientation_gyro_e.change(function () {
FC.SENSOR_ALIGNMENT.align_gyro = parseInt($(this).val()); let value = parseInt($(this).val());
let newValue = undefined;
if (value !== FC.SENSOR_ALIGNMENT.align_gyro) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['GyroAlignment'] = newValue;
FC.SENSOR_ALIGNMENT.align_gyro = value;
}); });
orientation_acc_e.change(function () { orientation_acc_e.change(function () {
FC.SENSOR_ALIGNMENT.align_acc = parseInt($(this).val()); let value = parseInt($(this).val());
let newValue = undefined;
if (value !== FC.SENSOR_ALIGNMENT.align_acc) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['AccAlignment'] = newValue;
FC.SENSOR_ALIGNMENT.align_acc = value;
}); });
orientation_mag_e.change(function () { orientation_mag_e.change(function () {
FC.SENSOR_ALIGNMENT.align_mag = parseInt($(this).val()); let value = parseInt($(this).val());
let newValue = undefined;
if (value !== FC.SENSOR_ALIGNMENT.align_mag) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['MagAlignment'] = newValue;
FC.SENSOR_ALIGNMENT.align_mag = value;
}); });
// Multi gyro config // Multi gyro config
@ -178,11 +206,27 @@ configuration.initialize = function (callback) {
$('.gyro_alignment_inputs_notfound').toggle(!detected_gyro_1 && !detected_gyro_2); $('.gyro_alignment_inputs_notfound').toggle(!detected_gyro_1 && !detected_gyro_2);
orientation_gyro_1_align_e.change(function () { orientation_gyro_1_align_e.change(function () {
FC.SENSOR_ALIGNMENT.gyro_1_align = parseInt($(this).val()); let value = parseInt($(this).val());
let newValue = undefined;
if (value !== FC.SENSOR_ALIGNMENT.gyro_1_align) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['Gyro1Alignment'] = newValue;
FC.SENSOR_ALIGNMENT.gyro_1_align = value;
}); });
orientation_gyro_2_align_e.change(function () { orientation_gyro_2_align_e.change(function () {
FC.SENSOR_ALIGNMENT.gyro_2_align = parseInt($(this).val()); let value = parseInt($(this).val());
let newValue = undefined;
if (value !== FC.SENSOR_ALIGNMENT.gyro_2_align) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['Gyro2Alignment'] = newValue;
FC.SENSOR_ALIGNMENT.gyro_2_align = value;
}); });
// Gyro and PID update // Gyro and PID update
@ -446,10 +490,20 @@ configuration.initialize = function (callback) {
const value = parseInt(pidSelectElement.val()); const value = parseInt(pidSelectElement.val());
if (value !== FC.PID_ADVANCED_CONFIG.pid_process_denom) {
const newFrequency = pidSelectElement.find('option:selected').text();
self.analyticsChanges['PIDLoopSettings'] = `denominator: ${value} | frequency: ${newFrequency}`;
} else {
self.analyticsChanges['PIDLoopSettings'] = undefined;
}
FC.PID_ADVANCED_CONFIG.pid_process_denom = value; FC.PID_ADVANCED_CONFIG.pid_process_denom = value;
FC.RX_CONFIG.fpvCamAngleDegrees = parseInt($('input[name="fpvCamAngleDegrees"]').val()); FC.RX_CONFIG.fpvCamAngleDegrees = parseInt($('input[name="fpvCamAngleDegrees"]').val());
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'configuration');
self.analyticsChanges = {};
// fill some data // fill some data
FC.GPS_CONFIG.auto_baud = $('input[name="gps_auto_baud"]').is(':checked') ? 1 : 0; FC.GPS_CONFIG.auto_baud = $('input[name="gps_auto_baud"]').is(':checked') ? 1 : 0;
FC.GPS_CONFIG.auto_config = $('input[name="gps_auto_config"]').is(':checked') ? 1 : 0; FC.GPS_CONFIG.auto_config = $('input[name="gps_auto_config"]').is(':checked') ? 1 : 0;

View File

@ -113,6 +113,7 @@ firmware_flasher.initialize = function (callback) {
self.parsed_hex = data; self.parsed_hex = data;
if (self.parsed_hex) { if (self.parsed_hex) {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SIZE, self.parsed_hex.bytes_total);
showLoadedHex(key); showLoadedHex(key);
} else { } else {
self.flashingMessage(i18n.getMessage('firmwareFlasherHexCorrupted'), self.FLASH_MESSAGE_TYPES.INVALID); self.flashingMessage(i18n.getMessage('firmwareFlasherHexCorrupted'), self.FLASH_MESSAGE_TYPES.INVALID);
@ -245,6 +246,8 @@ firmware_flasher.initialize = function (callback) {
i18n.localizePage(); i18n.localizePage();
buildType_e.change(function() { buildType_e.change(function() {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHANNEL, $('option:selected', this).text());
$("a.load_remote_file").addClass('disabled'); $("a.load_remote_file").addClass('disabled');
const build_type = $(this).val(); const build_type = $(this).val();
@ -463,9 +466,13 @@ firmware_flasher.initialize = function (callback) {
function flashFirmware(firmware) { function flashFirmware(firmware) {
const options = {}; const options = {};
let 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 (!$('option:selected', portPickerElement).data().isDFU) { if (!$('option:selected', portPickerElement).data().isDFU) {
if (String(portPickerElement.val()) !== '0') { if (String(portPickerElement.val()) !== '0') {
@ -482,12 +489,16 @@ firmware_flasher.initialize = function (callback) {
baud = parseInt($('#flash_manual_baud_rate').val()); baud = parseInt($('#flash_manual_baud_rate').val());
} }
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLASHING, 'Flashing', self.fileName || null);
STM32.connect(port, baud, firmware, options); STM32.connect(port, baud, firmware, options);
} else { } else {
console.log('Please select valid serial port'); console.log('Please select valid serial port');
GUI.log(i18n.getMessage('firmwareFlasherNoValidPort')); GUI.log(i18n.getMessage('firmwareFlasherNoValidPort'));
} }
} else { } else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLASHING, 'Flashing', self.fileName || null);
STM32DFU.connect(usbDevices, firmware, options); STM32DFU.connect(usbDevices, firmware, options);
} }
} }
@ -690,6 +701,9 @@ firmware_flasher.initialize = function (callback) {
self.enableFlashing(false); self.enableFlashing(false);
self.developmentFirmwareLoaded = false; self.developmentFirmwareLoaded = false;
analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHANNEL, undefined);
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SOURCE, 'file');
chrome.fileSystem.chooseEntry({ chrome.fileSystem.chooseEntry({
type: 'openFile', type: 'openFile',
accepts: [ accepts: [
@ -711,6 +725,7 @@ 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);
const reader = new FileReader(); const reader = new FileReader();
reader.onloadend = function(e) { reader.onloadend = function(e) {
@ -724,6 +739,7 @@ firmware_flasher.initialize = function (callback) {
self.parsed_hex = data; self.parsed_hex = data;
if (self.parsed_hex) { if (self.parsed_hex) {
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SIZE, self.parsed_hex.bytes_total);
self.localFirmwareLoaded = true; self.localFirmwareLoaded = true;
showLoadedHex(file.name); showLoadedHex(file.name);
@ -770,6 +786,8 @@ firmware_flasher.initialize = function (callback) {
self.localFirmwareLoaded = false; self.localFirmwareLoaded = false;
self.developmentFirmwareLoaded = buildTypesToShow[$('select[name="build_type"]').val()].tag === 'firmwareFlasherOptionLabelBuildTypeDevelopment'; self.developmentFirmwareLoaded = buildTypesToShow[$('select[name="build_type"]').val()].tag === 'firmwareFlasherOptionLabelBuildTypeDevelopment';
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'));
return; return;
@ -856,7 +874,10 @@ firmware_flasher.initialize = function (callback) {
return; return;
} }
analytics.setFirmwareData(analytics.DATA.FIRMWARE_NAME, response.file);
updateStatus('Pending', response.key, 0, false); updateStatus('Pending', response.key, 0, false);
let retries = 1; let retries = 1;
self.releaseLoader.requestBuildStatus(response.key, (statusResponse) => { self.releaseLoader.requestBuildStatus(response.key, (statusResponse) => {
if (statusResponse.status !== "queued") { if (statusResponse.status !== "queued") {
@ -900,6 +921,7 @@ firmware_flasher.initialize = function (callback) {
if (!exitDfuElement.hasClass('disabled')) { if (!exitDfuElement.hasClass('disabled')) {
exitDfuElement.addClass("disabled"); exitDfuElement.addClass("disabled");
if (!GUI.connect_lock) { // button disabled while flashing is in progress if (!GUI.connect_lock) { // button disabled while flashing is in progress
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLASHING, 'ExitDfu', null);
try { try {
console.log('Closing DFU'); console.log('Closing DFU');
STM32DFU.connect(usbDevices, self.parsed_hex, { exitDfu: true }); STM32DFU.connect(usbDevices, self.parsed_hex, { exitDfu: true });
@ -1053,7 +1075,11 @@ firmware_flasher.initialize = function (callback) {
// onwriteend will be fired again when truncation is finished // onwriteend will be fired again when truncation is finished
truncated = true; truncated = true;
writer.truncate(blob.size); writer.truncate(blob.size);
return;
} }
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLASHING, 'SaveFirmware', path);
}; };
writer.write(blob); writer.write(blob);
@ -1131,6 +1157,8 @@ firmware_flasher.cleanup = function (callback) {
$('div#flashbutton a.flash_state').removeClass('active'); $('div#flashbutton a.flash_state').removeClass('active');
$('div#flashbutton a.flash').removeClass('active'); $('div#flashbutton a.flash').removeClass('active');
analytics.resetFirmwareData();
if (callback) callback(); if (callback) callback();
}; };

View File

@ -9,6 +9,7 @@ const motors = {
previousDshotBidir: null, previousDshotBidir: null,
previousFilterDynQ: null, previousFilterDynQ: null,
previousFilterDynCount: null, previousFilterDynCount: null,
analyticsChanges: {},
configHasChanged: false, configHasChanged: false,
configChanges: {}, configChanges: {},
feature3DEnabled: false, feature3DEnabled: false,
@ -236,6 +237,7 @@ motors.initialize = async function (callback) {
self.feature3DEnabled = FC.FEATURE_CONFIG.features.isEnabled('3D'); self.feature3DEnabled = FC.FEATURE_CONFIG.features.isEnabled('3D');
const motorsEnableTestModeElement = $('#motorsEnableTestMode'); const motorsEnableTestModeElement = $('#motorsEnableTestMode');
self.analyticsChanges = {};
motorsEnableTestModeElement.prop('checked', false).trigger('change'); motorsEnableTestModeElement.prop('checked', false).trigger('change');
@ -357,8 +359,15 @@ motors.initialize = async function (callback) {
reverseMotorSwitchElement.prop('checked', FC.MIXER_CONFIG.reverseMotorDir !== 0).change(); reverseMotorSwitchElement.prop('checked', FC.MIXER_CONFIG.reverseMotorDir !== 0).change();
mixerListElement.change(function () { mixerListElement.change(function () {
FC.MIXER_CONFIG.mixer = parseInt($(this).val()); const mixerValue = parseInt($(this).val());
let newValue;
if (mixerValue !== FC.MIXER_CONFIG.mixer) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['Mixer'] = newValue;
FC.MIXER_CONFIG.mixer = mixerValue;
refreshMixerPreview(); refreshMixerPreview();
}); });
@ -679,7 +688,10 @@ motors.initialize = async function (callback) {
self.previousFilterDynCount = FC.FILTER_CONFIG.dyn_notch_count; self.previousFilterDynCount = FC.FILTER_CONFIG.dyn_notch_count;
dshotBidirElement.on("change", function () { dshotBidirElement.on("change", function () {
FC.MOTOR_CONFIG.use_dshot_telemetry = dshotBidirElement.is(':checked'); const value = dshotBidirElement.is(':checked');
const newValue = (value !== FC.MOTOR_CONFIG.use_dshot_telemetry) ? 'On' : 'Off';
self.analyticsChanges['BidirectionalDshot'] = newValue;
FC.MOTOR_CONFIG.use_dshot_telemetry = value;
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) { if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
const rpmFilterIsDisabled = FC.FILTER_CONFIG.gyro_rpm_notch_harmonics === 0; const rpmFilterIsDisabled = FC.FILTER_CONFIG.gyro_rpm_notch_harmonics === 0;
@ -773,7 +785,17 @@ motors.initialize = async function (callback) {
escProtocolElement.val(FC.PID_ADVANCED_CONFIG.fast_pwm_protocol + 1); escProtocolElement.val(FC.PID_ADVANCED_CONFIG.fast_pwm_protocol + 1);
escProtocolElement.on("change", () => updateVisibility()).trigger("change"); escProtocolElement.on("change", function () {
const escProtocolValue = parseInt($(this).val()) - 1;
let newValue = undefined;
if (escProtocolValue !== FC.PID_ADVANCED_CONFIG.fast_pwm_protocol) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['EscProtocol'] = newValue;
updateVisibility();
}).trigger("change");
//trigger change dshotBidir and ESC_SENSOR to show/hide Motor Poles tab //trigger change dshotBidir and ESC_SENSOR to show/hide Motor Poles tab
dshotBidirElement.change(updateVisibility).trigger("change"); dshotBidirElement.change(updateVisibility).trigger("change");
@ -1120,6 +1142,8 @@ motors.initialize = async function (callback) {
} }
await MSP.promise(MSPCodes.MSP_EEPROM_WRITE); await MSP.promise(MSPCodes.MSP_EEPROM_WRITE);
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'motors');
self.analyticsChanges = {};
self.configHasChanged = false; self.configHasChanged = false;
reboot(); reboot();

View File

@ -137,6 +137,8 @@ onboard_logging.initialize = function (callback) {
.toggleClass("msc-supported", true); .toggleClass("msc-supported", true);
$('a.onboardLoggingRebootMsc').click(function () { $('a.onboardLoggingRebootMsc').click(function () {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'RebootMsc');
const buffer = []; const buffer = [];
if (GUI.operating_system === "Linux") { if (GUI.operating_system === "Linux") {
// Reboot into MSC using UTC time offset instead of user timezone // Reboot into MSC using UTC time offset instead of user timezone
@ -404,26 +406,38 @@ onboard_logging.initialize = function (callback) {
$('a.onboardLoggingRebootMsc').removeClass('disabled'); $('a.onboardLoggingRebootMsc').removeClass('disabled');
} }
let loggingStatus;
switch (FC.SDCARD.state) { switch (FC.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',[FC.SDCARD.state])); $(".sdcard-status").text(i18n.getMessage('sdcardStatusUnknown',[FC.SDCARD.state]));
} }
if (dataflashPresent && FC.SDCARD.state === MSP.SDCARD_STATE_NOT_PRESENT) {
loggingStatus = 'Dataflash';
analytics.setFlightControllerData(analytics.DATA.LOG_SIZE, FC.DATAFLASH.usedSize);
}
analytics.setFlightControllerData(analytics.DATA.LOGGING_STATUS, loggingStatus);
if (FC.SDCARD.supported && !sdcardTimer) { if (FC.SDCARD.supported && !sdcardTimer) {
// Poll for changes in SD card status // Poll for changes in SD card status
sdcardTimer = setTimeout(function() { sdcardTimer = setTimeout(function() {
@ -455,8 +469,9 @@ onboard_logging.initialize = function (callback) {
} }
function mark_saving_dialog_done(startTime, totalBytes, totalBytesCompressed) { function mark_saving_dialog_done(startTime, totalBytes, totalBytesCompressed) {
const totalTime = (new Date().getTime() - startTime) / 1000; analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'SaveDataflash');
const 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}.`);
if (!isNaN(totalBytesCompressed)) { if (!isNaN(totalBytesCompressed)) {
@ -616,6 +631,9 @@ onboard_logging.initialize = function (callback) {
}; };
onboard_logging.cleanup = function (callback) { 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

@ -14,6 +14,7 @@ options.initialize = function (callback) {
TABS.options.initPermanentExpertMode(); TABS.options.initPermanentExpertMode();
TABS.options.initRememberLastTab(); TABS.options.initRememberLastTab();
TABS.options.initCheckForConfiguratorUnstableVersions(); TABS.options.initCheckForConfiguratorUnstableVersions();
TABS.options.initAnalyticsOptOut();
TABS.options.initCliAutoComplete(); TABS.options.initCliAutoComplete();
TABS.options.initShowAllSerialDevices(); TABS.options.initShowAllSerialDevices();
TABS.options.initShowVirtualMode(); TABS.options.initShowVirtualMode();
@ -82,6 +83,31 @@ options.initCheckForConfiguratorUnstableVersions = function () {
}); });
}; };
options.initAnalyticsOptOut = function () {
const result = ConfigStorage.get('analyticsOptOut');
if (result.analyticsOptOut) {
$('div.analyticsOptOut input').prop('checked', true);
}
$('div.analyticsOptOut input').change(function () {
const checked = $(this).is(':checked');
ConfigStorage.set({'analyticsOptOut': checked});
checkSetupAnalytics(function (analyticsService) {
if (checked) {
analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, 'OptOut');
}
analyticsService.setOptOut(checked);
if (!checked) {
analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, 'OptIn');
}
});
}).change();
};
options.initCliAutoComplete = function () { options.initCliAutoComplete = function () {
$('div.cliAutoComplete input') $('div.cliAutoComplete input')
.prop('checked', CliAutoComplete.configEnabled) .prop('checked', CliAutoComplete.configEnabled)

View File

@ -2561,7 +2561,7 @@ OSD.GUI.preview = {
}; };
const osd = { const osd = {
// intentional analyticsChanges: {},
}; };
osd.initialize = function(callback) { osd.initialize = function(callback) {
@ -2839,6 +2839,11 @@ osd.initialize = function(callback) {
fieldChanged.enabled = !fieldChanged.enabled; fieldChanged.enabled = !fieldChanged.enabled;
if (self.analyticsChanges[`OSDStatistic${fieldChanged.name}`] === undefined) {
self.analyticsChanges[`OSDStatistic${fieldChanged.name}`] = 0;
}
self.analyticsChanges[`OSDStatistic${fieldChanged.name}`] += fieldChanged.enabled ? 1 : -1;
MSP.promise(MSPCodes.MSP_SET_OSD_CONFIG, OSD.msp.encodeStatistics(fieldChanged)) MSP.promise(MSPCodes.MSP_SET_OSD_CONFIG, OSD.msp.encodeStatistics(fieldChanged))
.then(updateOsdView); .then(updateOsdView);
}), }),
@ -2876,6 +2881,11 @@ osd.initialize = function(callback) {
const fieldChanged = $(this).data('field'); const fieldChanged = $(this).data('field');
fieldChanged.enabled = !fieldChanged.enabled; fieldChanged.enabled = !fieldChanged.enabled;
if (self.analyticsChanges[`OSDWarning${fieldChanged.name}`] === undefined) {
self.analyticsChanges[`OSDWarning${fieldChanged.name}`] = 0;
}
self.analyticsChanges[`OSDWarning${fieldChanged.name}`] += fieldChanged.enabled ? 1 : -1;
MSP.promise(MSPCodes.MSP_SET_OSD_CONFIG, OSD.msp.encodeOther()) MSP.promise(MSPCodes.MSP_SET_OSD_CONFIG, OSD.msp.encodeOther())
.then(updateOsdView); .then(updateOsdView);
}), }),
@ -2993,6 +3003,11 @@ osd.initialize = function(callback) {
const $position = $(this).parent().find(`.position.${fieldChanged.name}`); const $position = $(this).parent().find(`.position.${fieldChanged.name}`);
fieldChanged.isVisible[profile] = !fieldChanged.isVisible[profile]; fieldChanged.isVisible[profile] = !fieldChanged.isVisible[profile];
if (self.analyticsChanges[`OSDElement${fieldChanged.name}`] === undefined) {
self.analyticsChanges[`OSDElement${fieldChanged.name}`] = 0;
}
self.analyticsChanges[`OSDElement${fieldChanged.name}`] += fieldChanged.isVisible[profile] ? 1 : -1;
if (fieldChanged.isVisible[OSD.getCurrentPreviewProfile()]) { if (fieldChanged.isVisible[OSD.getCurrentPreviewProfile()]) {
$position.show(); $position.show();
} else { } else {
@ -3232,6 +3247,20 @@ osd.initialize = function(callback) {
setTimeout(() => { setTimeout(() => {
$(this).html(oldText); $(this).html(oldText);
}, 1500); }, 1500);
Object.keys(self.analyticsChanges).forEach(function(change) {
const value = self.analyticsChanges[change];
if (value > 0) {
self.analyticsChanges[change] = 'On';
} else if (value < 0) {
self.analyticsChanges[change] = 'Off';
} else {
self.analyticsChanges[change] = undefined;
}
});
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'osd');
self.analyticsChanges = {};
}); });
// font preview window // font preview window
@ -3346,6 +3375,8 @@ osd.initialize = function(callback) {
} }
}); });
self.analyticsChanges = {};
MSP.promise(MSPCodes.MSP_RX_CONFIG) MSP.promise(MSPCodes.MSP_RX_CONFIG)
.finally(() => { .finally(() => {
GUI.content_ready(callback); GUI.content_ready(callback);

View File

@ -17,6 +17,7 @@ const pid_tuning = {
SETPOINT_WEIGHT_RANGE_HIGH: 20, SETPOINT_WEIGHT_RANGE_HIGH: 20,
SETPOINT_WEIGHT_RANGE_LEGACY: 2.54, SETPOINT_WEIGHT_RANGE_LEGACY: 2.54,
activeSubtab: 'pid', activeSubtab: 'pid',
analyticsChanges: {},
CONFIGURATOR_PIDS: [], CONFIGURATOR_PIDS: [],
CONFIGURATOR_ADVANCED_TUNING: {}, CONFIGURATOR_ADVANCED_TUNING: {},
@ -1111,7 +1112,15 @@ pid_tuning.initialize = function (callback) {
FC.FILTER_CONFIG.yaw_lowpass_hz = parseInt($('.pid_filter input[name="yawLowpassFrequency"]').val()); FC.FILTER_CONFIG.yaw_lowpass_hz = parseInt($('.pid_filter input[name="yawLowpassFrequency"]').val());
if (vbatpidcompensationIsUsed) { if (vbatpidcompensationIsUsed) {
FC.ADVANCED_TUNING.vbatPidCompensation = $('input[id="vbatpidcompensation"]').is(':checked') ? 1 : 0; const element = $('input[id="vbatpidcompensation"]');
const value = element.is(':checked') ? 1 : 0;
let analyticsValue = undefined;
if (value !== FC.ADVANCED_TUNING.vbatPidCompensation) {
analyticsValue = element.is(':checked');
}
self.analyticsChanges['VbatPidCompensation'] = analyticsValue;
FC.ADVANCED_TUNING.vbatPidCompensation = value;
} }
FC.ADVANCED_TUNING.deltaMethod = $('#pid-tuning .delta select').val(); FC.ADVANCED_TUNING.deltaMethod = $('#pid-tuning .delta select').val();
@ -1206,7 +1215,15 @@ pid_tuning.initialize = function (callback) {
FC.ADVANCED_TUNING.motorOutputLimit = parseInt($('.tab-pid_tuning input[name="motorLimit"]').val()); FC.ADVANCED_TUNING.motorOutputLimit = parseInt($('.tab-pid_tuning input[name="motorLimit"]').val());
FC.ADVANCED_TUNING.autoProfileCellCount = parseInt($('.tab-pid_tuning select[name="cellCount"]').val()); FC.ADVANCED_TUNING.autoProfileCellCount = parseInt($('.tab-pid_tuning select[name="cellCount"]').val());
FC.ADVANCED_TUNING.idleMinRpm = parseInt($('input[name="idleMinRpm-number"]').val()); FC.ADVANCED_TUNING.idleMinRpm = parseInt($('input[name="idleMinRpm-number"]').val());
FC.RC_TUNING.rates_type = $('select[id="ratesType"]').val();
const selectedRatesType = $('select[id="ratesType"]').val(); // send analytics for rates type
let selectedRatesTypeName = undefined;
if (selectedRatesType !== FC.RC_TUNING.rates_type) {
selectedRatesTypeName = $('select[id="ratesType"]').find('option:selected').text();
}
self.analyticsChanges['RatesType'] = selectedRatesTypeName;
FC.RC_TUNING.rates_type = selectedRatesType;
} }
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) { if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
@ -1993,6 +2010,7 @@ pid_tuning.initialize = function (callback) {
} }
self.calculateNewPids(); self.calculateNewPids();
self.analyticsChanges['PidTuningSliders'] = "On";
}); });
// reset to middle with double click // reset to middle with double click
@ -2084,9 +2102,11 @@ pid_tuning.initialize = function (callback) {
if (slider.is('#sliderGyroFilterMultiplier')) { if (slider.is('#sliderGyroFilterMultiplier')) {
TuningSliders.sliderGyroFilterMultiplier = sliderValue; TuningSliders.sliderGyroFilterMultiplier = sliderValue;
self.calculateNewGyroFilters(); self.calculateNewGyroFilters();
self.analyticsChanges['GyroFilterTuningSlider'] = "On";
} else if (slider.is('#sliderDTermFilterMultiplier')) { } else if (slider.is('#sliderDTermFilterMultiplier')) {
TuningSliders.sliderDTermFilterMultiplier = sliderValue; TuningSliders.sliderDTermFilterMultiplier = sliderValue;
self.calculateNewDTermFilters(); self.calculateNewDTermFilters();
self.analyticsChanges['DTermFilterTuningSlider'] = "On";
} }
}); });
@ -2115,6 +2135,13 @@ pid_tuning.initialize = function (callback) {
} else { } else {
TuningSliders.updateFilterSlidersDisplay(); TuningSliders.updateFilterSlidersDisplay();
} }
if (TuningSliders.GyroSliderUnavailable) {
self.analyticsChanges['GyroFilterTuningSlider'] = "Off";
}
if (TuningSliders.DTermSliderUnavailable) {
self.analyticsChanges['DTermFilterTuningSlider'] = "Off";
}
}); });
// update on filter switch changes // update on filter switch changes
@ -2161,6 +2188,8 @@ pid_tuning.initialize = function (callback) {
if ($('input[id="useIntegratedYaw"]').is(':checked')) { if ($('input[id="useIntegratedYaw"]').is(':checked')) {
$('input[id="useIntegratedYaw"]').prop('checked', true).click(); $('input[id="useIntegratedYaw"]').prop('checked', true).click();
} }
self.analyticsChanges['PidTuningSliders'] = "On";
}); });
// enable Filter sliders button (legacy sliders) // enable Filter sliders button (legacy sliders)
@ -2171,18 +2200,23 @@ pid_tuning.initialize = function (callback) {
$('input[id="gyroLowpassEnabled"]').prop('checked', true).click(); $('input[id="gyroLowpassEnabled"]').prop('checked', true).click();
$('input[id="gyroLowpass2Enabled"]').prop('checked', false).click(); $('input[id="gyroLowpass2Enabled"]').prop('checked', false).click();
TuningSliders.resetGyroFilterSlider(); TuningSliders.resetGyroFilterSlider();
self.analyticsChanges['GyroFilterTuningSlider'] = "On";
} }
if (TuningSliders.DTermSliderUnavailable) { if (TuningSliders.DTermSliderUnavailable) {
$('input[id="dtermLowpassDynEnabled"]').prop('checked', false).click(); $('input[id="dtermLowpassDynEnabled"]').prop('checked', false).click();
$('input[id="dtermLowpassEnabled"]').prop('checked', true).click(); $('input[id="dtermLowpassEnabled"]').prop('checked', true).click();
$('input[id="dtermLowpass2Enabled"]').prop('checked', false).click(); $('input[id="dtermLowpass2Enabled"]').prop('checked', false).click();
TuningSliders.resetDTermFilterSlider(); TuningSliders.resetDTermFilterSlider();
self.analyticsChanges['DTermFilterTuningSlider'] = "On";
} }
}); });
// update on pid table inputs // update on pid table inputs
$('#pid_main input').on('input', function() { $('#pid_main input').on('input', function() {
TuningSliders.updatePidSlidersDisplay(); TuningSliders.updatePidSlidersDisplay();
self.analyticsChanges['PidTuningSliders'] = "Off";
}); });
} }
@ -2228,6 +2262,9 @@ pid_tuning.initialize = function (callback) {
self.refresh(); self.refresh();
}); });
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'pid_tuning');
self.analyticsChanges = {};
}); });
// Setup model for rates preview // Setup model for rates preview
@ -2244,6 +2281,8 @@ pid_tuning.initialize = function (callback) {
MSP.send_message(MSPCodes.MSP_STATUS_EX, false, false, self.checkUpdateProfile(true)); MSP.send_message(MSPCodes.MSP_STATUS_EX, false, false, self.checkUpdateProfile(true));
}, 500, true); }, 500, true);
self.analyticsChanges = {};
GUI.content_ready(callback); GUI.content_ready(callback);
TABS.pid_tuning.isHtmlProcessing = false; TABS.pid_tuning.isHtmlProcessing = false;
} }

View File

@ -3,10 +3,12 @@ import { i18n } from "../localization";
import GUI from '../gui'; import GUI from '../gui';
const ports = { const ports = {
// intentional analyticsChanges: {},
}; };
ports.initialize = function (callback) { ports.initialize = function (callback) {
const self = this;
let board_definition = {}; let board_definition = {};
const functionRules = [ const functionRules = [
@ -118,6 +120,8 @@ ports.initialize = function (callback) {
} }
function update_ui() { function update_ui() {
self.analyticsChanges = {};
$(".tab-ports").addClass("supported"); $(".tab-ports").addClass("supported");
const VCP_PORT_IDENTIFIER = 20; const VCP_PORT_IDENTIFIER = 20;
@ -258,6 +262,19 @@ ports.initialize = function (callback) {
lastMspSelected = functionName; lastMspSelected = functionName;
} }
} }
if (column === 'telemetry') {
const initialValue = functionName;
selectElement.on('change', function () {
const telemetryValue = $(this).val();
let newValue;
if (telemetryValue !== initialValue) {
newValue = $(this).find('option:selected').text();
}
self.analyticsChanges['Telemetry'] = newValue;
});
}
} }
} }
} }
@ -303,10 +320,14 @@ ports.initialize = function (callback) {
}); });
if (lastVtxControlSelected !== vtxControlSelected) { if (lastVtxControlSelected !== vtxControlSelected) {
self.analyticsChanges['VtxControl'] = vtxControlSelected;
lastVtxControlSelected = vtxControlSelected; lastVtxControlSelected = vtxControlSelected;
} }
if (lastMspSelected !== mspControlSelected) { if (lastMspSelected !== mspControlSelected) {
self.analyticsChanges['MspControl'] = mspControlSelected;
lastMspSelected = mspControlSelected; lastMspSelected = mspControlSelected;
} }
@ -335,6 +356,9 @@ ports.initialize = function (callback) {
} }
function on_save_handler() { function on_save_handler() {
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'ports');
self.analyticsChanges = {};
// update configuration based on current ui state // update configuration based on current ui state
FC.SERIAL_CONFIG.ports = []; FC.SERIAL_CONFIG.ports = [];

View File

@ -3,10 +3,17 @@ import GUI from '../gui';
const power = { const power = {
supported: false, supported: false,
analyticsChanges: {},
}; };
power.initialize = function (callback) { power.initialize = function (callback) {
const self = this;
if (GUI.active_tab != 'power') {
GUI.active_tab = 'power'; GUI.active_tab = 'power';
// Disabled on merge into betaflight-configurator
//googleAnalytics.sendAppView('Power');
}
if (GUI.calibrationManager) { if (GUI.calibrationManager) {
GUI.calibrationManager.destroy(); GUI.calibrationManager.destroy();
@ -105,6 +112,14 @@ power.initialize = function (callback) {
$(`input[name="vbatresdivmultiplier-${index}"]`).val(voltageDataSource[index].vbatresdivmultiplier); $(`input[name="vbatresdivmultiplier-${index}"]`).val(voltageDataSource[index].vbatresdivmultiplier);
} }
$('input[name="vbatscale-0"]').change(function () {
const value = parseInt($(this).val());
if (value !== voltageDataSource[0].vbatscale) {
self.analyticsChanges['PowerVBatUpdated'] = value;
}
});
// amperage meters // amperage meters
if (FC.BATTERY_CONFIG.currentMeterSource == 0) { if (FC.BATTERY_CONFIG.currentMeterSource == 0) {
$('.boxAmperageConfiguration').hide(); $('.boxAmperageConfiguration').hide();
@ -155,6 +170,26 @@ power.initialize = function (callback) {
$(`input[name="amperageoffset-${index}"]`).val(currentDataSource[index].offset); $(`input[name="amperageoffset-${index}"]`).val(currentDataSource[index].offset);
} }
$('input[name="amperagescale-0"]').change(function () {
if (FC.BATTERY_CONFIG.currentMeterSource === 1) {
let value = parseInt($(this).val());
if (value !== currentDataSource[0].scale) {
self.analyticsChanges['PowerAmperageUpdated'] = value;
}
}
});
$('input[name="amperagescale-1"]').change(function () {
if (FC.BATTERY_CONFIG.currentMeterSource === 2) {
let value = parseInt($(this).val());
if (value !== currentDataSource[1].scale) {
self.analyticsChanges['PowerAmperageUpdated'] = value;
}
}
});
if(FC.BATTERY_CONFIG.voltageMeterSource == 1 || FC.BATTERY_CONFIG.currentMeterSource == 1 || FC.BATTERY_CONFIG.currentMeterSource == 2) { if(FC.BATTERY_CONFIG.voltageMeterSource == 1 || FC.BATTERY_CONFIG.currentMeterSource == 1 || FC.BATTERY_CONFIG.currentMeterSource == 2) {
$('.calibration').show(); $('.calibration').show();
} else { } else {
@ -399,6 +434,14 @@ power.initialize = function (callback) {
$('output[name="amperagenewscale"').val(amperagenewscale); $('output[name="amperagenewscale"').val(amperagenewscale);
$('a.applycalibration').click(function() { $('a.applycalibration').click(function() {
if (vbatscalechanged) {
self.analyticsChanges['PowerVBatUpdated'] = 'Calibrated';
}
if (amperagescalechanged) {
self.analyticsChanges['PowerAmperageUpdated'] = 'Calibrated';
}
calibrationconfirmed = true; calibrationconfirmed = true;
GUI.calibrationManagerConfirmation.close(); GUI.calibrationManagerConfirmation.close();
updateDisplay(FC.VOLTAGE_METER_CONFIGS, FC.CURRENT_METER_CONFIGS); updateDisplay(FC.VOLTAGE_METER_CONFIGS, FC.CURRENT_METER_CONFIGS);
@ -430,6 +473,8 @@ power.initialize = function (callback) {
FC.BATTERY_CONFIG.vbatwarningcellvoltage = parseFloat($('input[name="warningcellvoltage"]').val()); FC.BATTERY_CONFIG.vbatwarningcellvoltage = parseFloat($('input[name="warningcellvoltage"]').val());
FC.BATTERY_CONFIG.capacity = parseInt($('input[name="capacity"]').val()); FC.BATTERY_CONFIG.capacity = parseInt($('input[name="capacity"]').val());
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'power');
save_power_config(); save_power_config();
}); });
@ -463,6 +508,7 @@ power.initialize = function (callback) {
} }
function process_html() { function process_html() {
self.analyticsChanges = {};
// translate to user-selected language // translate to user-selected language
i18n.localizePage(); i18n.localizePage();

View File

@ -6,6 +6,7 @@ import CryptoES from 'crypto-es';
const receiver = { const receiver = {
rateChartHeight: 117, rateChartHeight: 117,
analyticsChanges: {},
needReboot: false, needReboot: false,
elrsPassphraseEnabled: false, elrsPassphraseEnabled: false,
}; };
@ -75,6 +76,8 @@ receiver.initialize = function (callback) {
MSP.send_message(MSPCodes.MSP_FEATURE_CONFIG, false, false, get_rc_data); MSP.send_message(MSPCodes.MSP_FEATURE_CONFIG, false, false, get_rc_data);
function process_html() { function process_html() {
self.analyticsChanges = {};
const featuresElement = $('.tab-receiver .features'); const featuresElement = $('.tab-receiver .features');
FC.FEATURE_CONFIG.features.generateElements(featuresElement); FC.FEATURE_CONFIG.features.generateElements(featuresElement);
@ -240,9 +243,12 @@ receiver.initialize = function (callback) {
serialRxSelectElement.change(function () { serialRxSelectElement.change(function () {
const serialRxValue = parseInt($(this).val()); const serialRxValue = parseInt($(this).val());
let newValue;
if (serialRxValue !== FC.RX_CONFIG.serialrx_provider) { if (serialRxValue !== FC.RX_CONFIG.serialrx_provider) {
newValue = $(this).find('option:selected').text();
updateSaveButton(true); updateSaveButton(true);
} }
tab.analyticsChanges['SerialRx'] = newValue;
FC.RX_CONFIG.serialrx_provider = serialRxValue; FC.RX_CONFIG.serialrx_provider = serialRxValue;
}); });
@ -296,9 +302,12 @@ receiver.initialize = function (callback) {
spiRxElement.change(function () { spiRxElement.change(function () {
const value = parseInt($(this).val()); const value = parseInt($(this).val());
let newValue = undefined;
if (value !== FC.RX_CONFIG.rxSpiProtocol) { if (value !== FC.RX_CONFIG.rxSpiProtocol) {
newValue = $(this).find('option:selected').text();
updateSaveButton(true); updateSaveButton(true);
} }
tab.analyticsChanges['SPIRXProtocol'] = newValue;
FC.RX_CONFIG.rxSpiProtocol = value; FC.RX_CONFIG.rxSpiProtocol = value;
}); });
@ -484,6 +493,9 @@ receiver.initialize = function (callback) {
} }
} }
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, tab.analyticsChanges, 'receiver');
tab.analyticsChanges = {};
MSP.send_message(MSPCodes.MSP_SET_RX_MAP, mspHelper.crunch(MSPCodes.MSP_SET_RX_MAP), false, save_rssi_config); MSP.send_message(MSPCodes.MSP_SET_RX_MAP, mspHelper.crunch(MSPCodes.MSP_SET_RX_MAP), false, save_rssi_config);
} }

View File

@ -8,7 +8,9 @@ const setup = {
setup.initialize = function (callback) { setup.initialize = function (callback) {
const self = this; const self = this;
if (GUI.active_tab != 'setup') {
GUI.active_tab = 'setup'; GUI.active_tab = 'setup';
}
function load_status() { function load_status() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_mixer_config); MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_mixer_config);

View File

@ -6,7 +6,11 @@ const setup_osd = {
setup_osd.initialize = function (callback) { setup_osd.initialize = function (callback) {
if (GUI.active_tab != 'setup_osd') {
GUI.active_tab = 'setup_osd'; GUI.active_tab = 'setup_osd';
// Disabled on merge into betaflight-configurator
//googleAnalytics.sendAppView('Setup OSD');
}
function load_status() { function load_status() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_html); MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_html);

View File

@ -110,6 +110,8 @@ transponder.initialize = function(callback) {
///////////////////////////////////////////// /////////////////////////////////////////////
GUI.active_tab = 'transponder'; GUI.active_tab = 'transponder';
// Disabled on merge into betaflight-configurator
//googleAnalytics.sendAppView('Transponder');
// transponder supported added in MSP API Version 1.16.0 // transponder supported added in MSP API Version 1.16.0
if (FC.CONFIG) { if (FC.CONFIG) {

View File

@ -11,6 +11,7 @@ const vtx = {
MAX_BAND_CHANNELS_VALUES: 8, MAX_BAND_CHANNELS_VALUES: 8,
VTXTABLE_BAND_LIST: [], VTXTABLE_BAND_LIST: [],
VTXTABLE_POWERLEVEL_LIST: [], VTXTABLE_POWERLEVEL_LIST: [],
analyticsChanges: {},
updating: true, updating: true,
env: new djv(), env: new djv(),
get _DEVICE_STATUS_UPDATE_INTERVAL_NAME() { get _DEVICE_STATUS_UPDATE_INTERVAL_NAME() {
@ -55,7 +56,9 @@ vtx.initialize = function (callback) {
GUI.active_tab = 'vtx'; GUI.active_tab = 'vtx';
} }
self.supported = semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_42); self.analyticsChanges = {};
this.supported = semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_42);
if (!this.supported) { if (!this.supported) {
load_html(); load_html();
@ -401,6 +404,13 @@ vtx.initialize = function (callback) {
} }
$("#vtx_table_channels").on('input', showHideBandChannels).trigger('input'); $("#vtx_table_channels").on('input', showHideBandChannels).trigger('input');
$("#vtx_table").change(function() {
let fromScratch = true;
if (self.analyticsChanges['VtxTableLoadFromClipboard'] !== undefined || self.analyticsChanges['VtxTableLoadFromFile'] !== undefined) {
fromScratch = false;
}
self.analyticsChanges['VtxTableEdit'] = fromScratch ? 'modificationOnly' : 'fromTemplate';
});
/*** Helper functions */ /*** Helper functions */
@ -636,6 +646,7 @@ vtx.initialize = function (callback) {
// we get here at the end of the truncate method, change to the new end // we get here at the end of the truncate method, change to the new end
writer.onwriteend = function() { writer.onwriteend = function() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'VtxTableLuaSave', text.length);
console.log('Write VTX table lua file end'); console.log('Write VTX table lua file end');
GUI.log(i18n.getMessage('vtxSavedLuaFileOk')); GUI.log(i18n.getMessage('vtxSavedLuaFileOk'));
}; };
@ -686,6 +697,7 @@ vtx.initialize = function (callback) {
// we get here at the end of the truncate method, change to the new end // we get here at the end of the truncate method, change to the new end
writer.onwriteend = function() { writer.onwriteend = function() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'VtxTableSave', text.length);
console.log(vtxConfig); console.log(vtxConfig);
console.log('Write VTX file end'); console.log('Write VTX file end');
GUI.log(i18n.getMessage('vtxSavedFileOk')); GUI.log(i18n.getMessage('vtxSavedFileOk'));
@ -736,6 +748,9 @@ vtx.initialize = function (callback) {
TABS.vtx.vtxTableSavePending = true; TABS.vtx.vtxTableSavePending = true;
self.analyticsChanges['VtxTableLoadFromClipboard'] = undefined;
self.analyticsChanges['VtxTableLoadFromFile'] = file.name;
console.log('Load VTX file end'); console.log('Load VTX file end');
GUI.log(i18n.getMessage('vtxLoadFileOk')); GUI.log(i18n.getMessage('vtxLoadFileOk'));
}, },
@ -783,6 +798,9 @@ vtx.initialize = function (callback) {
TABS.vtx.vtxTableSavePending = true; TABS.vtx.vtxTableSavePending = true;
self.analyticsChanges['VtxTableLoadFromFile'] = undefined;
self.analyticsChanges['VtxTableLoadFromClipboard'] = text.length;
console.log('Load VTX clipboard end'); console.log('Load VTX clipboard end');
GUI.log(i18n.getMessage('vtxLoadClipboardOk')); GUI.log(i18n.getMessage('vtxLoadClipboardOk'));
}, },
@ -816,6 +834,8 @@ vtx.initialize = function (callback) {
// Start MSP saving // Start MSP saving
save_vtx_config(); save_vtx_config();
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'vtx');
function save_vtx_config() { function save_vtx_config() {
MSP.send_message(MSPCodes.MSP_SET_VTX_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_VTX_CONFIG), false, save_vtx_powerlevels); MSP.send_message(MSPCodes.MSP_SET_VTX_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_VTX_CONFIG), false, save_vtx_powerlevels);
} }

View File

@ -60,6 +60,7 @@
<script type="text/javascript" src="./node_modules/lru_map/lru.js"></script> <script type="text/javascript" src="./node_modules/lru_map/lru.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/dompurify/dist/purify.min.js"></script> <script type="text/javascript" src="./node_modules/dompurify/dist/purify.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/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="./node_modules/object-hash/dist/object_hash.js"></script>
<script type="text/javascript" src="./node_modules/jquery/dist/jquery.min.js"></script> <script type="text/javascript" src="./node_modules/jquery/dist/jquery.min.js"></script>
@ -77,6 +78,7 @@
<script type="text/javascript" src="./node_modules/bluebird/js/browser/bluebird.min.js"></script> <script type="text/javascript" src="./node_modules/bluebird/js/browser/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="./node_modules/inflection/lib/inflection.js"></script> <script type="text/javascript" src="./node_modules/inflection/lib/inflection.js"></script>
<script type="text/javascript" src="./js/libraries/analytics.js"></script>
<script type="text/javascript" src="./js/utils/window_watchers.js"></script> <script type="text/javascript" src="./js/utils/window_watchers.js"></script>
<script type="text/javascript" src="./js/utils/VtxDeviceStatus/VtxDeviceStatusFactory.js"></script> <script type="text/javascript" src="./js/utils/VtxDeviceStatus/VtxDeviceStatusFactory.js"></script>
<script type="text/javascript" src="./js/utils/VtxDeviceStatus/VtxDeviceStatus.js"></script> <script type="text/javascript" src="./js/utils/VtxDeviceStatus/VtxDeviceStatus.js"></script>
@ -106,6 +108,7 @@
<script type="text/javascript" src="./js/RateCurve.js"></script> <script type="text/javascript" src="./js/RateCurve.js"></script>
<script type="text/javascript" src="./js/Features.js"></script> <script type="text/javascript" src="./js/Features.js"></script>
<script type="text/javascript" src="./js/Beepers.js"></script> <script type="text/javascript" src="./js/Beepers.js"></script>
<script type="text/javascript" src="./js/Analytics.js"></script>
<script type="text/javascript" src="./js/GitHubApi.js"></script> <script type="text/javascript" src="./js/GitHubApi.js"></script>
<script type="module" src="./js/main.js"></script> <script type="module" src="./js/main.js"></script>
<script type="text/javascript" src="./js/LogoManager.js"></script> <script type="text/javascript" src="./js/LogoManager.js"></script>

View File

@ -46,6 +46,9 @@
<div class="socialMediaText" i18n="defaultDiscordText"></div> <div class="socialMediaText" i18n="defaultDiscordText"></div>
</div> </div>
</div> </div>
<div class="content_bottom">
<div class="statsCollection" i18n="statisticsDisclaimer"></div>
</div>
<div class="content_foot"> <div class="content_foot">
<div class="languageSwitcher"></div> <div class="languageSwitcher"></div>
</div> </div>

View File

@ -23,6 +23,12 @@
</div> </div>
<span class="freelabel" i18n="rememberLastTab"></span> <span class="freelabel" i18n="rememberLastTab"></span>
</div> </div>
<div class="analyticsOptOut margin-bottom">
<div>
<input type="checkbox" class="toggle" />
</div>
<span class="freelabel" i18n="analyticsOptOut"></span>
</div>
<div class="cliAutoComplete margin-bottom"> <div class="cliAutoComplete margin-bottom">
<div> <div>
<input type="checkbox" class="toggle" /> <input type="checkbox" class="toggle" />