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": {
"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": {
"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) {
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'CliAutoComplete', this.configEnabled);
this.$textarea = $textarea;
this.forceOpen = false;
this.sendLine = sendLine;

View File

@ -39,14 +39,23 @@ const Features = function (config) {
self._features = features;
self._featureMask = 0;
self._analyticsChanges = {};
};
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) {
this._featureMask = featureMask;
const self = this;
self._featureMask = featureMask;
};
Features.prototype.isEnabled = function (featureName) {
@ -159,25 +168,33 @@ Features.prototype.updateData = function (featureElement) {
if (featureElement.attr('type') === 'checkbox') {
const bit = featureElement.data('bit');
let featureValue;
if (featureElement.is(':checked')) {
self._featureMask = bit_set(self._featureMask, bit);
featureValue = 'On';
} else {
self._featureMask = bit_clear(self._featureMask, bit);
featureValue = 'Off';
}
self._analyticsChanges[`Feature${self.findFeatureByBit(bit).name}`] = featureValue;
} else if (featureElement.prop('localName') === 'select') {
const controlElements = featureElement.children();
const selectedBit = featureElement.val();
if (selectedBit !== -1) {
let selectedFeature;
for (const controlElement of controlElements) {
const bit = controlElement.value;
if (selectedBit === bit) {
self._featureMask = bit_set(self._featureMask, bit);
selectedFeature = self.findFeatureByBit(bit);
} else {
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;
}
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Backup');
console.log('Write SUCCESSFUL');
if (callback) callback();
};
@ -307,6 +308,8 @@ function configuration_restore(callback) {
configuration.FEATURE_CONFIG.features = features;
}
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'Restore');
configuration_upload(configuration, callback);
} else {
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();
});
}
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');
$('.firmware_b a.flash').removeClass('disabled');
});
}
function closeSerial() {
@ -134,6 +187,8 @@ function closeHandler() {
this.hide();
}
analytics.sendEvent(analytics.EVENT_CATEGORIES.APPLICATION, 'AppClose', { sessionControl: 'end' });
closeSerial();
if (!GUI.isCordova()) {
@ -268,6 +323,10 @@ function startProcess() {
GUI.tab_switch_in_progress = false;
}
checkSetupAnalytics(function (analyticsService) {
analyticsService.sendAppView(tab);
});
switch (tab) {
case 'landing':
import("./tabs/landing").then(({ landing }) =>
@ -524,6 +583,9 @@ function startProcess() {
$(expertModeCheckbox).on("change", () => {
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) {
updateTabList(FC.FEATURE_CONFIG.features);
@ -567,6 +629,10 @@ function startProcess() {
function setDarkTheme(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.
// once moved to modules extract to own module.
window.googleAnalytics = analytics;
window.analytics = null;
window.showErrorDialog = showErrorDialog;
window.generateFilename = generateFilename;
window.updateTabList = updateTabList;
@ -739,3 +807,4 @@ window.isExpertModeEnabled = isExpertModeEnabled;
window.checkForConfiguratorUpdates = checkForConfiguratorUpdates;
window.setDarkTheme = setDarkTheme;
window.appReady = appReady;
window.checkSetupAnalytics = checkSetupAnalytics;

View File

@ -157,9 +157,16 @@ function finishClose(finishedCallback) {
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
$('#dialogResetToCustomDefaults')[0].close();
analytics.resetFlightControllerData();
serial.disconnect(onClosed);
MSP.disconnect_cleanup();
@ -242,13 +249,18 @@ function onOpen(openInfo) {
console.log(`Requesting configuration data`);
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]));
if (semver.gte(FC.CONFIG.apiVersion, CONFIGURATOR.API_VERSION_ACCEPTED)) {
MSP.send_message(MSPCodes.MSP_FC_VARIANT, false, false, function () {
analytics.setFlightControllerData(analytics.DATA.FIRMWARE_TYPE, FC.CONFIG.flightControllerIdentifier);
if (FC.CONFIG.flightControllerIdentifier === 'BTFL') {
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]));
MSP.send_message(MSPCodes.MSP_BUILD_INFO, false, false, function () {
@ -259,6 +271,8 @@ function onOpen(openInfo) {
});
});
} else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'ConnectionRefusedFirmwareType', FC.CONFIG.flightControllerIdentifier);
const dialog = $('.dialogConnectWarning')[0];
$('.dialogConnectWarning-content').html(i18n.getMessage('firmwareTypeNotSupported'));
@ -273,6 +287,8 @@ function onOpen(openInfo) {
}
});
} else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'ConnectionRefusedFirmwareVersion', FC.CONFIG.apiVersion);
const dialog = $('.dialogConnectWarning')[0];
$('.dialogConnectWarning-content').html(i18n.getMessage('firmwareVersionNotSupported', [CONFIGURATOR.API_VERSION_ACCEPTED]));
@ -287,6 +303,8 @@ function onOpen(openInfo) {
}
});
} else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'SerialPortFailed');
console.log('Failed to open serial port');
GUI.log(i18n.getMessage('serialPortOpenFail'));
@ -323,12 +341,20 @@ function abortConnect() {
}
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]));
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];
$('#dialogResetToCustomDefaults-acceptbtn').click(function() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'AcceptResetToCustomDefaults');
const buffer = [];
buffer.push(mspHelper.RESET_TYPES.CUSTOM_DEFAULTS);
MSP.send_message(MSPCodes.MSP_RESET_CONF, buffer, false);
@ -341,6 +367,8 @@ function processBoardInfo() {
});
$('#dialogResetToCustomDefaults-cancelbtn').click(function() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'CancelResetToCustomDefaults');
dialog.close();
setConnectionTimeout();
@ -357,12 +385,15 @@ function processBoardInfo() {
}
function checkReportProblems() {
const PROBLEM_ANALYTICS_EVENT = 'ProblemFound';
const problemItemTemplate = $('#dialogReportProblems-listItemTemplate');
function checkReportProblem(problemName, problemDialogList) {
if (bit_check(FC.CONFIG.configurationProblems, FC.CONFIGURATION_PROBLEM_FLAGS[problemName])) {
problemItemTemplate.clone().html(i18n.getMessage(`reportProblemsDialog${problemName}`)).appendTo(problemDialogList);
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, PROBLEM_ANALYTICS_EVENT, problemName);
return true;
}
@ -379,6 +410,9 @@ function checkReportProblems() {
problemItemTemplate.clone().html(i18n.getMessage(`reportProblemsDialog${problemName}`,
[CONFIGURATOR.latestVersion, CONFIGURATOR.latestVersionReleaseUrl, CONFIGURATOR.getDisplayVersion(), FC.CONFIG.flightControllerVersion])).appendTo(problemDialogList);
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;
@ -406,6 +440,8 @@ function processUid() {
MSP.send_message(MSPCodes.MSP_UID, false, false, function () {
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();
GUI.log(i18n.getMessage('uniqueDeviceIdReceived', [deviceIdentifier]));

View File

@ -54,6 +54,7 @@ function getCliCommand(command, cliBuffer) {
function copyToClipboard(text) {
function onCopySuccessful() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'CliCopyToClipboard', text.length);
const button = TABS.cli.GUI.copyButton;
const origText = button.text();
const origWidth = button.css("width");
@ -176,6 +177,8 @@ cli.initialize = function (callback) {
if (self.outputHistory.length > 0 && writer.length === 0) {
writer.write(new Blob([self.outputHistory], {type: 'text/plain'}));
} else {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'CliSave', self.outputHistory.length);
console.log('write complete');
}
};
@ -219,6 +222,8 @@ cli.initialize = function (callback) {
function executeSnippet(fileName) {
const commands = previewArea.val();
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'CliExecuteFromFile', fileName);
executeCommands(commands);
self.GUI.snippetPreviewWindow.close();
}

View File

@ -3,10 +3,12 @@ import { i18n } from '../localization';
import GUI from '../gui';
const configuration = {
// intended
analyticsChanges: {},
};
configuration.initialize = function (callback) {
const self = this;
if (GUI.active_tab != 'configuration') {
GUI.active_tab = 'configuration';
GUI.configuration_loaded = true;
@ -48,6 +50,8 @@ configuration.initialize = function (callback) {
load_serial_config();
function process_html() {
self.analyticsChanges = {};
const features_e = $('.tab-configuration .features');
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_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 () {
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 () {
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
@ -178,11 +206,27 @@ configuration.initialize = function (callback) {
$('.gyro_alignment_inputs_notfound').toggle(!detected_gyro_1 && !detected_gyro_2);
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 () {
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
@ -446,10 +490,20 @@ configuration.initialize = function (callback) {
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.RX_CONFIG.fpvCamAngleDegrees = parseInt($('input[name="fpvCamAngleDegrees"]').val());
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'configuration');
self.analyticsChanges = {};
// fill some data
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;

View File

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

View File

@ -9,6 +9,7 @@ const motors = {
previousDshotBidir: null,
previousFilterDynQ: null,
previousFilterDynCount: null,
analyticsChanges: {},
configHasChanged: false,
configChanges: {},
feature3DEnabled: false,
@ -236,6 +237,7 @@ motors.initialize = async function (callback) {
self.feature3DEnabled = FC.FEATURE_CONFIG.features.isEnabled('3D');
const motorsEnableTestModeElement = $('#motorsEnableTestMode');
self.analyticsChanges = {};
motorsEnableTestModeElement.prop('checked', false).trigger('change');
@ -357,8 +359,15 @@ motors.initialize = async function (callback) {
reverseMotorSwitchElement.prop('checked', FC.MIXER_CONFIG.reverseMotorDir !== 0).change();
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();
});
@ -679,7 +688,10 @@ motors.initialize = async function (callback) {
self.previousFilterDynCount = FC.FILTER_CONFIG.dyn_notch_count;
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)) {
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.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
dshotBidirElement.change(updateVisibility).trigger("change");
@ -1120,6 +1142,8 @@ motors.initialize = async function (callback) {
}
await MSP.promise(MSPCodes.MSP_EEPROM_WRITE);
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'motors');
self.analyticsChanges = {};
self.configHasChanged = false;
reboot();

View File

@ -137,6 +137,8 @@ onboard_logging.initialize = function (callback) {
.toggleClass("msc-supported", true);
$('a.onboardLoggingRebootMsc').click(function () {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'RebootMsc');
const buffer = [];
if (GUI.operating_system === "Linux") {
// Reboot into MSC using UTC time offset instead of user timezone
@ -404,26 +406,38 @@ onboard_logging.initialize = function (callback) {
$('a.onboardLoggingRebootMsc').removeClass('disabled');
}
let loggingStatus;
switch (FC.SDCARD.state) {
case MSP.SDCARD_STATE_NOT_PRESENT:
$(".sdcard-status").text(i18n.getMessage('sdcardStatusNoCard'));
loggingStatus = 'SdCard: NotPresent';
break;
case MSP.SDCARD_STATE_FATAL:
$(".sdcard-status").html(i18n.getMessage('sdcardStatusReboot'));
loggingStatus = 'SdCard: Error';
break;
case MSP.SDCARD_STATE_READY:
$(".sdcard-status").text(i18n.getMessage('sdcardStatusReady'));
loggingStatus = 'SdCard: Ready';
break;
case MSP.SDCARD_STATE_CARD_INIT:
$(".sdcard-status").text(i18n.getMessage('sdcardStatusStarting'));
loggingStatus = 'SdCard: Init';
break;
case MSP.SDCARD_STATE_FS_INIT:
$(".sdcard-status").text(i18n.getMessage('sdcardStatusFileSystem'));
loggingStatus = 'SdCard: FsInit';
break;
default:
$(".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) {
// Poll for changes in SD card status
sdcardTimer = setTimeout(function() {
@ -455,8 +469,9 @@ onboard_logging.initialize = function (callback) {
}
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 (${
(totalBytes / totalTime / 1024).toFixed(2)}kB / s) with block size ${self.blockSize}.`);
if (!isNaN(totalBytesCompressed)) {
@ -616,6 +631,9 @@ onboard_logging.initialize = function (callback) {
};
onboard_logging.cleanup = function (callback) {
analytics.setFlightControllerData(analytics.DATA.LOGGING_STATUS, undefined);
analytics.setFlightControllerData(analytics.DATA.LOG_SIZE, undefined);
if (sdcardTimer) {
clearTimeout(sdcardTimer);
sdcardTimer = false;

View File

@ -14,6 +14,7 @@ options.initialize = function (callback) {
TABS.options.initPermanentExpertMode();
TABS.options.initRememberLastTab();
TABS.options.initCheckForConfiguratorUnstableVersions();
TABS.options.initAnalyticsOptOut();
TABS.options.initCliAutoComplete();
TABS.options.initShowAllSerialDevices();
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 () {
$('div.cliAutoComplete input')
.prop('checked', CliAutoComplete.configEnabled)

View File

@ -2561,7 +2561,7 @@ OSD.GUI.preview = {
};
const osd = {
// intentional
analyticsChanges: {},
};
osd.initialize = function(callback) {
@ -2839,6 +2839,11 @@ osd.initialize = function(callback) {
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))
.then(updateOsdView);
}),
@ -2876,6 +2881,11 @@ osd.initialize = function(callback) {
const fieldChanged = $(this).data('field');
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())
.then(updateOsdView);
}),
@ -2993,6 +3003,11 @@ osd.initialize = function(callback) {
const $position = $(this).parent().find(`.position.${fieldChanged.name}`);
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()]) {
$position.show();
} else {
@ -3232,6 +3247,20 @@ osd.initialize = function(callback) {
setTimeout(() => {
$(this).html(oldText);
}, 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
@ -3346,6 +3375,8 @@ osd.initialize = function(callback) {
}
});
self.analyticsChanges = {};
MSP.promise(MSPCodes.MSP_RX_CONFIG)
.finally(() => {
GUI.content_ready(callback);

View File

@ -17,6 +17,7 @@ const pid_tuning = {
SETPOINT_WEIGHT_RANGE_HIGH: 20,
SETPOINT_WEIGHT_RANGE_LEGACY: 2.54,
activeSubtab: 'pid',
analyticsChanges: {},
CONFIGURATOR_PIDS: [],
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());
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();
@ -1206,7 +1215,15 @@ pid_tuning.initialize = function (callback) {
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.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)) {
@ -1993,6 +2010,7 @@ pid_tuning.initialize = function (callback) {
}
self.calculateNewPids();
self.analyticsChanges['PidTuningSliders'] = "On";
});
// reset to middle with double click
@ -2084,9 +2102,11 @@ pid_tuning.initialize = function (callback) {
if (slider.is('#sliderGyroFilterMultiplier')) {
TuningSliders.sliderGyroFilterMultiplier = sliderValue;
self.calculateNewGyroFilters();
self.analyticsChanges['GyroFilterTuningSlider'] = "On";
} else if (slider.is('#sliderDTermFilterMultiplier')) {
TuningSliders.sliderDTermFilterMultiplier = sliderValue;
self.calculateNewDTermFilters();
self.analyticsChanges['DTermFilterTuningSlider'] = "On";
}
});
@ -2115,6 +2135,13 @@ pid_tuning.initialize = function (callback) {
} else {
TuningSliders.updateFilterSlidersDisplay();
}
if (TuningSliders.GyroSliderUnavailable) {
self.analyticsChanges['GyroFilterTuningSlider'] = "Off";
}
if (TuningSliders.DTermSliderUnavailable) {
self.analyticsChanges['DTermFilterTuningSlider'] = "Off";
}
});
// update on filter switch changes
@ -2161,6 +2188,8 @@ pid_tuning.initialize = function (callback) {
if ($('input[id="useIntegratedYaw"]').is(':checked')) {
$('input[id="useIntegratedYaw"]').prop('checked', true).click();
}
self.analyticsChanges['PidTuningSliders'] = "On";
});
// enable Filter sliders button (legacy sliders)
@ -2171,18 +2200,23 @@ pid_tuning.initialize = function (callback) {
$('input[id="gyroLowpassEnabled"]').prop('checked', true).click();
$('input[id="gyroLowpass2Enabled"]').prop('checked', false).click();
TuningSliders.resetGyroFilterSlider();
self.analyticsChanges['GyroFilterTuningSlider'] = "On";
}
if (TuningSliders.DTermSliderUnavailable) {
$('input[id="dtermLowpassDynEnabled"]').prop('checked', false).click();
$('input[id="dtermLowpassEnabled"]').prop('checked', true).click();
$('input[id="dtermLowpass2Enabled"]').prop('checked', false).click();
TuningSliders.resetDTermFilterSlider();
self.analyticsChanges['DTermFilterTuningSlider'] = "On";
}
});
// update on pid table inputs
$('#pid_main input').on('input', function() {
TuningSliders.updatePidSlidersDisplay();
self.analyticsChanges['PidTuningSliders'] = "Off";
});
}
@ -2228,6 +2262,9 @@ pid_tuning.initialize = function (callback) {
self.refresh();
});
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'pid_tuning');
self.analyticsChanges = {};
});
// 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));
}, 500, true);
self.analyticsChanges = {};
GUI.content_ready(callback);
TABS.pid_tuning.isHtmlProcessing = false;
}

View File

@ -3,10 +3,12 @@ import { i18n } from "../localization";
import GUI from '../gui';
const ports = {
// intentional
analyticsChanges: {},
};
ports.initialize = function (callback) {
const self = this;
let board_definition = {};
const functionRules = [
@ -118,6 +120,8 @@ ports.initialize = function (callback) {
}
function update_ui() {
self.analyticsChanges = {};
$(".tab-ports").addClass("supported");
const VCP_PORT_IDENTIFIER = 20;
@ -258,6 +262,19 @@ ports.initialize = function (callback) {
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) {
self.analyticsChanges['VtxControl'] = vtxControlSelected;
lastVtxControlSelected = vtxControlSelected;
}
if (lastMspSelected !== mspControlSelected) {
self.analyticsChanges['MspControl'] = mspControlSelected;
lastMspSelected = mspControlSelected;
}
@ -335,6 +356,9 @@ ports.initialize = function (callback) {
}
function on_save_handler() {
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'ports');
self.analyticsChanges = {};
// update configuration based on current ui state
FC.SERIAL_CONFIG.ports = [];

View File

@ -3,10 +3,17 @@ import GUI from '../gui';
const power = {
supported: false,
analyticsChanges: {},
};
power.initialize = function (callback) {
const self = this;
if (GUI.active_tab != 'power') {
GUI.active_tab = 'power';
// Disabled on merge into betaflight-configurator
//googleAnalytics.sendAppView('Power');
}
if (GUI.calibrationManager) {
GUI.calibrationManager.destroy();
@ -105,6 +112,14 @@ power.initialize = function (callback) {
$(`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
if (FC.BATTERY_CONFIG.currentMeterSource == 0) {
$('.boxAmperageConfiguration').hide();
@ -155,6 +170,26 @@ power.initialize = function (callback) {
$(`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) {
$('.calibration').show();
} else {
@ -399,6 +434,14 @@ power.initialize = function (callback) {
$('output[name="amperagenewscale"').val(amperagenewscale);
$('a.applycalibration').click(function() {
if (vbatscalechanged) {
self.analyticsChanges['PowerVBatUpdated'] = 'Calibrated';
}
if (amperagescalechanged) {
self.analyticsChanges['PowerAmperageUpdated'] = 'Calibrated';
}
calibrationconfirmed = true;
GUI.calibrationManagerConfirmation.close();
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.capacity = parseInt($('input[name="capacity"]').val());
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'power');
save_power_config();
});
@ -463,6 +508,7 @@ power.initialize = function (callback) {
}
function process_html() {
self.analyticsChanges = {};
// translate to user-selected language
i18n.localizePage();

View File

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

View File

@ -8,7 +8,9 @@ const setup = {
setup.initialize = function (callback) {
const self = this;
if (GUI.active_tab != 'setup') {
GUI.active_tab = 'setup';
}
function load_status() {
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) {
if (GUI.active_tab != 'setup_osd') {
GUI.active_tab = 'setup_osd';
// Disabled on merge into betaflight-configurator
//googleAnalytics.sendAppView('Setup OSD');
}
function load_status() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_html);

View File

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

View File

@ -11,6 +11,7 @@ const vtx = {
MAX_BAND_CHANNELS_VALUES: 8,
VTXTABLE_BAND_LIST: [],
VTXTABLE_POWERLEVEL_LIST: [],
analyticsChanges: {},
updating: true,
env: new djv(),
get _DEVICE_STATUS_UPDATE_INTERVAL_NAME() {
@ -55,7 +56,9 @@ vtx.initialize = function (callback) {
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) {
load_html();
@ -401,6 +404,13 @@ vtx.initialize = function (callback) {
}
$("#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 */
@ -636,6 +646,7 @@ vtx.initialize = function (callback) {
// we get here at the end of the truncate method, change to the new end
writer.onwriteend = function() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'VtxTableLuaSave', text.length);
console.log('Write VTX table lua file end');
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
writer.onwriteend = function() {
analytics.sendEvent(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, 'VtxTableSave', text.length);
console.log(vtxConfig);
console.log('Write VTX file end');
GUI.log(i18n.getMessage('vtxSavedFileOk'));
@ -736,6 +748,9 @@ vtx.initialize = function (callback) {
TABS.vtx.vtxTableSavePending = true;
self.analyticsChanges['VtxTableLoadFromClipboard'] = undefined;
self.analyticsChanges['VtxTableLoadFromFile'] = file.name;
console.log('Load VTX file end');
GUI.log(i18n.getMessage('vtxLoadFileOk'));
},
@ -783,6 +798,9 @@ vtx.initialize = function (callback) {
TABS.vtx.vtxTableSavePending = true;
self.analyticsChanges['VtxTableLoadFromFile'] = undefined;
self.analyticsChanges['VtxTableLoadFromClipboard'] = text.length;
console.log('Load VTX clipboard end');
GUI.log(i18n.getMessage('vtxLoadClipboardOk'));
},
@ -816,6 +834,8 @@ vtx.initialize = function (callback) {
// Start MSP saving
save_vtx_config();
analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges, 'vtx');
function save_vtx_config() {
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/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/universal-ga/lib/analytics.min.js"></script>
<script type="text/javascript" src="./node_modules/short-unique-id/dist/short-unique-id.min.js"></script>
<script type="text/javascript" src="./node_modules/object-hash/dist/object_hash.js"></script>
<script type="text/javascript" src="./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="./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="./js/libraries/analytics.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/VtxDeviceStatus.js"></script>
@ -106,6 +108,7 @@
<script type="text/javascript" src="./js/RateCurve.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/Analytics.js"></script>
<script type="text/javascript" src="./js/GitHubApi.js"></script>
<script type="module" src="./js/main.js"></script>
<script type="text/javascript" src="./js/LogoManager.js"></script>

View File

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

View File

@ -23,6 +23,12 @@
</div>
<span class="freelabel" i18n="rememberLastTab"></span>
</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>
<input type="checkbox" class="toggle" />