diff --git a/libraries/google-analytics-bundle.js b/libraries/google-analytics-bundle.js new file mode 100644 index 00000000..26b3c00d --- /dev/null +++ b/libraries/google-analytics-bundle.js @@ -0,0 +1,95 @@ +(function() { 'use strict';var h,aa=aa||{},k=this,m=function(a){return void 0!==a},ba=function(){},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&& +"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},n=function(a){return"array"==ca(a)},da=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},p=function(a){return"string"==typeof a},ea=function(a){return"number"==typeof a},q=function(a){return"function"==ca(a)},r=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},fa= +function(a,b,c){return a.call.apply(a.bind,arguments)},ga=function(a,b,c){if(!a)throw Error();if(2b?1:0};var la=Array.prototype.indexOf?function(a,b,c){return Array.prototype.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(p(a))return p(b)&&1==b.length?a.indexOf(b,c):-1;for(;cb?null:p(a)?a.charAt(b):a[b]},ra=function(a, +b){var c=la(a,b),d;(d=0<=c)&&Array.prototype.splice.call(a,c,1);return d},sa=function(a){return Array.prototype.concat.apply(Array.prototype,arguments)},ta=function(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};var ua="StopIteration"in k?k.StopIteration:{message:"StopIteration",stack:""},va=function(){};va.prototype.next=function(){throw ua;};va.prototype.Yb=function(){return this};var wa=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},xa=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b},ya=function(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b},za=function(a){return null!==a&&"withCredentials"in a},Aa=function(a,b){var c;a:{for(c in a)if(b.call(void 0,a[c],c,a))break a;c=void 0}return c&&a[c]},Ba="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Ca=function(a,b){for(var c,d,e=1;e2*this.g&&Da(this),!0):!1};var Da=function(a){if(a.g!=a.b.length){for(var b=0,c=0;b=d.b.length)throw ua;var e=d.b[b++];return a?e:d.B[e]};return e};var A=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var Ea,Fa,Ga={id:"hitType",name:"t",valueType:"text",maxLength:void 0,defaultValue:void 0},Ha={id:"sessionControl",name:"sc",valueType:"text",maxLength:void 0,defaultValue:void 0},Ia={id:"description",name:"cd",valueType:"text",maxLength:2048,defaultValue:void 0},Ja={id:"eventCategory",name:"ec",valueType:"text",maxLength:150,defaultValue:void 0},Ka={id:"eventAction",name:"ea",valueType:"text",maxLength:500,defaultValue:void 0},La={id:"eventLabel",name:"el",valueType:"text",maxLength:500,defaultValue:void 0}, +Ma={id:"eventValue",name:"ev",valueType:"integer",maxLength:void 0,defaultValue:void 0},Na={Id:Ga,Xb:{id:"anonymizeIp",name:"aip",valueType:"boolean",maxLength:void 0,defaultValue:void 0},Td:{id:"queueTime",name:"qt",valueType:"integer",maxLength:void 0,defaultValue:void 0},pd:{id:"cacheBuster",name:"z",valueType:"text",maxLength:void 0,defaultValue:void 0},Zd:Ha,$d:{id:"sessionGroup",name:"sg",valueType:"text",maxLength:void 0,defaultValue:void 0},pe:{id:"userId",name:"uid",valueType:"text",maxLength:void 0, +defaultValue:void 0},Qd:{id:"nonInteraction",name:"ni",valueType:"boolean",maxLength:void 0,defaultValue:void 0},zd:Ia,ie:{id:"title",name:"dt",valueType:"text",maxLength:1500,defaultValue:void 0},ld:{id:"appId",name:"aid",valueType:"text",maxLength:150,defaultValue:void 0},md:{id:"appInstallerId",name:"aiid",valueType:"text",maxLength:150,defaultValue:void 0},Cd:Ja,Bd:Ka,Dd:La,Ed:Ma,be:{id:"socialNetwork",name:"sn",valueType:"text",maxLength:50,defaultValue:void 0},ae:{id:"socialAction",name:"sa", +valueType:"text",maxLength:50,defaultValue:void 0},ce:{id:"socialTarget",name:"st",valueType:"text",maxLength:2048,defaultValue:void 0},le:{id:"transactionId",name:"ti",valueType:"text",maxLength:500,defaultValue:void 0},ke:{id:"transactionAffiliation",name:"ta",valueType:"text",maxLength:500,defaultValue:void 0},me:{id:"transactionRevenue",name:"tr",valueType:"currency",maxLength:void 0,defaultValue:void 0},ne:{id:"transactionShipping",name:"ts",valueType:"currency",maxLength:void 0,defaultValue:void 0}, +oe:{id:"transactionTax",name:"tt",valueType:"currency",maxLength:void 0,defaultValue:void 0},xd:{id:"currencyCode",name:"cu",valueType:"text",maxLength:10,defaultValue:void 0},Md:{id:"itemPrice",name:"ip",valueType:"currency",maxLength:void 0,defaultValue:void 0},Nd:{id:"itemQuantity",name:"iq",valueType:"integer",maxLength:void 0,defaultValue:void 0},Kd:{id:"itemCode",name:"ic",valueType:"text",maxLength:500,defaultValue:void 0},Ld:{id:"itemName",name:"in",valueType:"text",maxLength:500,defaultValue:void 0}, +Jd:{id:"itemCategory",name:"iv",valueType:"text",maxLength:500,defaultValue:void 0},vd:{id:"campaignSource",name:"cs",valueType:"text",maxLength:100,defaultValue:void 0},td:{id:"campaignMedium",name:"cm",valueType:"text",maxLength:50,defaultValue:void 0},ud:{id:"campaignName",name:"cn",valueType:"text",maxLength:100,defaultValue:void 0},sd:{id:"campaignKeyword",name:"ck",valueType:"text",maxLength:500,defaultValue:void 0},qd:{id:"campaignContent",name:"cc",valueType:"text",maxLength:500,defaultValue:void 0}, +rd:{id:"campaignId",name:"ci",valueType:"text",maxLength:100,defaultValue:void 0},Hd:{id:"gclid",name:"gclid",valueType:"text",maxLength:void 0,defaultValue:void 0},yd:{id:"dclid",name:"dclid",valueType:"text",maxLength:void 0,defaultValue:void 0},Sd:{id:"pageLoadTime",name:"plt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Ad:{id:"dnsTime",name:"dns",valueType:"integer",maxLength:void 0,defaultValue:void 0},de:{id:"tcpConnectTime",name:"tcp",valueType:"integer",maxLength:void 0,defaultValue:void 0}, +Yd:{id:"serverResponseTime",name:"srt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Rd:{id:"pageDownloadTime",name:"pdt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Ud:{id:"redirectResponseTime",name:"rrt",valueType:"integer",maxLength:void 0,defaultValue:void 0},ee:{id:"timingCategory",name:"utc",valueType:"text",maxLength:150,defaultValue:void 0},he:{id:"timingVar",name:"utv",valueType:"text",maxLength:500,defaultValue:void 0},ge:{id:"timingValue",name:"utt",valueType:"integer", +maxLength:void 0,defaultValue:void 0},fe:{id:"timingLabel",name:"utl",valueType:"text",maxLength:500,defaultValue:void 0},Fd:{id:"exDescription",name:"exd",valueType:"text",maxLength:150,defaultValue:void 0},Gd:{id:"exFatal",name:"exf",valueType:"boolean",maxLength:void 0,defaultValue:"1"}},Oa=function(a){if(1>a||200a||200< +a)throw Error("Expected metric index range 1-200, but was : "+a);return{id:"metric"+a,name:"cm"+a,valueType:"integer",maxLength:void 0,defaultValue:void 0}};var Qa=function(a){if(1>a)return"0";if(3>a)return"1-2";a=Math.floor(Math.log(a-1)/Math.log(2));return Math.pow(2,a)+1+"-"+Math.pow(2,a+1)},Ra=function(a,b){for(var c=0,d=a.length-1,e=0;c<=d;){var f=Math.floor((c+d)/2),e=a[f];if(b<=e){d=0==f?0:a[f-1];if(b>d)return(d+1).toString()+"-"+e.toString();d=f-1}else if(b>e){if(f>=a.length-1)return(a[a.length-1]+1).toString()+"+";c=f+1}}return"<= 0"};var B=function(){this.mb=[]},Sa=function(){return new B};h=B.prototype;h.when=function(a){this.mb.push(a);return this};h.Wb=function(a){var b=arguments;this.when(function(a){return 0<=la(b,a.Ab())});return this};h.hd=function(a,b){var c=ta(arguments,1);this.when(function(b){b=b.ba().get(a);return 0<=la(c,b)});return this};h.sb=function(a,b){if(r(this.h))throw Error("Filter has already been set.");this.h=r(b)?t(a,b):a;return this}; +h.qa=function(){if(0==this.mb.length)throw Error("Must specify at least one predicate using #when or a helper method.");if(!r(this.h))throw Error("Must specify a delegate filter using #applyFilter.");return t(function(a){oa(this.mb,function(b){return b(a)})&&this.h(a)},this)};var C=function(){this.rb=!1;this.Fb="";this.Rb=!1;this.Ga=null};C.prototype.cc=function(a){this.rb=!0;this.Fb=a||" - ";return this};C.prototype.$c=function(){this.Rb=!0;return this};C.prototype.Kc=function(){return Ta(this,Qa)};C.prototype.Mc=function(a){return Ta(this,ha(Ra,a))}; +var Ta=function(a,b){if(null!=a.Ga)throw Error("LabelerBuilder: Only one labeling strategy may be used.");a.Ga=t(function(a){var d=a.ba().get(Ma),e=a.ba().get(La);ea(d)&&(d=b(d),null!=e&&this.rb&&(d=e+this.Fb+d),a.ba().set(La,d))},a);return a};C.prototype.qa=function(){if(null==this.Ga)throw Error("LabelerBuilder: a labeling strategy must be specified prior to calling build().");return Sa().Wb("event").sb(t(function(a){this.Ga(a);this.Rb&&a.ba().remove(Ma)},this)).qa()};var Ua=function(a,b){var c=Array.prototype.slice.call(arguments),d=c.shift();if("undefined"==typeof d)throw Error("[goog.string.format] Template required");return d.replace(/%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g,function(a,b,d,l,x,M,V,W){if("%"==M)return"%";var Pb=c.shift();if("undefined"==typeof Pb)throw Error("[goog.string.format] Not enough arguments");arguments[0]=Pb;return D[M].apply(null,arguments)})},D={s:function(a,b,c){return isNaN(c)||""==c||a.length>=Number(c)?a:a=-1Number(a)?"-":0<=b.indexOf("+")?"+":0<=b.indexOf(" ")?" ":"";0<=Number(a)&&(d=f+d);if(isNaN(c)||d.length>=Number(c))return d;d=isNaN(e)?Math.abs(Number(a)).toString():Math.abs(Number(a)).toFixed(e);a=Number(c)-d.length-f.length;return d=0<=b.indexOf("-",0)?f+d+ja(" ",a):f+ja(0<=b.indexOf("0",0)?"0":" ",a)+d},d:function(a,b,c,d,e,f,g,l){return D.f(parseInt(a, +10),b,c,d,0,f,g,l)}};D.i=D.d;D.u=D.d;var Va=function(a){if(a.v&&"function"==typeof a.v)return a.v();if(p(a))return a.split("");if(da(a)){for(var b=[],c=a.length,d=0;dparseFloat(hb)){gb=String(jb);break a}}gb=hb} +var kb=gb,lb={},L=function(a){var b;if(!(b=lb[a])){b=0;for(var c=ia(String(kb)).split("."),d=ia(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f=a.keyCode)a.keyCode=-1}catch(b){}};var ub="closure_listenable_"+(1E6*Math.random()|0),vb=function(a){return!(!a||!a[ub])},wb=0;var xb=function(a,b,c,d,e){this.listener=a;this.proxy=null;this.src=b;this.type=c;this.sa=!!d;this.Aa=e;this.key=++wb;this.removed=this.ra=!1},yb=function(a){a.removed=!0;a.listener=null;a.proxy=null;a.src=null;a.Aa=null};var N=function(a){this.src=a;this.m={};this.na=0};N.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.m[f];a||(a=this.m[f]=[],this.na++);var g=zb(a,b,d,e);-1e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(x){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,g=e.length-1;!c.ea&&0<=g;g--){c.currentTarget=e[g];var l=Qb(e[g],f,!0,c),d=d&&l}for(g=0;!c.ea&& +g>>0),Fb=function(a){if(q(a))return a;a[Rb]||(a[Rb]=function(b){return a.handleEvent(b)});return a[Rb]};var O=function(){G.call(this);this.J=new N(this);this.Zb=this;this.lb=null};w(O,G);O.prototype[ub]=!0;h=O.prototype;h.addEventListener=function(a,b,c,d){Eb(this,a,b,c,d)};h.removeEventListener=function(a,b,c,d){Mb(this,a,b,c,d)}; +h.dispatchEvent=function(a){var b,c=this.lb;if(c){b=[];for(var d=1;c;c=c.lb)b.push(c),++d}c=this.Zb;d=a.type||a;if(p(a))a=new H(a,c);else if(a instanceof H)a.target=a.target||c;else{var e=a;a=new H(d,c);Ca(a,e)}var e=!0,f;if(b)for(var g=b.length-1;!a.ea&&0<=g;g--)f=a.currentTarget=b[g],e=Sb(f,d,!0,a)&&e;a.ea||(f=a.currentTarget=c,e=Sb(f,d,!0,a)&&e,a.ea||(e=Sb(f,d,!1,a)&&e));if(b)for(g=0;!a.ea&&g=b.Wa&&b.cancel())}this.Ib?this.Ib.call(this.xb,this):this.nb=!0;this.K||this.I(new wc)}};Q.prototype.wb=function(a,b){this.Ua=!1;xc(this,a,b)}; +var xc=function(a,b,c){a.K=!0;a.M=c;a.ka=!b;yc(a)},Ac=function(a){if(a.K){if(!a.nb)throw new zc;a.nb=!1}};Q.prototype.G=function(a){Ac(this);xc(this,!0,a)};Q.prototype.I=function(a){Ac(this);xc(this,!1,a)};Q.prototype.w=function(a,b){return Bc(this,a,null,b)};var Bc=function(a,b,c,d){a.Ma.push([b,c,d]);a.K&&yc(a);return a};Q.prototype.then=function(a,b,c){var d,e,f=new P(function(a,b){d=a;e=b});Bc(this,d,function(a){a instanceof wc?f.cancel():e(a)});return f.then(a,b,c)};fc(Q); +var Cc=function(a){var b=new Q;Bc(a,b.G,b.I,b);return b},Dc=function(a){return na(a.Ma,function(a){return q(a[1])})},yc=function(a){if(a.Pa&&a.K&&Dc(a)){var b=a.Pa,c=Ec[b];c&&(k.clearTimeout(c.Ca),delete Ec[b]);a.Pa=0}a.o&&(a.o.Wa--,delete a.o);for(var b=a.M,d=c=!1;a.Ma.length&&!a.Ua;){var e=a.Ma.shift(),f=e[0],g=e[1],e=e[2];if(f=a.ka?g:f)try{var l=f.call(e||a.xb,b);m(l)&&(a.ka=a.ka&&(l==b||l instanceof Error),a.M=b=l);if(gc(b)||"function"===typeof k.Promise&&b instanceof k.Promise)d=!0,a.Ua=!0}catch(x){b= +x,a.ka=!0,Dc(a)||(c=!0)}}a.M=b;d&&(l=t(a.wb,a,!0),d=t(a.wb,a,!1),b instanceof Q?(Bc(b,l,d),b.dc=!0):b.then(l,d));c&&(b=new Fc(b),Ec[b.Ca]=b,a.Pa=b.Ca)},Gc=function(a){var b=new Q;b.G(a);return b},Ic=function(){var a=Hc,b=new Q;b.I(a);return b},zc=function(){y.call(this)};w(zc,y);zc.prototype.message="Deferred has already fired";zc.prototype.name="AlreadyCalledError";var wc=function(){y.call(this)};w(wc,y);wc.prototype.message="Deferred was canceled";wc.prototype.name="CanceledError"; +var Fc=function(a){this.Ca=k.setTimeout(t(this.ad,this),0);this.va=a};Fc.prototype.ad=function(){delete Ec[this.Ca];throw this.va;};var Ec={};var Jc=function(a){this.ya=[];this.h=a};Jc.prototype.Y=function(a){if(!q(a))throw Error("Invalid filter. Must be a function.");this.ya.push(a)};Jc.prototype.send=function(a,b){if(0==this.ya.length)return this.h.send(a,b);var c=new R(a,b);return Kc(this,0,c).w(function(){if(!c.Ya)return this.h.send(a,b)},this)};var Kc=function(a,b,c){return Gc().w(function(){return this.ya[b](c)},a).w(function(){if(++bb.maxLength&&a.set(b,c.substring(0,b.maxLength))})},Dd=function(a){Xa(a,function(b,c){m(b.defaultValue)&&c==b.defaultValue&&a.remove(b)})};var Hc={status:"device-offline",ta:void 0},Ed={status:"rate-limited",ta:void 0},Fd={status:"sampled-out",ta:void 0},Gd={status:"sent",ta:void 0};var Hd=function(a,b){this.cd=a;this.h=b};Hd.prototype.send=function(a,b){var c;c=this.cd;var d=c.Sb(),e=Math.floor((d-c.Gb)*c.oc);0c.ha?c=!1:(--c.ha,c=!0);return c||"item"==a||"transaction"==a?this.h.send(a,b):Gc(Ed)};var Id=function(){this.ha=60;this.Bc=500;this.oc=5E-4;this.Sb=function(){return(new Date).getTime()};this.Gb=this.Sb()};var Jd=function(a,b){this.j=a;this.h=b};Jd.prototype.send=function(a,b){var c=b.get(Qc),c=parseInt(c.split("-")[1],16),d;"timing"!=a?d=nd(this.j):((d=b.get(Tc))&&b.remove(Tc),d=d||nd(this.j));return c<655.36*d?this.h.send(a,b):Gc(Fd)};var Kd=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#(.*))?$/,Ld=function(a,b){if(a)for(var c=a.split("&"),d=0;dthis.Ia?c.I({status:"payload-too-big",ta:Ua("Encoded hit length == %s, but should be <= %s.",d.length,this.Ia)}):Vd(this.Uc,function(){c.G(Gd)},d);return c};var he=function(a,b){var c=new de;c.add(Ga.name,a);Xa(b,function(a,b){c.add(a.name,b.toString())});return c.toString()};var ie=function(a,b,c){this.j=a;this.Tc=b;this.Ia=c};ie.prototype.cb=function(){if(!this.D){if(!Cc(this.j.ma).K)throw Error("Cannot construct shared channel prior to settings being ready.");new zd;var a=new Bd(new ge(this.Tc,this.Ia)),b=new Id;this.D=new Ad(this.j,new Jd(this.j,new Hd(b,a)))}return this.D};var je=new z,ke=function(){Ea||(Ea=new T(new od));return Ea};v("goog.async.Deferred",Q);v("goog.async.Deferred.prototype.addCallback",Q.prototype.w);v("goog.async.Deferred.prototype.callback",Q.prototype.G);v("goog.async.Deferred.prototype.then",Q.prototype.then);v("goog.events.EventTarget",O);v("goog.events.EventTarget.prototype.listen",O.prototype.listen); +v("analytics.getService",function(a,b){var c=je.get(a,null),d=b||chrome.runtime.getManifest().version;if(null===c){c=ke();if(!Fa){var e=ke();Fa=new ud(e,new ie(e,"https://www.google-analytics.com/collect",8192))}c=new bd("ca1.6.0",a,d,c,Fa);je.set(a,c)}return c});v("analytics.internal.GoogleAnalyticsService",bd);v("analytics.internal.GoogleAnalyticsService.prototype.getTracker",bd.prototype.tc);v("analytics.internal.GoogleAnalyticsService.prototype.getConfig",bd.prototype.rc); +v("analytics.internal.ServiceSettings",T);v("analytics.internal.ServiceSettings.prototype.setTrackingPermitted",T.prototype.Wc);v("analytics.internal.ServiceSettings.prototype.isTrackingPermitted",T.prototype.Fa);v("analytics.internal.ServiceSettings.prototype.setSampleRate",T.prototype.Vc);v("analytics.internal.ServiceSettings.prototype.resetUserId",T.prototype.Nc);v("analytics.internal.ServiceTracker",S);v("analytics.internal.ServiceTracker.prototype.send",S.prototype.send); +v("analytics.internal.ServiceTracker.prototype.sendAppView",S.prototype.Pc);v("analytics.internal.ServiceTracker.prototype.sendEvent",S.prototype.Qc);v("analytics.internal.ServiceTracker.prototype.sendSocial",S.prototype.Sc);v("analytics.internal.ServiceTracker.prototype.sendException",S.prototype.Rc);v("analytics.internal.ServiceTracker.prototype.sendTiming",S.prototype.Pb);v("analytics.internal.ServiceTracker.prototype.startTiming",S.prototype.Zc);v("analytics.internal.ServiceTracker.Timing",ad); +v("analytics.internal.ServiceTracker.Timing.prototype.send",ad.prototype.send);v("analytics.internal.ServiceTracker.prototype.forceSessionStart",S.prototype.qc);v("analytics.internal.ServiceTracker.prototype.addFilter",S.prototype.Y);v("analytics.internal.FilterChannel.Hit",R);v("analytics.internal.FilterChannel.Hit.prototype.getHitType",R.prototype.Ab);v("analytics.internal.FilterChannel.Hit.prototype.getParameters",R.prototype.ba);v("analytics.internal.FilterChannel.Hit.prototype.cancel",R.prototype.cancel); +v("analytics.ParameterMap",E);v("analytics.ParameterMap.Entry",E.Entry);v("analytics.ParameterMap.prototype.set",E.prototype.set);v("analytics.ParameterMap.prototype.get",E.prototype.get);v("analytics.ParameterMap.prototype.remove",E.prototype.remove);v("analytics.ParameterMap.prototype.toObject",E.prototype.Ub);v("analytics.HitTypes.APPVIEW","appview");v("analytics.HitTypes.EVENT","event");v("analytics.HitTypes.SOCIAL","social");v("analytics.HitTypes.TRANSACTION","transaction"); +v("analytics.HitTypes.ITEM","item");v("analytics.HitTypes.TIMING","timing");v("analytics.HitTypes.EXCEPTION","exception");v("analytics.createDimensionParam",Oa);v("analytics.createMetricParam",Pa);wa(Na,function(a){var b=a.id.replace(/[A-Z]/,"_$&").toUpperCase();v("analytics.Parameters."+b,a)});v("analytics.filters.EventLabelerBuilder",C);v("analytics.filters.EventLabelerBuilder.prototype.appendToExistingLabel",C.prototype.cc);v("analytics.filters.EventLabelerBuilder.prototype.stripValue",C.prototype.$c); +v("analytics.filters.EventLabelerBuilder.prototype.powersOfTwo",C.prototype.Kc);v("analytics.filters.EventLabelerBuilder.prototype.rangeBounds",C.prototype.Mc);v("analytics.filters.EventLabelerBuilder.prototype.build",C.prototype.qa);v("analytics.filters.FilterBuilder",B);v("analytics.filters.FilterBuilder.builder",Sa);v("analytics.filters.FilterBuilder.prototype.when",B.prototype.when);v("analytics.filters.FilterBuilder.prototype.whenHitType",B.prototype.Wb); +v("analytics.filters.FilterBuilder.prototype.whenValue",B.prototype.hd);v("analytics.filters.FilterBuilder.prototype.applyFilter",B.prototype.sb);v("analytics.filters.FilterBuilder.prototype.build",B.prototype.qa);v("analytics.EventBuilder",F);v("analytics.EventBuilder.builder",function(){return Ya});v("analytics.EventBuilder.prototype.category",F.prototype.ec);v("analytics.EventBuilder.prototype.action",F.prototype.action);v("analytics.EventBuilder.prototype.label",F.prototype.label); +v("analytics.EventBuilder.prototype.value",F.prototype.value);v("analytics.EventBuilder.prototype.dimension",F.prototype.mc);v("analytics.EventBuilder.prototype.metric",F.prototype.Cc);v("analytics.EventBuilder.prototype.send",F.prototype.send); }).call(this); diff --git a/locales/en/messages.json b/locales/en/messages.json index bcc0f694..dd72c614 100644 --- a/locales/en/messages.json +++ b/locales/en/messages.json @@ -63,6 +63,9 @@ "rememberLastTab": { "message": "Reopen last tab on connect" }, + "analyticsOptOut": { + "message": "Opt out of the anonymised collection of statistics data" + }, "userLanguageSelect": { "message": "Language (need to restart the application for the changes to take effect)" }, diff --git a/manifest.json b/manifest.json index 9025484d..34c1617f 100755 --- a/manifest.json +++ b/manifest.json @@ -24,6 +24,7 @@ "http://*.baseflight.net/", "https://*.amazonaws.com/", "https://*.betaflight.tech/", + "https://www.google-analytics.com/", "serial", "usb", "storage", diff --git a/package-lock.json b/package-lock.json index 77427281..398c1cf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5900,6 +5900,11 @@ } } }, + "object-hash": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.0.tgz", + "integrity": "sha512-05KzQ70lSeGSrZJQXE5wNDiTkBJDlUT/myi6RX9dVIvz7a7Qh4oH93BQdiPMn27nldYvVQCKMUaM83AfizZlsQ==" + }, "object-keys": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", @@ -7237,6 +7242,11 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "short-unique-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-1.1.1.tgz", + "integrity": "sha512-gyWJ4pknxpCx0WlUIdCIwHiQ/4sCy0i0YpDOniXP/TboMMRs6rkg6PPKR2nmNfZ9VYeAIm0x9qz/QmJysd9jNg==" + }, "shortid": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.8.tgz", diff --git a/package.json b/package.json index 418328de..30011d48 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,9 @@ "i18next": "^10.3.0", "i18next-xhr-backend": "^1.5.1", "lru_map": "^0.3.3", - "marked": "^0.3.12" + "marked": "^0.3.12", + "object-hash": "^1.3.0", + "short-unique-id": "^1.1.1" }, "devDependencies": { "chai": "^4.1.2", diff --git a/src/js/Analytics.js b/src/js/Analytics.js new file mode 100644 index 00000000..9240eb9a --- /dev/null +++ b/src/js/Analytics.js @@ -0,0 +1,64 @@ +'use strict'; + +var Analytics = function (serviceName, trackingId, operatingSystem) { + this.eventBuilder = analytics.EventBuilder; + this.service = analytics.getService(serviceName); + this.tracker = this.service.getTracker(trackingId); + + this.DATA = { + BOARD_TYPE: 'boardType', + FIRMWARE_TYPE: 'firmwareType', + FIRMWARE_VERSION: 'firmwareVersion', + API_VERSION: 'apiVersion', + MCU_ID: 'mcuId', + }; + + this.DIMENSIONS = { + OS: 1, + BOARD_TYPE: 2, + FIRMWARE_TYPE: 3, + FIRMWARE_VERSION: 4, + API_VERSION: 5, + }; + + this.APPLICATION_EVENT = this.eventBuilder.builder() + .category('Application') + .dimension(this.DIMENSIONS.OS, operatingSystem); + + this.resetFlightControllerData(); +}; + +Analytics.prototype.setTrackingPermitted = function (permitted) { + this.service.getConfig().addCallback(function(config) { + config.setTrackingPermitted(permitted); + }); +} + +Analytics.prototype.send = function (event) { + this.tracker.send(event); +} + +Analytics.prototype.sendAppView = function (viewName) { + this.tracker.sendAppView(viewName); +} + +Analytics.prototype.rebuildFlightControllerEvent = function () { + this.FLIGHT_CONTROLLER_EVENT = this.eventBuilder.builder() + .category('FlightController') + .dimension(this.DIMENSIONS.BOARD_TYPE, this.flightControllerData[this.DATA.BOARD_TYPE]) + .dimension(this.DIMENSIONS.FIRMWARE_TYPE, this.flightControllerData[this.DATA.FIRMWARE_TYPE]) + .dimension(this.DIMENSIONS.FIRMWARE_VERSION, this.flightControllerData[this.DATA.FIRMWARE_VERSION]) + .dimension(this.DIMENSIONS.API_VERSION, this.flightControllerData[this.DATA.API_VERSION]); +} + +Analytics.prototype.setFlightControllerData = function (property, value) { + this.flightControllerData[property] = value; + + this.rebuildFlightControllerEvent(); +} + +Analytics.prototype.resetFlightControllerData = function () { + this.flightControllerData = {}; + + this.rebuildFlightControllerEvent(); +} diff --git a/src/js/main.js b/src/js/main.js index 0d8c308a..dfe9e453 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,19 +1,60 @@ 'use strict'; +var analytics; + openNewWindowsInExternalBrowser(); //Asynchronous configuration to be done. //When finish the startProcess() function must be called $(document).ready(function () { i18n.init(function() { - startProcess(); + setupAnalytics(); initializeSerialBackend(); }); }); +function setupAnalytics() { + analytics = new Analytics('com.betaflight.configurator', 'UA-123002063-1', GUI.operating_system); + chrome.storage.local.get('userId', function (result) { + var userId; + if (result.userId) { + userId = result.userId; + } else { + var uid = new ShortUniqueId(); + userId = uid.randomUUID(13); + + chrome.storage.local.set({'userId': userId}); + } + + analytics.tracker.set('userId', userId); + + analytics.tracker.set('sessionControl', 'start'); + analytics.send(analytics.APPLICATION_EVENT.action('AppStart')) + + function sendCloseEvent() { + analytics.send(analytics.APPLICATION_EVENT.action('AppClose')) + analytics.tracker.set('sessionControl', 'end'); + } + + try { + var gui = require('nw.gui'); + var win = gui.Window.get(); + win.on('close', function () { + sendCloseEvent(); + + this.close(true); + }); + } catch (ex) { + // Looks like we're in Chrome - but the event does not actually get fired + chrome.runtime.onSuspend.addListener(sendCloseEvent); + } + + startProcess(); + }); +} + //Process to execute to real start the app function startProcess() { - // translate to user-selected language i18n.localizePage(); @@ -113,6 +154,8 @@ function startProcess() { GUI.tab_switch_in_progress = false; } + analytics.sendAppView(tab); + switch (tab) { case 'landing': TABS.landing.initialize(content_ready); @@ -249,6 +292,28 @@ function startProcess() { $('div.checkForConfiguratorUnstableVersions').hide(); } + chrome.storage.local.get('analyticsOptOut', function (result) { + if (result.analyticsOptOut) { + $('div.analyticsOptOut input').prop('checked', true); + } + + $('div.analyticsOptOut input').change(function () { + var checked = $(this).is(':checked'); + + chrome.storage.local.set({'analyticsOptOut': checked}); + + if (checked) { + analytics.send(analytics.APPLICATION_EVENT.action('OptOut')); + } + + analytics.setTrackingPermitted(!checked); + + if (!checked) { + analytics.send(analytics.APPLICATION_EVENT.action('OptIn')); + } + }).change(); + }); + chrome.storage.local.get('userLanguageSelect', function (result) { var userLanguage_e = $('div.userLanguage select'); diff --git a/src/js/serial_backend.js b/src/js/serial_backend.js index b225d6b0..a64a714c 100755 --- a/src/js/serial_backend.js +++ b/src/js/serial_backend.js @@ -1,6 +1,8 @@ 'use strict'; var mspHelper; +var analyticsTimer; + function initializeSerialBackend() { GUI.updateManualPortVisibility = function(){ @@ -124,6 +126,14 @@ function initializeSerialBackend() { function finishClose(finishedCallback) { var wasConnected = CONFIGURATOR.connectionValid; + analytics.send(analytics.FLIGHT_CONTROLLER_EVENT.action('Disconnected')); + if (analyticsTimer) { + analyticsTimer.send(); + + analyticsTimer = undefined; + } + analytics.resetFlightControllerData(); + serial.disconnect(onClosed); MSP.disconnect_cleanup(); @@ -200,13 +210,17 @@ function onOpen(openInfo) { // request configuration data MSP.send_message(MSPCodes.MSP_API_VERSION, false, false, function () { + analytics.setFlightControllerData(analytics.DATA.API_VERSION, CONFIG.apiVersion); + GUI.log(i18n.getMessage('apiVersionReceived', [CONFIG.apiVersion])); if (semver.gte(CONFIG.apiVersion, CONFIGURATOR.apiVersionAccepted)) { MSP.send_message(MSPCodes.MSP_FC_VARIANT, false, false, function () { + analytics.setFlightControllerData(analytics.DATA.FIRMWARE_TYPE, CONFIG.flightControllerIdentifier); if (CONFIG.flightControllerIdentifier === 'BTFL') { MSP.send_message(MSPCodes.MSP_FC_VERSION, false, false, function () { + analytics.setFlightControllerData(analytics.DATA.FIRMWARE_VERSION, CONFIG.flightControllerVersion); GUI.log(i18n.getMessage('fcInfoReceived', [CONFIG.flightControllerIdentifier, CONFIG.flightControllerVersion])); updateStatusBarVersion(CONFIG.flightControllerVersion, CONFIG.flightControllerIdentifier); @@ -217,13 +231,19 @@ function onOpen(openInfo) { GUI.log(i18n.getMessage('buildInfoReceived', [CONFIG.buildInfo])); MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, function () { + analytics.setFlightControllerData(analytics.DATA.BOARD_TYPE, CONFIG.boardIdentifier); GUI.log(i18n.getMessage('boardInfoReceived', [CONFIG.boardIdentifier, CONFIG.boardVersion])); updateStatusBarVersion(CONFIG.flightControllerVersion, CONFIG.flightControllerIdentifier, CONFIG.boardIdentifier); updateTopBarVersion(CONFIG.flightControllerVersion, CONFIG.flightControllerIdentifier, CONFIG.boardIdentifier); MSP.send_message(MSPCodes.MSP_UID, false, false, function () { - GUI.log(i18n.getMessage('uniqueDeviceIdReceived', [CONFIG.uid[0].toString(16) + CONFIG.uid[1].toString(16) + CONFIG.uid[2].toString(16)])); + var uniqueDeviceIdentifier = CONFIG.uid[0].toString(16) + CONFIG.uid[1].toString(16) + CONFIG.uid[2].toString(16); + + analytics.setFlightControllerData(analytics.DATA.MCU_ID, objectHash.sha1(uniqueDeviceIdentifier)); + analytics.send(analytics.FLIGHT_CONTROLLER_EVENT.action('Connected')); + analyticsTimer = analytics.tracker.startTiming('FlightController', 'Connected'); + GUI.log(i18n.getMessage('uniqueDeviceIdReceived', [uniqueDeviceIdentifier])); if (semver.gte(CONFIG.apiVersion, "1.20.0")) { MSP.send_message(MSPCodes.MSP_NAME, false, false, function () { @@ -240,6 +260,8 @@ function onOpen(openInfo) { }); }); } else { + analytics.send(analytics.FLIGHT_CONTROLLER_EVENT.action('ConnectionRefused')); + var dialog = $('.dialogConnectWarning')[0]; $('.dialogConnectWarning-content').html(i18n.getMessage('firmwareTypeNotSupported')); @@ -254,6 +276,8 @@ function onOpen(openInfo) { } }); } else { + analytics.send(analytics.FLIGHT_CONTROLLER_EVENT.action('ConnectionRefused')); + var dialog = $('.dialogConnectWarning')[0]; $('.dialogConnectWarning-content').html(i18n.getMessage('firmwareVersionNotSupported', [CONFIGURATOR.apiVersionAccepted])); @@ -268,6 +292,8 @@ function onOpen(openInfo) { } }); } else { + analytics.send(analytics.APPLICATION_EVENT.action('SerialPortFailed')); + console.log('Failed to open serial port'); GUI.log(i18n.getMessage('serialPortOpenFail')); diff --git a/src/main.html b/src/main.html index 2b397d3e..8fb778ad 100755 --- a/src/main.html +++ b/src/main.html @@ -39,6 +39,8 @@ + + @@ -54,6 +56,7 @@ + @@ -79,6 +82,7 @@ + diff --git a/src/tabs/options.html b/src/tabs/options.html index 1de44b98..43be4837 100644 --- a/src/tabs/options.html +++ b/src/tabs/options.html @@ -7,6 +7,9 @@
+
+ +