From 39f479f8c49c2ed71892ec149be51f2d92b663f4 Mon Sep 17 00:00:00 2001 From: Thomas Steur Date: Tue, 1 Dec 2015 22:10:03 +0000 Subject: Added support to specify a path in a website URL and Piwik will recognize the path during tracking --- core/Tracker/TrackerCodeGenerator.php | 16 +- js/piwik.js | 135 ++++++++++++++++- piwik.js | 77 +++++----- .../javascripts/jsTrackingGenerator.js | 2 +- .../templates/trackingCodeGenerator.twig | 2 +- plugins/Referrers/Columns/Base.php | 34 +++-- plugins/Referrers/Columns/Campaign.php | 2 +- plugins/Referrers/Referrers.php | 10 ++ .../tests/Integration/Columns/ReferrerTypeTest.php | 121 +++++++++++++++ plugins/SitesManager/API.php | 1 + plugins/SitesManager/Model.php | 16 ++ plugins/SitesManager/SiteUrls.php | 148 +++++++++++++++++- .../SitesManager/tests/Integration/ModelTest.php | 52 ++++++- .../tests/Integration/SiteUrlsTest.php | 124 +++++++++++++++ tests/javascript/index.php | 168 +++++++++++++++++++-- tests/javascript/piwiktest.js | 2 + 16 files changed, 830 insertions(+), 80 deletions(-) create mode 100644 plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php diff --git a/core/Tracker/TrackerCodeGenerator.php b/core/Tracker/TrackerCodeGenerator.php index fbf4b4c2be..c985db88e7 100644 --- a/core/Tracker/TrackerCodeGenerator.php +++ b/core/Tracker/TrackerCodeGenerator.php @@ -172,13 +172,23 @@ class TrackerCodeGenerator } // We need to parse_url to isolate hosts $websiteHosts = array(); + $firstHost = null; foreach ($websiteUrls as $site_url) { $referrerParsed = parse_url($site_url); - $websiteHosts[] = $referrerParsed['host']; + + if (!isset($firstHost)) { + $firstHost = $referrerParsed['host']; + } + + $url = $referrerParsed['host']; + if (!empty($referrerParsed['path'])) { + $url .= $referrerParsed['path']; + } + $websiteHosts[] = $url; } $options = ''; - if ($mergeSubdomains && !empty($websiteHosts)) { - $options .= ' _paq.push(["setCookieDomain", "*.' . $websiteHosts[0] . '"]);' . "\n"; + if ($mergeSubdomains && !empty($firstHost)) { + $options .= ' _paq.push(["setCookieDomain", "*.' . $firstHost . '"]);' . "\n"; } if ($mergeAliasUrls && !empty($websiteHosts)) { $urls = '["*.' . implode('","*.', $websiteHosts) . '"]'; diff --git a/js/piwik.js b/js/piwik.js index fa9d9f57e5..66e5ff6b75 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -1021,7 +1021,7 @@ if (typeof JSON2 !== 'object' && typeof window.JSON === 'object' && window.JSON. trackVisibleContentImpressions, isTrackOnlyVisibleContentEnabled, port, isUrlToCurrentDomain, isNodeAuthorizedToTriggerInteraction, replaceHrefIfInternalLink, getConfigDownloadExtensions, disableLinkTracking, substr, setAnyAttribute, wasContentTargetAttrReplaced, max, abs, childNodes, compareDocumentPosition, body, - getConfigVisitorCookieTimeout, getRemainingVisitorCookieTimeout, + getConfigVisitorCookieTimeout, getRemainingVisitorCookieTimeout, getDomains, newVisitor, uuid, createTs, visitCount, currentVisitTs, lastVisitTs, lastEcommerceOrderTs, "", "\b", "\t", "\n", "\f", "\r", "\"", "\\", apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length, parse, prototype, push, replace, @@ -1593,6 +1593,10 @@ if (typeof Piwik !== 'object') { domain = domain.slice(1); } + if (domain.indexOf('/') !== -1) { + domain = domain.substr(0, domain.indexOf('/')); + } + return domain; } @@ -3012,10 +3016,110 @@ if (typeof Piwik !== 'object') { return baseUrl + url; } + function isSameHost (hostName, alias) { + var offset; + + hostName = String(hostName).toLowerCase(); + alias = String(alias).toLowerCase(); + + if (hostName === alias) { + return true; + } + + if (alias.slice(0, 1) === '.') { + if (hostName === alias.slice(1)) { + return true; + } + + offset = hostName.length - alias.length; + + if ((offset > 0) && (hostName.slice(offset) === alias)) { + return true; + } + } + + return false; + } + + function stringEndsWith(str, suffix) { + str = String(str); + return str.indexOf(suffix, str.length - suffix.length) !== -1; + } + + /* + * Extract pathname from URL. element.pathname is actually supported by pretty much all browsers including + * IE6 apart from some rare very old ones + */ + function getPathName(url) { + var parser = document.createElement('a'); + if (url.indexOf('//') !== 0 && url.indexOf('http') !== 0) { + url = 'http://' + url; + } + + parser.href = content.toAbsoluteUrl(url); + if (parser.pathname) { + return parser.pathname; + } + + return ''; + } + + function isSitePath (path, pathAlias) + { + var matchesAnyPath = (!pathAlias || pathAlias === '/'); + + if (matchesAnyPath) { + return true; + } + + if (path === pathAlias) { + return true; + } + + if (!path) { + return false; + } + + pathAlias = String(pathAlias).toLowerCase(); + path = String(path).toLowerCase(); + + // we need to append slashes so /foobarbaz won't match a site /foobar + if (!stringEndsWith(path, '/')) { + path += '/'; + } + + if (!stringEndsWith(pathAlias, '/')) { + pathAlias += '/'; + } + + return path.indexOf(pathAlias) === 0; + } + + function isSiteHostPath(host, path) + { + var i, + alias, + configAlias, + aliasHost, + aliasPath; + + for (i = 0; i < configHostsAlias.length; i++) { + aliasHost = domainFixup(configHostsAlias[i]); + aliasPath = getPathName(configHostsAlias[i]); + + if (isSameHost(host, aliasHost) && isSitePath(path, aliasPath)) { + return true; + } + } + + return false; + } + /* * Is the host local? (i.e., not an outlink) */ function isSiteHostName(hostName) { + var i, alias, offset; @@ -4004,6 +4108,8 @@ if (typeof Piwik !== 'object') { return; } + var originalSourcePath = sourceElement.pathname || getPathName(sourceElement.href); + // browsers, such as Safari, don't downcase hostname and href var originalSourceHostName = sourceElement.hostname || getHostName(sourceElement.href); var sourceHostName = originalSourceHostName.toLowerCase(); @@ -4014,7 +4120,7 @@ if (typeof Piwik !== 'object') { if (!scriptProtocol.test(sourceHref)) { // track outlinks and all downloads - var linkType = getLinkType(sourceElement.className, sourceHref, isSiteHostName(sourceHostName), query.hasNodeAttribute(sourceElement, 'download')); + var linkType = getLinkType(sourceElement.className, sourceHref, isSiteHostPath(sourceHostName, originalSourcePath), query.hasNodeAttribute(sourceElement, 'download')); if (linkType) { return { @@ -4917,6 +5023,9 @@ if (typeof Piwik !== 'object') { internalIsNodeVisible: isVisible, isNodeAuthorizedToTriggerInteraction: isNodeAuthorizedToTriggerInteraction, replaceHrefIfInternalLink: replaceHrefIfInternalLink, + getDomains: function () { + return configHostsAlias; + }, getConfigDownloadExtensions: function () { return configDownloadExtensions; }, @@ -5347,13 +5456,31 @@ if (typeof Piwik !== 'object') { }, /** - * Set array of domains to be treated as local + * Set array of domains to be treated as local. Also supports path, eg '.piwik.org/subsite1'. In this + * case all links that don't go to '*.piwik.org/subsite1/ *' would be treated as outlinks. + * For example a link to 'piwik.org/' or 'piwik.org/subsite2' both would be treated as outlinks * * @param string|array hostsAlias */ setDomains: function (hostsAlias) { configHostsAlias = isString(hostsAlias) ? [hostsAlias] : hostsAlias; - configHostsAlias.push(domainAlias); + + var hasDomainAliasAlready = false, i; + for (i in configHostsAlias) { + if (Object.prototype.hasOwnProperty.call(configHostsAlias, i) + && isSameHost(domainAlias, domainFixup(configHostsAlias[i]))) { + hasDomainAliasAlready = true; + break; + } + } + + if (!hasDomainAliasAlready) { + /** + * eg if domainAlias = 'piwik.org' and someone set hostsAlias = ['piwik.org/foo'] then we should + * not add piwik.org as it would increase the allowed scope. + */ + configHostsAlias.push(domainAlias); + } }, /** diff --git a/piwik.js b/piwik.js index 401ae10af1..f0fbdf735f 100644 --- a/piwik.js +++ b/piwik.js @@ -23,43 +23,44 @@ function j(Y){try{return H(Y)}catch(Z){return unescape(Y)}}function y(Z){var Y=t R()})}else{if(w.attachEvent){w.attachEvent("onreadystatechange",function Y(){if(w.readyState==="complete"){w.detachEvent("onreadystatechange",Y);R()}});if(w.documentElement.doScroll&&I===I.top){(function Y(){if(!q){try{w.documentElement.doScroll("left")}catch(aa){setTimeout(Y,0);return}R()}}())}}}if((new RegExp("WebKit")).test(e.userAgent)){Z=setInterval(function(){if(q||/loaded|complete/.test(w.readyState)){clearInterval(Z);R()}},10)}X(I,"load",R,false)}function i(aa,Z){var Y=w.createElement("script");Y.type="text/javascript";Y.src=aa;if(Y.readyState){Y.onreadystatechange=function(){var ab=this.readyState;if(ab==="loaded"||ab==="complete"){Y.onreadystatechange=null;Z()}}}else{Y.onload=Z}w.getElementsByTagName("head")[0].appendChild(Y)}function z(){var Y="";try{Y=I.top.document.referrer}catch(aa){if(I.parent){try{Y=I.parent.document.referrer}catch(Z){Y=""}}}if(Y===""){Y=w.referrer}return Y}function l(Y){var aa=new RegExp("^([a-z]+):"),Z=aa.exec(Y);return Z?Z[1]:null}function c(Y){var aa=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)"),Z=aa.exec(Y); return Z?Z[1]:Y}function K(aa,Z){var Y="[\\?&#]"+Z+"=([^&#]*)";var ac=new RegExp(Y);var ab=ac.exec(aa);return ab?H(ab[1]):""}function u(Y){return unescape(m(Y))}function W(an){var aa=function(au,at){return(au<>>(32-at))},ao=function(aw){var au="",av,at;for(av=7;av>=0;av--){at=(aw>>>(av*4))&15;au+=at.toString(16)}return au},ad,aq,ap,Z=[],ah=1732584193,af=4023233417,ae=2562383102,ac=271733878,ab=3285377520,am,al,ak,aj,ai,ar,Y,ag=[];an=u(an);Y=an.length;for(aq=0;aq>>29);ag.push((Y<<3)&4294967295);for(ad=0;adad.offsetWidth+ad.scrollLeft||af+ai-ajad.offsetHeight+ad.scrollTop||ak+ag-ajag?-1:1});if(Y.length<=1){return Y}var Z=0;var ab=0;var ac=[];var aa;aa=Y[Z++];while(aa){if(aa===Y[Z]){ab=ac.push(Z)}aa=Y[Z++]||null}while(ab--){Y.splice(ac[ab],1)}return Y},getAttributeValueFromNode:function(ac,aa){if(!this.hasNodeAttribute(ac,aa)){return}if(ac&&ac.getAttribute){return ac.getAttribute(aa)}if(!ac||!ac.attributes){return}var ab=(typeof ac.attributes[aa]);if("undefined"===ab){return}if(ac.attributes[aa].value){return ac.attributes[aa].value}if(ac.attributes[aa].nodeValue){return ac.attributes[aa].nodeValue -}var Z;var Y=ac.attributes;if(!Y){return}for(Z=0;Z1000){break}Y++}},findPieceNode:function(Z){var Y;Y=S.findFirstNodeHavingAttribute(Z,this.CONTENT_PIECE_ATTR);if(!Y){Y=S.findFirstNodeHavingClass(Z,this.CONTENT_PIECE_CLASS)}if(Y){return Y}return Z},findTargetNodeNoDefault:function(Y){if(!Y){return}var Z=S.findFirstNodeHavingAttributeWithValue(Y,this.CONTENT_TARGET_ATTR); -if(Z){return Z}Z=S.findFirstNodeHavingAttribute(Y,this.CONTENT_TARGET_ATTR);if(Z){return Z}Z=S.findFirstNodeHavingClass(Y,this.CONTENT_TARGET_CLASS);if(Z){return Z}},findTargetNode:function(Y){var Z=this.findTargetNodeNoDefault(Y);if(Z){return Z}return Y},findContentName:function(Z){if(!Z){return}var ac=S.findFirstNodeHavingAttributeWithValue(Z,this.CONTENT_NAME_ATTR);if(ac){return S.getAttributeValueFromNode(ac,this.CONTENT_NAME_ATTR)}var Y=this.findContentPiece(Z);if(Y){return this.removeDomainIfIsInLink(Y)}if(S.hasNodeAttributeWithValue(Z,"title")){return S.getAttributeValueFromNode(Z,"title")}var aa=this.findPieceNode(Z);if(S.hasNodeAttributeWithValue(aa,"title")){return S.getAttributeValueFromNode(aa,"title")}var ab=this.findTargetNode(Z);if(S.hasNodeAttributeWithValue(ab,"title")){return S.getAttributeValueFromNode(ab,"title")}},findContentPiece:function(Z){if(!Z){return}var ab=S.findFirstNodeHavingAttributeWithValue(Z,this.CONTENT_PIECE_ATTR);if(ab){return S.getAttributeValueFromNode(ab,this.CONTENT_PIECE_ATTR) -}var Y=this.findPieceNode(Z);var aa=this.findMediaUrlInNode(Y);if(aa){return this.toAbsoluteUrl(aa)}},findContentTarget:function(aa){if(!aa){return}var ab=this.findTargetNode(aa);if(S.hasNodeAttributeWithValue(ab,this.CONTENT_TARGET_ATTR)){return S.getAttributeValueFromNode(ab,this.CONTENT_TARGET_ATTR)}var Z;if(S.hasNodeAttributeWithValue(ab,"href")){Z=S.getAttributeValueFromNode(ab,"href");return this.toAbsoluteUrl(Z)}var Y=this.findPieceNode(aa);if(S.hasNodeAttributeWithValue(Y,"href")){Z=S.getAttributeValueFromNode(Y,"href");return this.toAbsoluteUrl(Z)}},isSameDomain:function(Y){if(!Y||!Y.indexOf){return false}if(0===Y.indexOf(this.getLocation().origin)){return true}var Z=Y.indexOf(this.getLocation().host);if(8>=Z&&0<=Z){return true}return false},removeDomainIfIsInLink:function(aa){var Z="^https?://[^/]+";var Y="^.*//[^/]+";if(aa&&aa.search&&-1!==aa.search(new RegExp(Z))&&this.isSameDomain(aa)){aa=aa.replace(new RegExp(Y),"");if(!aa){aa="/"}}return aa},findMediaUrlInNode:function(ac){if(!ac){return -}var aa=["img","embed","video","audio"];var Y=ac.nodeName.toLowerCase();if(-1!==B(aa,Y)&&S.findFirstNodeHavingAttributeWithValue(ac,"src")){var ab=S.findFirstNodeHavingAttributeWithValue(ac,"src");return S.getAttributeValueFromNode(ab,"src")}if(Y==="object"&&S.hasNodeAttributeWithValue(ac,"data")){return S.getAttributeValueFromNode(ac,"data")}if(Y==="object"){var ad=S.findNodesByTagName(ac,"param");if(ad&&ad.length){var Z;for(Z=0;Z0 -}var Z=ab.clientWidth;if(I.innerWidth&&Z>I.innerWidth){Z=I.innerWidth}var Y=ab.clientHeight;if(I.innerHeight&&Y>I.innerHeight){Y=I.innerHeight}return((ac.bottom>0||aa)&&ac.right>0&&ac.left=0){cu=cu.slice(0,ct)}ct=cu.lastIndexOf("/");if(ct!==cu.length-1){cu=cu.slice(0,ct+1)}return cu+cs}function bE(cv){var ct,cs,cu;for(ct=0;ct0)&&(cv.slice(cu)===cs)){return true}}}return false}function cr(cs,cu){var ct=new Image(1,1);ct.onload=function(){v=0;if(typeof cu==="function"){cu() -}};ct.src=ac+(ac.indexOf("?")<0?"?":"&")+cs}function bW(ct,cw,cs){if(!y(cs)||null===cs){cs=true}try{var cv=I.XMLHttpRequest?new I.XMLHttpRequest():I.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;cv.open("POST",ac,true);cv.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cs){cr(ct,cw)}else{if(typeof cw==="function"){cw()}}};cv.setRequestHeader("Content-Type",aQ);cv.send(ct)}catch(cu){if(cs){cr(ct,cw)}}}function ce(ct){var cs=new Date();var cu=cs.getTime()+ct;if(!k||cu>k){k=cu}}function aD(cs){if(bc||!b5){return}bc=setTimeout(function ct(){bc=null;if(bs()){return}var cu=new Date(),cv=b5-(cu.getTime()-bp);cv=Math.min(b5,cv);aD(cv)},cs||b5)}function bf(){if(!bc){return}clearTimeout(bc);bc=null}function ay(){if(bs()){return}aD()}function bn(){bf()}function bG(){if(bO||!b5){return}bO=true;X(I,"focus",ay);X(I,"blur",bn);aD()}function aJ(cw){var ct=new Date();var cs=ct.getTime();bp=cs;if(bh&&cscR){cI.visitCount++;cI.lastVisitTs=cI.currentVisitTs}if(!bb||!cA.length){for(cO in a4){if(Object.prototype.hasOwnProperty.call(a4,cO)){cA=K(cS,a4[cO]); -if(cA.length){break}}}for(cO in aV){if(Object.prototype.hasOwnProperty.call(aV,cO)){cs=K(cS,aV[cO]);if(cs.length){break}}}}cV=c(bH);cD=cN.length?c(cN):"";if(cV.length&&!bE(cV)&&(!bb||!cD.length||bE(cD))){cN=bH}if(cN.length||cA.length){cz=cC;cM=[cA,cs,cz,cf(cN.slice(0,cw))];bV(cJ,JSON2.stringify(cM),bi,ck,at)}}cu+="&idsite="+b7+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+ct.getHours()+"&m="+ct.getMinutes()+"&s="+ct.getSeconds()+"&url="+m(cf(cS))+(bH.length?"&urlref="+m(cf(bH)):"")+((aP&&aP.length)?"&uid="+m(aP):"")+"&_id="+cI.uuid+"&_idts="+cI.createTs+"&_idvc="+cI.visitCount+"&_idn="+cI.newVisitor+(cA.length?"&_rcn="+m(cA):"")+(cs.length?"&_rck="+m(cs):"")+"&_refts="+cz+"&_viewts="+cI.lastVisitTs+(String(cI.lastEcommerceOrderTs).length?"&_ects="+cI.lastEcommerceOrderTs:"")+(String(cN).length?"&_ref="+m(cf(cN.slice(0,cw))):"")+(cF?"&cs="+m(cF):"")+"&send_image=0";for(cO in b8){if(Object.prototype.hasOwnProperty.call(b8,cO)){cu+="&"+cO+"="+b8[cO]}}var cU=[];if(cP){for(cO in cP){if(Object.prototype.hasOwnProperty.call(cP,cO)&&/^dimension\d+$/.test(cO)){var cy=cO.replace("dimension",""); -cU.push(parseInt(cy,10));cU.push(String(cy));cu+="&"+cO+"="+cP[cO];delete cP[cO]}}}if(cP&&s(cP)){cP=null}for(cO in bS){if(Object.prototype.hasOwnProperty.call(bS,cO)){var cE=(-1===cU.indexOf(cO));if(cE){cu+="&dimension"+cO+"="+bS[cO]}}}if(cP){cu+="&data="+m(JSON2.stringify(cP))}else{if(ap){cu+="&data="+m(JSON2.stringify(ap))}}function cB(cW,cX){var cY=JSON2.stringify(cW);if(cY.length>2){return"&"+cX+"="+m(cY)}return""}var cT=b4(br);var cK=b4(ad);cu+=cB(cT,"cvar");cu+=cB(cK,"e_cvar");if(ai){cu+=cB(ai,"_cvar");for(cO in cL){if(Object.prototype.hasOwnProperty.call(cL,cO)){if(ai[cO][0]===""||ai[cO][1]===""){delete ai[cO]}}}if(bd){bV(cG,JSON2.stringify(ai),aF,ck,at)}}if(a7){if(aN){cu+=">_ms="+aN}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){cu+=">_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}cI.lastEcommerceOrderTs=y(cv)&&String(cv).length?cv:cI.lastEcommerceOrderTs;ao(cI);ba();cu+=Q(cQ);if(bB.length){cu+="&"+bB}if(r(aw)){cu=aw(cu)}return cu}bs=function bx(){var cs=new Date(); -if(bp+b5<=cs.getTime()){var ct=aZ("ping=1",null,"ping");a5(ct,bA);return true}return false};function bY(cv,cu,cz,cw,cs,cC){var cx="idgoal=0",cy,ct=new Date(),cA=[],cB;if(String(cv).length){cx+="&ec_id="+m(cv);cy=Math.round(ct.getTime()/1000)}cx+="&revenue="+cu;if(String(cz).length){cx+="&ec_st="+cz}if(String(cw).length){cx+="&ec_tx="+cw}if(String(cs).length){cx+="&ec_sh="+cs}if(String(cC).length){cx+="&ec_dt="+cC}if(bT){for(cB in bT){if(Object.prototype.hasOwnProperty.call(bT,cB)){if(!y(bT[cB][1])){bT[cB][1]=""}if(!y(bT[cB][2])){bT[cB][2]=""}if(!y(bT[cB][3])||String(bT[cB][3]).length===0){bT[cB][3]=0}if(!y(bT[cB][4])||String(bT[cB][4]).length===0){bT[cB][4]=1}cA.push(bT[cB])}}cx+="&ec_items="+m(JSON2.stringify(cA))}cx=aZ(cx,ap,"ecommerce",cy);a5(cx,bA)}function bX(cs,cw,cv,cu,ct,cx){if(String(cs).length&&y(cw)){bY(cs,cw,cv,cu,ct,cx)}}function ci(cs){if(y(cs)){bY("",cs,"","","","")}}function bm(cu,cv){var cs=new Date(),ct=aZ("action_name="+m(V(cu||aY)),cv,"log");a5(ct,bA)}function aM(cu,ct){var cv,cs="(^| )(piwik[_-]"+ct; -if(cu){for(cv=0;cv0){cw=parseInt(cw,10);cz(cw)}})}function cg(){var ct,cu,cv={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"},cs=I.devicePixelRatio||1;if(!((new RegExp("MSIE")).test(e.userAgent))){if(e.mimeTypes&&e.mimeTypes.length){for(ct in cv){if(Object.prototype.hasOwnProperty.call(cv,ct)){cu=e.mimeTypes[cv[ct]]; -b8[ct]=(cu&&cu.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&y(e.javaEnabled)&&e.javaEnabled()){b8.java="1"}if(r(I.GearsFactory)){b8.gears="1"}b8.cookie=ag()}b8.res=M.width*cs+"x"+M.height*cs}cg();bw();ao();return{getVisitorId:function(){return bN().uuid},getVisitorInfo:function(){return aa()},getAttributionInfo:function(){return Z()},getAttributionCampaignName:function(){return Z()[0]},getAttributionCampaignKeyword:function(){return Z()[1]},getAttributionReferrerTimestamp:function(){return Z()[2]},getAttributionReferrerUrl:function(){return Z()[3]},setTrackerUrl:function(cs){ac=cs},getTrackerUrl:function(){return ac},getSiteId:function(){return b7},setSiteId:function(cs){co(cs)},setUserId:function(cs){if(!y(cs)||!cs.length){return}aP=cs;a8=a3(aP).substr(0,16)},getUserId:function(){return aP},setCustomData:function(cs,ct){if(L(cs)){ap=cs}else{if(!ap){ap={}}ap[cs]=ct}},getCustomData:function(){return ap},setCustomRequestProcessing:function(cs){aw=cs},appendToTrackingUrl:function(cs){bB=cs -},getRequest:function(cs){return aZ(cs)},addPlugin:function(cs,ct){a[cs]=ct},setCustomDimension:function(cs,ct){cs=parseInt(cs,10);if(cs>0){if(!y(ct)){ct=""}if(!o(ct)){ct=String(ct)}bS[cs]=ct}},getCustomDimension:function(cs){cs=parseInt(cs,10);if(cs>0&&Object.prototype.hasOwnProperty.call(bS,cs)){return bS[cs]}},deleteCustomDimension:function(cs){cs=parseInt(cs,10);if(cs>0){delete bS[cs]}},setCustomVariable:function(ct,cs,cw,cu){var cv;if(!y(cu)){cu="visit"}if(!y(cs)){return}if(!y(cw)){cw=""}if(ct>0){cs=!o(cs)?String(cs):cs;cw=!o(cw)?String(cw):cw;cv=[cs.slice(0,cc),cw.slice(0,cc)];if(cu==="visit"||cu===2){ab();ai[ct]=cv}else{if(cu==="page"||cu===3){br[ct]=cv}else{if(cu==="event"){ad[ct]=cv}}}}},getCustomVariable:function(ct,cu){var cs;if(!y(cu)){cu="visit"}if(cu==="page"||cu===3){cs=br[ct]}else{if(cu==="event"){cs=ad[ct]}else{if(cu==="visit"||cu===2){ab();cs=ai[ct]}}}if(!y(cs)||(cs&&cs[0]==="")){return false}return cs},deleteCustomVariable:function(cs,ct){if(this.getCustomVariable(cs,ct)){this.setCustomVariable(cs,"","",ct) -}},storeCustomVariablesInCookie:function(){bd=true},setLinkTrackingTimer:function(cs){bA=cs},setDownloadExtensions:function(cs){if(o(cs)){cs=cs.split("|")}a0=cs},addDownloadExtensions:function(ct){var cs;if(o(ct)){ct=ct.split("|")}for(cs=0;cs1){if(console!==undefined&&console&&console.error){console.error("The method "+Z+' is registered more than once in "paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}}ae[Z]++}}}}return ad}X(I,"beforeunload",U,false);p();Date.prototype.getTimeAlias=Date.prototype.getTime;N=new F();var t=["disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setUserId","setSiteId","enableLinkTracking"]; -_paq=b(_paq,t);for(v=0;v<_paq.length;v++){if(_paq[v]){T(_paq[v])}}_paq=new x();d={addPlugin:function(Y,Z){a[Y]=Z},getTracker:function(Y,Z){if(!y(Z)){Z=this.getAsyncTracker().getSiteId()}if(!y(Y)){Y=this.getAsyncTracker().getTrackerUrl()}return new F(Y,Z)},getAsyncTracker:function(){return N}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return d})}return d}())}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k); -e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}; +}function A(Z){var Y=Z.length;if(Z.charAt(--Y)==="."){Z=Z.slice(0,Y)}if(Z.slice(0,2)==="*."){Z=Z.slice(1)}if(Z.indexOf("/")!==-1){Z=Z.substr(0,Z.indexOf("/"))}return Z}function V(Z){Z=Z&&Z.text?Z.text:Z;if(!o(Z)){var Y=w.getElementsByTagName("title");if(Y&&y(Y[0])){Z=Y[0].text}}return Z}function E(Y){if(!Y){return[]}if(!y(Y.children)&&y(Y.childNodes)){return Y.children}if(y(Y.children)){return Y.children}return[]}function J(Z,Y){if(!Z||!Y){return false}if(Z.contains){return Z.contains(Y)}if(Z===Y){return true}if(Z.compareDocumentPosition){return !!(Z.compareDocumentPosition(Y)&16)}return false}function B(aa,ab){if(aa&&aa.indexOf){return aa.indexOf(ab)}if(!y(aa)||aa===null){return -1}if(!aa.length){return -1}var Y=aa.length;if(Y===0){return -1}var Z=0;while(Zad.offsetWidth+ad.scrollLeft||af+ai-ajad.offsetHeight+ad.scrollTop||ak+ag-ajag?-1:1});if(Y.length<=1){return Y}var Z=0;var ab=0;var ac=[];var aa;aa=Y[Z++];while(aa){if(aa===Y[Z]){ab=ac.push(Z)}aa=Y[Z++]||null}while(ab--){Y.splice(ac[ab],1)}return Y},getAttributeValueFromNode:function(ac,aa){if(!this.hasNodeAttribute(ac,aa)){return}if(ac&&ac.getAttribute){return ac.getAttribute(aa)}if(!ac||!ac.attributes){return}var ab=(typeof ac.attributes[aa]);if("undefined"===ab){return}if(ac.attributes[aa].value){return ac.attributes[aa].value +}if(ac.attributes[aa].nodeValue){return ac.attributes[aa].nodeValue}var Z;var Y=ac.attributes;if(!Y){return}for(Z=0;Z1000){break}Y++}},findPieceNode:function(Z){var Y;Y=S.findFirstNodeHavingAttribute(Z,this.CONTENT_PIECE_ATTR);if(!Y){Y=S.findFirstNodeHavingClass(Z,this.CONTENT_PIECE_CLASS)}if(Y){return Y}return Z},findTargetNodeNoDefault:function(Y){if(!Y){return}var Z=S.findFirstNodeHavingAttributeWithValue(Y,this.CONTENT_TARGET_ATTR);if(Z){return Z}Z=S.findFirstNodeHavingAttribute(Y,this.CONTENT_TARGET_ATTR);if(Z){return Z}Z=S.findFirstNodeHavingClass(Y,this.CONTENT_TARGET_CLASS);if(Z){return Z}},findTargetNode:function(Y){var Z=this.findTargetNodeNoDefault(Y);if(Z){return Z}return Y},findContentName:function(Z){if(!Z){return}var ac=S.findFirstNodeHavingAttributeWithValue(Z,this.CONTENT_NAME_ATTR);if(ac){return S.getAttributeValueFromNode(ac,this.CONTENT_NAME_ATTR)}var Y=this.findContentPiece(Z);if(Y){return this.removeDomainIfIsInLink(Y) +}if(S.hasNodeAttributeWithValue(Z,"title")){return S.getAttributeValueFromNode(Z,"title")}var aa=this.findPieceNode(Z);if(S.hasNodeAttributeWithValue(aa,"title")){return S.getAttributeValueFromNode(aa,"title")}var ab=this.findTargetNode(Z);if(S.hasNodeAttributeWithValue(ab,"title")){return S.getAttributeValueFromNode(ab,"title")}},findContentPiece:function(Z){if(!Z){return}var ab=S.findFirstNodeHavingAttributeWithValue(Z,this.CONTENT_PIECE_ATTR);if(ab){return S.getAttributeValueFromNode(ab,this.CONTENT_PIECE_ATTR)}var Y=this.findPieceNode(Z);var aa=this.findMediaUrlInNode(Y);if(aa){return this.toAbsoluteUrl(aa)}},findContentTarget:function(aa){if(!aa){return}var ab=this.findTargetNode(aa);if(S.hasNodeAttributeWithValue(ab,this.CONTENT_TARGET_ATTR)){return S.getAttributeValueFromNode(ab,this.CONTENT_TARGET_ATTR)}var Z;if(S.hasNodeAttributeWithValue(ab,"href")){Z=S.getAttributeValueFromNode(ab,"href");return this.toAbsoluteUrl(Z)}var Y=this.findPieceNode(aa);if(S.hasNodeAttributeWithValue(Y,"href")){Z=S.getAttributeValueFromNode(Y,"href"); +return this.toAbsoluteUrl(Z)}},isSameDomain:function(Y){if(!Y||!Y.indexOf){return false}if(0===Y.indexOf(this.getLocation().origin)){return true}var Z=Y.indexOf(this.getLocation().host);if(8>=Z&&0<=Z){return true}return false},removeDomainIfIsInLink:function(aa){var Z="^https?://[^/]+";var Y="^.*//[^/]+";if(aa&&aa.search&&-1!==aa.search(new RegExp(Z))&&this.isSameDomain(aa)){aa=aa.replace(new RegExp(Y),"");if(!aa){aa="/"}}return aa},findMediaUrlInNode:function(ac){if(!ac){return}var aa=["img","embed","video","audio"];var Y=ac.nodeName.toLowerCase();if(-1!==B(aa,Y)&&S.findFirstNodeHavingAttributeWithValue(ac,"src")){var ab=S.findFirstNodeHavingAttributeWithValue(ac,"src");return S.getAttributeValueFromNode(ab,"src")}if(Y==="object"&&S.hasNodeAttributeWithValue(ac,"data")){return S.getAttributeValueFromNode(ac,"data")}if(Y==="object"){var ad=S.findNodesByTagName(ac,"param");if(ad&&ad.length){var Z;for(Z=0;Z0}var Z=ab.clientWidth;if(I.innerWidth&&Z>I.innerWidth){Z=I.innerWidth}var Y=ab.clientHeight;if(I.innerHeight&&Y>I.innerHeight){Y=I.innerHeight}return((ac.bottom>0||aa)&&ac.right>0&&ac.left=0){cz=cz.slice(0,cy)}cy=cz.lastIndexOf("/");if(cy!==cz.length-1){cz=cz.slice(0,cy+1)}return cz+cx}function b2(cz,cx){var cy;cz=String(cz).toLowerCase();cx=String(cx).toLowerCase();if(cz===cx){return true}if(cx.slice(0,1)==="."){if(cz===cx.slice(1)){return true}cy=cz.length-cx.length;if((cy>0)&&(cz.slice(cy)===cx)){return true}}return false}function ck(cy,cx){cy=String(cy);return cy.indexOf(cx,cy.length-cx.length)!==-1}function bL(cx){var cy=document.createElement("a"); +if(cx.indexOf("//")!==0&&cx.indexOf("http")!==0){cx="http://"+cx}cy.href=n.toAbsoluteUrl(cx);if(cy.pathname){return cy.pathname}return""}function aF(cy,cx){var cz=(!cx||cx==="/");if(cz){return true}if(cy===cx){return true}if(!cy){return false}cx=String(cx).toLowerCase();cy=String(cy).toLowerCase();if(!ck(cy,"/")){cy+="/"}if(!ck(cx,"/")){cx+="/"}return cy.indexOf(cx)===0}function ab(cB,cD){var cy,cx,cz,cA,cC;for(cy=0;cy0)&&(cA.slice(cz)===cx)){return true}}}return false}function bQ(cx,cz){var cy=new Image(1,1);cy.onload=function(){v=0;if(typeof cz==="function"){cz()}};cy.src=ai+(ai.indexOf("?")<0?"?":"&")+cx}function cl(cy,cB,cx){if(!y(cx)||null===cx){cx=true}try{var cA=I.XMLHttpRequest?new I.XMLHttpRequest():I.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null; +cA.open("POST",ai,true);cA.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cx){bQ(cy,cB)}else{if(typeof cB==="function"){cB()}}};cA.setRequestHeader("Content-Type",bV);cA.send(cy)}catch(cz){if(cx){bQ(cy,cB)}}}function bH(cy){var cx=new Date();var cz=cx.getTime()+cy;if(!k||cz>k){k=cz}}function bO(cx){if(bD||!aH){return}bD=setTimeout(function cy(){bD=null;if(bj()){return}var cz=new Date(),cA=aH-(cz.getTime()-cg);cA=Math.min(aH,cA);bO(cA)},cx||aH)}function bc(){if(!bD){return}clearTimeout(bD);bD=null}function aL(){if(bj()){return}bO()}function af(){bc()}function cv(){if(aq||!aH){return}aq=true;X(I,"focus",aL);X(I,"blur",af);bO()}function bZ(cB){var cy=new Date();var cx=cy.getTime();cg=cx;if(bY&&cxcW){cN.visitCount++;cN.lastVisitTs=cN.currentVisitTs}if(!a0||!cF.length){for(cT in bR){if(Object.prototype.hasOwnProperty.call(bR,cT)){cF=K(cX,bR[cT]);if(cF.length){break}}}for(cT in a9){if(Object.prototype.hasOwnProperty.call(a9,cT)){cx=K(cX,a9[cT]);if(cx.length){break}}}}c0=c(aU);cI=cS.length?c(cS):"";if(c0.length&&!ay(c0)&&(!a0||!cI.length||ay(cI))){cS=aU +}if(cS.length||cF.length){cE=cH;cR=[cF,cx,cE,bt(cS.slice(0,cB))];ct(cO,JSON2.stringify(cR),cm,aW,cf)}}cz+="&idsite="+bx+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cy.getHours()+"&m="+cy.getMinutes()+"&s="+cy.getSeconds()+"&url="+m(bt(cX))+(aU.length?"&urlref="+m(bt(aU)):"")+((a3&&a3.length)?"&uid="+m(a3):"")+"&_id="+cN.uuid+"&_idts="+cN.createTs+"&_idvc="+cN.visitCount+"&_idn="+cN.newVisitor+(cF.length?"&_rcn="+m(cF):"")+(cx.length?"&_rck="+m(cx):"")+"&_refts="+cE+"&_viewts="+cN.lastVisitTs+(String(cN.lastEcommerceOrderTs).length?"&_ects="+cN.lastEcommerceOrderTs:"")+(String(cS).length?"&_ref="+m(bt(cS.slice(0,cB))):"")+(cK?"&cs="+m(cK):"")+"&send_image=0";for(cT in co){if(Object.prototype.hasOwnProperty.call(co,cT)){cz+="&"+cT+"="+co[cT]}}var cZ=[];if(cU){for(cT in cU){if(Object.prototype.hasOwnProperty.call(cU,cT)&&/^dimension\d+$/.test(cT)){var cD=cT.replace("dimension","");cZ.push(parseInt(cD,10));cZ.push(String(cD));cz+="&"+cT+"="+cU[cT];delete cU[cT]}}}if(cU&&s(cU)){cU=null +}for(cT in aT){if(Object.prototype.hasOwnProperty.call(aT,cT)){var cJ=(-1===cZ.indexOf(cT));if(cJ){cz+="&dimension"+cT+"="+aT[cT]}}}if(cU){cz+="&data="+m(JSON2.stringify(cU))}else{if(Y){cz+="&data="+m(JSON2.stringify(Y))}}function cG(c1,c2){var c3=JSON2.stringify(c1);if(c3.length>2){return"&"+c2+"="+m(c3)}return""}var cY=cw(bn);var cP=cw(bM);cz+=cG(cY,"cvar");cz+=cG(cP,"e_cvar");if(at){cz+=cG(at,"_cvar");for(cT in cQ){if(Object.prototype.hasOwnProperty.call(cQ,cT)){if(at[cT][0]===""||at[cT][1]===""){delete at[cT]}}}if(bi){ct(cL,JSON2.stringify(at),bP,aW,cf)}}if(aE){if(bN){cz+=">_ms="+bN}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){cz+=">_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}cN.lastEcommerceOrderTs=y(cA)&&String(cA).length?cA:cN.lastEcommerceOrderTs;ao(cN);bE();cz+=Q(cV);if(ch.length){cz+="&"+ch}if(r(bC)){cz=bC(cz)}return cz}bj=function aI(){var cx=new Date();if(cg+aH<=cx.getTime()){var cy=bS("ping=1",null,"ping");a8(cy,bb);return true}return false +};function aX(cA,cz,cE,cB,cx,cH){var cC="idgoal=0",cD,cy=new Date(),cF=[],cG;if(String(cA).length){cC+="&ec_id="+m(cA);cD=Math.round(cy.getTime()/1000)}cC+="&revenue="+cz;if(String(cE).length){cC+="&ec_st="+cE}if(String(cB).length){cC+="&ec_tx="+cB}if(String(cx).length){cC+="&ec_sh="+cx}if(String(cH).length){cC+="&ec_dt="+cH}if(ci){for(cG in ci){if(Object.prototype.hasOwnProperty.call(ci,cG)){if(!y(ci[cG][1])){ci[cG][1]=""}if(!y(ci[cG][2])){ci[cG][2]=""}if(!y(ci[cG][3])||String(ci[cG][3]).length===0){ci[cG][3]=0}if(!y(ci[cG][4])||String(ci[cG][4]).length===0){ci[cG][4]=1}cF.push(ci[cG])}}cC+="&ec_items="+m(JSON2.stringify(cF))}cC=bS(cC,Y,"ecommerce",cD);a8(cC,bb)}function bq(cx,cB,cA,cz,cy,cC){if(String(cx).length&&y(cB)){aX(cx,cB,cA,cz,cy,cC)}}function aY(cx){if(y(cx)){aX("",cx,"","","","")}}function br(cz,cA){var cx=new Date(),cy=bS("action_name="+m(V(cz||aQ)),cA,"log");a8(cy,bb)}function aC(cz,cy){var cA,cx="(^| )(piwik[_-]"+cy;if(cz){for(cA=0;cA0){cB=parseInt(cB,10);cE(cB)}})}function b1(){var cy,cz,cA={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"},cx=I.devicePixelRatio||1;if(!((new RegExp("MSIE")).test(e.userAgent))){if(e.mimeTypes&&e.mimeTypes.length){for(cy in cA){if(Object.prototype.hasOwnProperty.call(cA,cy)){cz=e.mimeTypes[cA[cy]]; +co[cy]=(cz&&cz.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&y(e.javaEnabled)&&e.javaEnabled()){co.java="1"}if(r(I.GearsFactory)){co.gears="1"}co.cookie=bw()}co.res=M.width*cx+"x"+M.height*cx}b1();aP();ao();return{getVisitorId:function(){return aA().uuid},getVisitorInfo:function(){return b7()},getAttributionInfo:function(){return be()},getAttributionCampaignName:function(){return be()[0]},getAttributionCampaignKeyword:function(){return be()[1]},getAttributionReferrerTimestamp:function(){return be()[2]},getAttributionReferrerUrl:function(){return be()[3]},setTrackerUrl:function(cx){ai=cx},getTrackerUrl:function(){return ai},getSiteId:function(){return bx},setSiteId:function(cx){bu(cx)},setUserId:function(cx){if(!y(cx)||!cx.length){return}a3=cx;bg=by(a3).substr(0,16)},getUserId:function(){return a3},setCustomData:function(cx,cy){if(L(cx)){Y=cx}else{if(!Y){Y={}}Y[cx]=cy}},getCustomData:function(){return Y},setCustomRequestProcessing:function(cx){bC=cx},appendToTrackingUrl:function(cx){ch=cx +},getRequest:function(cx){return bS(cx)},addPlugin:function(cx,cy){a[cx]=cy},setCustomDimension:function(cx,cy){cx=parseInt(cx,10);if(cx>0){if(!y(cy)){cy=""}if(!o(cy)){cy=String(cy)}aT[cx]=cy}},getCustomDimension:function(cx){cx=parseInt(cx,10);if(cx>0&&Object.prototype.hasOwnProperty.call(aT,cx)){return aT[cx]}},deleteCustomDimension:function(cx){cx=parseInt(cx,10);if(cx>0){delete aT[cx]}},setCustomVariable:function(cy,cx,cB,cz){var cA;if(!y(cz)){cz="visit"}if(!y(cx)){return}if(!y(cB)){cB=""}if(cy>0){cx=!o(cx)?String(cx):cx;cB=!o(cB)?String(cB):cB;cA=[cx.slice(0,aZ),cB.slice(0,aZ)];if(cz==="visit"||cz===2){b0();at[cy]=cA}else{if(cz==="page"||cz===3){bn[cy]=cA}else{if(cz==="event"){bM[cy]=cA}}}}},getCustomVariable:function(cy,cz){var cx;if(!y(cz)){cz="visit"}if(cz==="page"||cz===3){cx=bn[cy]}else{if(cz==="event"){cx=bM[cy]}else{if(cz==="visit"||cz===2){b0();cx=at[cy]}}}if(!y(cx)||(cx&&cx[0]==="")){return false}return cx},deleteCustomVariable:function(cx,cy){if(this.getCustomVariable(cx,cy)){this.setCustomVariable(cx,"","",cy) +}},storeCustomVariablesInCookie:function(){bi=true},setLinkTrackingTimer:function(cx){bb=cx},setDownloadExtensions:function(cx){if(o(cx)){cx=cx.split("|")}cn=cx},addDownloadExtensions:function(cy){var cx;if(o(cy)){cy=cy.split("|")}for(cx=0;cx1){if(console!==undefined&&console&&console.error){console.error("The method "+Z+' is registered more than once in "paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}}ae[Z]++}}}}return ad}X(I,"beforeunload",U,false);p(); +Date.prototype.getTimeAlias=Date.prototype.getTime;N=new F();var t=["disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setUserId","setSiteId","enableLinkTracking"];_paq=b(_paq,t);for(v=0;v<_paq.length;v++){if(_paq[v]){T(_paq[v])}}_paq=new x();d={addPlugin:function(Y,Z){a[Y]=Z},getTracker:function(Y,Z){if(!y(Z)){Z=this.getAsyncTracker().getSiteId()}if(!y(Y)){Y=this.getAsyncTracker().getTrackerUrl()}return new F(Y,Z)},getAsyncTracker:function(){return N}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return d})}return d}())}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c) +}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}; /*! @license-end */ }; \ No newline at end of file diff --git a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js index 87b053e980..3c983deca8 100644 --- a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js +++ b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js @@ -302,7 +302,7 @@ $('#javascript-text>textarea,#image-tracking-text>textarea').click(function () { $(this).select(); }); - + // initial generation getSiteData( $('#js-tracker-website').attr('siteid'), diff --git a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig index 25d0352733..6d66695552 100644 --- a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig +++ b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig @@ -75,7 +75,7 @@ {{ 'CoreAdminHome_JSTracking_MergeAliasesDesc'|translate(""~defaultReportSiteAlias~"")|raw }} diff --git a/plugins/Referrers/Columns/Base.php b/plugins/Referrers/Columns/Base.php index df86b60e65..54a9bd5e57 100644 --- a/plugins/Referrers/Columns/Base.php +++ b/plugins/Referrers/Columns/Base.php @@ -12,6 +12,8 @@ use Piwik\Common; use Piwik\Piwik; use Piwik\Plugin\Dimension\VisitDimension; use Piwik\Plugins\Referrers\SearchEngine AS SearchEngineDetection; +use Piwik\Plugins\SitesManager\SiteUrls; +use Piwik\Tracker\Cache; use Piwik\Tracker\PageUrl; use Piwik\Tracker\Request; use Piwik\Tracker\Visit; @@ -251,20 +253,34 @@ abstract class Base extends VisitDimension */ protected function detectReferrerDirectEntry() { - if (!empty($this->referrerHost)) { - // is the referrer host the current host? - if (isset($this->currentUrlParse['host'])) { - $currentHost = Common::mb_strtolower($this->currentUrlParse['host'], 'UTF-8'); - if ($currentHost == Common::mb_strtolower($this->referrerHost, 'UTF-8')) { - $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_DIRECT_ENTRY; - return true; - } + if (empty($this->referrerHost)) { + return false; + } + + $cache = Cache::getCacheGeneral(); + + if (!empty($cache['allUrlsByHostAndIdSite'])) { + $directEntry = new SiteUrls(); + $matchingSites = $directEntry->getIdSitesMatchingUrl($this->referrerUrlParse, $cache['allUrlsByHostAndIdSite']); + + if (isset($matchingSites) && is_array($matchingSites) && in_array($this->idsite, $matchingSites)) { + $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_DIRECT_ENTRY; + return true; + } elseif (isset($matchingSites)) { + return false; } - if (Visit::isHostKnownAliasHost($this->referrerHost, $this->idsite)) { + } + + // fallback logic if the referrer domain is not known to any site to not break BC + if (isset($this->currentUrlParse['host'])) { + // this might be actually buggy if first thing tracked is eg an outlink and referrer is from that site + $currentHost = Common::mb_strtolower($this->currentUrlParse['host']); + if ($currentHost == Common::mb_strtolower($this->referrerHost)) { $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_DIRECT_ENTRY; return true; } } + return false; } diff --git a/plugins/Referrers/Columns/Campaign.php b/plugins/Referrers/Columns/Campaign.php index ff2d5c2401..c29b622336 100644 --- a/plugins/Referrers/Columns/Campaign.php +++ b/plugins/Referrers/Columns/Campaign.php @@ -37,7 +37,7 @@ class Campaign extends Base /** * If we should create a new visit when the campaign changes, check if the campaign info changed and if so - * force the tracker to create a new visit. + * force the tracker to create a new visit.i * * @param Request $request * @param Visitor $visitor diff --git a/plugins/Referrers/Referrers.php b/plugins/Referrers/Referrers.php index ce8667929b..d262c79224 100644 --- a/plugins/Referrers/Referrers.php +++ b/plugins/Referrers/Referrers.php @@ -12,6 +12,7 @@ use Piwik\ArchiveProcessor; use Piwik\Common; use Piwik\Piwik; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; +use Piwik\Plugins\SitesManager\SiteUrls; /** * @see plugins/Referrers/functions.php @@ -31,9 +32,18 @@ class Referrers extends \Piwik\Plugin 'Insights.addReportToOverview' => 'addReportToInsightsOverview', 'Live.getAllVisitorDetails' => 'extendVisitorDetails', 'Request.getRenamedModuleAndAction' => 'renameDeprecatedModuleAndAction', + 'Tracker.setTrackerCacheGeneral' => 'setTrackerCacheGeneral' ); } + public function setTrackerCacheGeneral(&$cacheContent) + { + $siteUrls = new SiteUrls(); + $urls = $siteUrls->getAllCachedSiteUrls(); + + return $cacheContent['allUrlsByHostAndIdSite'] = $siteUrls->groupUrlsByHost($urls); + } + public function renameDeprecatedModuleAndAction(&$module, &$action) { if($module == 'Referers') { diff --git a/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php b/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php new file mode 100644 index 0000000000..c61ae04858 --- /dev/null +++ b/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php @@ -0,0 +1,121 @@ +referrerType = new ReferrerType(); + } + + public function tearDown() + { + // clean up your test here if needed + Cache::clearCacheGeneral(); + + parent::tearDown(); + } + + /** + * @dataProvider getReferrerUrls + */ + public function test_onNewVisit_shouldDetectCorrectReferrerType($expectedType, $idSite, $url, $referrerUrl) + { + $request = $this->getRequest(array('idsite' => $idSite, 'url' => $url, 'urlref' => $referrerUrl)); + $type = $this->referrerType->onNewVisit($request, $this->getNewVisitor(), $action = null); + + $this->assertSame($expectedType, $type); + } + + public function getReferrerUrls() + { + $url = 'http://piwik.org/foo/bar'; + $referrer = 'http://piwik.org'; + + return array( + // domain matches but path does not match for idsite1 + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer), + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer . '/'), + // idSite2 matches any piwik.org path so this is a direct entry for it + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite2, $url, $referrer), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite2, $url, $referrer . '/'), + // idSite3 has different domain so it is coming from different website + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer), + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer . '/'), + + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz/'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz?x=5'), + // /foo/bar/baz belongs to different website + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite2, $url, $referrer . '/foo/bar/baz'), + // website as it is from different domain anyway + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer . '/foo/bar/baz'), + + // should detect campaign independent of domain / path + array(Common::REFERRER_TYPE_CAMPAIGN, $this->idSite1, $url . '?pk_campaign=test', $referrer), + array(Common::REFERRER_TYPE_CAMPAIGN, $this->idSite2, $url . '?pk_campaign=test', $referrer), + array(Common::REFERRER_TYPE_CAMPAIGN, $this->idSite3, $url . '?pk_campaign=test', $referrer), + + array(Common::REFERRER_TYPE_SEARCH_ENGINE, $this->idSite3, $url, 'http://google.com/search?q=piwik'), + + // testing case for backwards compatibility where url has same domain as urlref but the domain is not known to any website + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite3, 'http://example.com/foo', 'http://example.com/bar'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite3, 'http://example.com/foo', 'http://example.com'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite3, 'http://example.com', 'http://example.com/bar'), + + // testing case where domain of referrer is not known to any site but neither is the URL, url != urlref + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, 'http://example.org', 'http://example.com/bar'), + ); + } + + private function getRequest($params) + { + return new Request($params); + } + + private function getNewVisitor() + { + return new Visitor(new VisitProperties()); + } + +} diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php index 9a6bf63469..739af7d5e9 100644 --- a/plugins/SitesManager/API.php +++ b/plugins/SitesManager/API.php @@ -645,6 +645,7 @@ class API extends \Piwik\Plugin\API { Site::clearCache(); Cache::regenerateCacheWebsiteAttributes($idSite); + Cache::clearCacheGeneral(); SiteUrls::clearSitesCache(); } diff --git a/plugins/SitesManager/Model.php b/plugins/SitesManager/Model.php index 93ddb01d8f..96062b608a 100644 --- a/plugins/SitesManager/Model.php +++ b/plugins/SitesManager/Model.php @@ -286,6 +286,22 @@ class Model return $urls; } + /** + * Returns the list of alias URLs registered for the given idSite. + * The website ID must be valid when calling this method! + * + * @param int $idSite + * @return array list of alias URLs + */ + public function getAllKnownUrlsForAllSites() + { + $db = $this->getDb(); + $mainUrls = $db->fetchAll("SELECT idsite, main_url as url FROM " . Common::prefixTable("site")); + $aliasUrls = $db->fetchAll("SELECT idsite, url FROM " . Common::prefixTable("site_url")); + + return array_merge($mainUrls, $aliasUrls); + } + public function updateSite($site, $idSite) { $idSite = (int) $idSite; diff --git a/plugins/SitesManager/SiteUrls.php b/plugins/SitesManager/SiteUrls.php index a1fac67a89..059521a4a9 100644 --- a/plugins/SitesManager/SiteUrls.php +++ b/plugins/SitesManager/SiteUrls.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\SitesManager; use Piwik\Cache; +use Piwik\Common; class SiteUrls { @@ -19,6 +20,96 @@ class SiteUrls self::getCache()->delete(self::$cacheId); } + /** + * Groups all URLs by host, path and idsite. + * + * @param array $urls An array containing URLs by idsite, + * eg array(array($idSite = 1 => array('apache.piwik', 'apache2.piwik'), 2 => array(), ...)) + * as returned by {@link getAllCachedSiteUrls()} and {@link getAllSiteUrls} + * @return array All urls grouped by host => path => idSites. Path having the most '/' will be listed first + * array( + 'apache.piwik' => array( + '/test/two' => $idsite = array(3), + '/test' => $idsite = array(1), + '/' => $idsite = array(2), + ), + 'test.apache.piwik' => array( + '/test/two' => $idsite = array(3), + '/test' => $idsite = array(1), + '/' => $idsite = array(2, 3), + ), + ); + */ + public function groupUrlsByHost($siteUrls) + { + if (empty($siteUrls)) { + return array(); + } + + $allUrls = array(); + + foreach ($siteUrls as $idSite => $urls) { + $idSite = (int) $idSite; + foreach ($urls as $url) { + $urlParsed = @parse_url($url); + + if ($urlParsed === false || !isset($urlParsed['host'])) { + continue; + } + + $host = $this->toCanonicalHost($urlParsed['host']); + $path = $this->getCanonicalPathFromParsedUrl($urlParsed); + + if (!isset($allUrls[$host])) { + $allUrls[$host] = array(); + } + + if (!isset($allUrls[$host][$path])) { + $allUrls[$host][$path] = array(); + } + + if (!in_array($idSite, $allUrls[$host][$path])) { + $allUrls[$host][$path][] = $idSite; + } + } + } + + foreach ($allUrls as $host => $paths) { + uksort($paths, array($this, 'sortByPathDepth')); + $allUrls[$host] = $paths; + } + + return $allUrls; + } + + public function getIdSitesMatchingUrl($parsedUrl, $urlsGroupedByHost) + { + if (empty($parsedUrl['host'])) { + return null; + } + + $urlHost = $this->toCanonicalHost($parsedUrl['host']); + $urlPath = $this->getCanonicalPathFromParsedUrl($parsedUrl); + + $matchingSites = null; + if (isset($urlsGroupedByHost[$urlHost])) { + $paths = $urlsGroupedByHost[$urlHost]; + + foreach ($paths as $path => $idSites) { + if (0 === strpos($urlPath, $path)) { + $matchingSites = $idSites; + break; + } + } + + if (!isset($matchingSites) && isset($paths['/'])) { + $matchingSites = $paths['/']; + } + } + + return $matchingSites; + } + public function getAllCachedSiteUrls() { $cache = $this->getCache(); @@ -35,23 +126,66 @@ class SiteUrls public function getAllSiteUrls() { $model = new Model(); - $siteIds = $model->getSitesId(); - $siteUrls = array(); + $siteUrls = $model->getAllKnownUrlsForAllSites(); - if (empty($siteIds)) { + if (empty($siteUrls)) { return array(); } - foreach ($siteIds as $siteId) { - $siteId = (int) $siteId; - $siteUrls[$siteId] = $model->getSiteUrlsFromId($siteId); + $urls = array(); + foreach ($siteUrls as $siteUrl) { + $siteId = (int) $siteUrl['idsite']; + + if (!isset($urls[$siteId])) { + $urls[$siteId] = array(); + } + + $urls[$siteId][] = $siteUrl['url']; } - return $siteUrls; + return $urls; } private static function getCache() { return Cache::getLazyCache(); } + + private function sortByPathDepth($pathA, $pathB) + { + // list first the paths with most '/' , and list path = '/' last + $numSlashA = substr_count($pathA, '/'); + $numSlashB = substr_count($pathB, '/'); + + if ($numSlashA === $numSlashB) { + return -1 * strcmp($pathA, $pathB); + } + + return $numSlashA > $numSlashB ? -1 : 1; + } + + private function toCanonicalHost($host) + { + $host = Common::mb_strtolower($host); + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; + } + + private function getCanonicalPathFromParsedUrl($urlParsed) + { + $path = '/'; + + if (isset($urlParsed['path'])) { + $path = Common::mb_strtolower($urlParsed['path']); + if (!Common::stringEndsWith($path, '/')) { + $path .= '/'; + } + } + + return $path; + } + } diff --git a/plugins/SitesManager/tests/Integration/ModelTest.php b/plugins/SitesManager/tests/Integration/ModelTest.php index 45ebce2be8..7291ac346f 100644 --- a/plugins/SitesManager/tests/Integration/ModelTest.php +++ b/plugins/SitesManager/tests/Integration/ModelTest.php @@ -56,10 +56,56 @@ class ModelTest extends IntegrationTestCase $this->assertSame(array('website', 'universal', 'mobileapp'), $this->model->getUsedTypeIds()); } - private function createMeasurable($type) + public function test_getAllKnownUrlsForAllSites_shouldReturnAllUrls() { - Fixture::createWebsite('2015-01-01 00:00:00', - $ecommerce = 0, $siteName = false, $siteUrl = false, + $idSite = $this->createMeasurable('website', 'http://apache.piwik'); + $this->model->insertSiteUrl($idSite, 'http://example.apache.piwik'); + $this->model->insertSiteUrl($idSite, 'http://example.org'); + + $idSite2 = $this->createMeasurable('website'); + $this->model->insertSiteUrl($idSite2, 'http://example.org'); + $this->model->insertSiteUrl($idSite2, 'http://example.com'); + + $idSite3 = $this->createMeasurable('website', 'http://example.pro'); + + $expected = array( + array( + 'idsite' => $idSite, + 'url' => 'http://apache.piwik' + ), + array( + 'idsite' => $idSite2, + 'url' => 'http://piwik.net' + ), + array( + 'idsite' => $idSite3, + 'url' => 'http://example.pro' + ), + array( + 'idsite' => $idSite, + 'url' => 'http://example.apache.piwik' + ), + array( + 'idsite' => $idSite, + 'url' => 'http://example.org' + ), + array( + 'idsite' => $idSite2, + 'url' => 'http://example.com' + ), + array( + 'idsite' => $idSite2, + 'url' => 'http://example.org' + ) + + ); + $this->assertEquals($expected, $this->model->getAllKnownUrlsForAllSites()); + } + + private function createMeasurable($type, $siteUrl = false) + { + return Fixture::createWebsite('2015-01-01 00:00:00', + $ecommerce = 0, $siteName = false, $siteUrl, $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $timezone = null, $type); } diff --git a/plugins/SitesManager/tests/Integration/SiteUrlsTest.php b/plugins/SitesManager/tests/Integration/SiteUrlsTest.php index f9362ae2f4..894e94fc0f 100644 --- a/plugins/SitesManager/tests/Integration/SiteUrlsTest.php +++ b/plugins/SitesManager/tests/Integration/SiteUrlsTest.php @@ -119,6 +119,130 @@ class SiteUrlsTest extends IntegrationTestCase $this->assertEquals($urlsToFake, $actual); } + public function test_groupUrlsByHost_shouldReturnEmptyArray_WhenNoUrlsGiven() + { + $this->assertSame(array(), $this->siteUrls->groupUrlsByHost(array())); + $this->assertSame(array(), $this->siteUrls->groupUrlsByHost(null)); + } + + public function test_groupUrlsByHost_shouldGroupByHost_WithOneSiteAndDifferentDomains_shouldRemoveWwwAndDefaultToPathSlash() + { + $idSite = 1; + $oneSite = array( + $idSite => array( + 'http://apache.piwik', + 'http://www.example.com', // should remove www. + 'https://example.org', // should handle https or other protocol + 'http://apache.piwik/', // same as initial one but with slash at the end, should not add idsite twice + 'http://third.www.com' // should not remove www. in the middle of a domain + ) + ); + + $expected = array( + 'apache.piwik' => array('/' => array($idSite)), + 'example.com' => array('/' => array($idSite)), + 'example.org' => array('/' => array($idSite)), + 'third.www.com' => array('/' => array($idSite)), + ); + + $this->assertSame($expected, $this->siteUrls->groupUrlsByHost($oneSite)); + } + + public function test_groupUrlsByHost_shouldGroupByHost_WithDifferentDomainsAndPathsShouldListPathByNumberOfDirectoriesAndConvertToLowerCase() + { + $idSite = 1; + $idSite2 = 2; + $idSite3 = 3; + $idSite4 = 4; + $idSite5 = 5; + + $urls = array( + $idSite => array( + 'http://apache.piwik/test', 'http://apache.piWik', 'http://apache.piwik/foo/bAr/' + ), + $idSite2 => array( + 'http://apache.piwik/test/', 'http://example.oRg', 'http://apache.piwik/foo/secOnd' + ), + $idSite3 => array( + 'http://apache.piwik/', 'http://apache.piwik/third', 'http://exampLe.com', 'http://example.org/foo/test/two' + ), + $idSite4 => array(), + $idSite5 => array('invalidUrl', 'ftp://example.org/'), + ); + + $expected = array( + 'apache.piwik' => array( + '/foo/second/' => array($idSite2), + '/foo/bar/' => array($idSite), + '/third/' => array($idSite3), + '/test/' => array($idSite, $idSite2), + '/' => array($idSite, $idSite3) + ), + 'example.org' => array( + '/foo/test/two/' => array($idSite3), + '/' => array($idSite2, $idSite5) + ), + 'example.com' => array( + '/' => array($idSite3) + ), + ); + + $this->assertSame($expected, $this->siteUrls->groupUrlsByHost($urls)); + } + + /** + * @dataProvider getTestIdSitesMatchingUrl + */ + public function test_getIdSitesMatchingUrl($expectedMatchSites, $parsedUrl) + { + $urlsGroupedByHost = array( + 'apache.piwik' => array( + '/foo/second/' => array(2), + '/foo/sec/' => array(4), + '/foo/bar/' => array(1), + '/third/' => array(3), + '/test/' => array(1, 2), + '/' => array(1, 3) + ), + 'example.org' => array( + '/foo/test/two/' => array(3), + '/foo/second/' => array(6), + '/' => array(2, 5) + ), + 'example.com' => array( + '/' => array(3) + ), + ); + $matchedSites = $this->siteUrls->getIdSitesMatchingUrl($parsedUrl, $urlsGroupedByHost); + + $this->assertSame($expectedMatchSites, $matchedSites); + } + + public function getTestIdSitesMatchingUrl() + { + return array( + array(array(1,3), array('host' => 'apache.piwik')), + array(array(1,3), array('host' => 'apache.piwik', 'path' => '/')), + array(array(1,3), array('host' => 'apache.piwik', 'path' => 'nomatch')), // no other URL matches a site so we fall back to domain match + array(array(1,3), array('host' => 'apache.piwik', 'path' => '/nomatch')), + array(array(2), array('host' => 'apache.piwik', 'path' => '/foo/second')), + array(array(2), array('host' => 'apache.piwik', 'path' => '/foo/second/')), // it shouldn't matter if slash is at end or not + array(array(2), array('host' => 'apache.piwik', 'path' => '/foo/second/test')), // it should find best match + array(array(4), array('host' => 'apache.piwik', 'path' => '/foo/sec/test')), // it should not use /foo/second for these + array(array(4), array('host' => 'apache.piwik', 'path' => '/foo/sec/')), + array(array(4), array('host' => 'apache.piwik', 'path' => '/foo/sec')), + array(array(1,3), array('host' => 'apache.piwik', 'path' => '/foo')), + array(array(2,5), array('host' => 'example.org')), + array(array(2,5), array('host' => 'example.org', 'path' => '/')), + array(array(2,5), array('host' => 'example.org', 'path' => 'any/nonmatching/path')), + array(array(6), array('host' => 'example.org', 'path' => '/foo/second')), + array(array(6), array('host' => 'example.org', 'path' => '/foo/second/test')), + array(array(3), array('host' => 'example.com')), + array(null, array('host' => 'example.pro')), + array(null, array('host' => 'example.pro', 'path' => '/any')), + ); + } + private function assertSiteUrls($expectedUrls) { $urls = $this->siteUrls->getAllSiteUrls(); diff --git a/tests/javascript/index.php b/tests/javascript/index.php index 71a125eb0c..dfcf7ec831 100644 --- a/tests/javascript/index.php +++ b/tests/javascript/index.php @@ -2242,12 +2242,46 @@ function PiwikTest() { } }); - test("Tracker setDomains() and isSiteHostName()", function() { - expect(13); + test("Tracker setDomains(), isSiteHostName(), isSiteHostPath() and getLinkIfShouldBeProcessed()", function() { + expect(102); var tracker = Piwik.getTracker(); + var initialDomains = tracker.getDomains(); + var domainAlias = initialDomains[0]; equal( typeof tracker.hook.test._isSiteHostName, 'function', "isSiteHostName" ); + equal( typeof tracker.hook.test._isSiteHostPath, 'function', "isSiteHostPath" ); + equal( typeof tracker.hook.test._getLinkIfShouldBeProcessed, 'function', "getLinkIfShouldBeProcessed" ); + + var isSiteHostName = tracker.hook.test._isSiteHostName; + var isSiteHostPath = tracker.hook.test._isSiteHostPath; + var getLinkIfShouldBeProcessed = tracker.hook.test._getLinkIfShouldBeProcessed; + + // tracker.setDomain() + + // test wildcards + tracker.setDomains( ['*.Example.com'] ); + propEqual(["*.Example.com", domainAlias], tracker.getDomains()), 'should add domainAlias'; + + tracker.setDomains( '*.Example.org' ); + propEqual(["*.Example.org", domainAlias], tracker.getDomains()), 'should handle a string'; + + tracker.setDomains( ['*.Example.com', '*.example.ORG'] ); + propEqual(["*.Example.com", '*.example.ORG', domainAlias], tracker.getDomains()), 'should be able to set many domains'; + + tracker.setDomains( [] ); + propEqual([domainAlias], tracker.getDomains()), 'setting an empty array should reset the list'; + + tracker.setDomains( ['*.Example.com', domainAlias + '/path', '*.example.ORG'] ); + propEqual(['*.Example.com', domainAlias + '/path', '*.example.ORG'], tracker.getDomains()), 'if domain alias is already given should not add domainAlias'; + + tracker.setDomains( ['.' + domainAlias + '/path'] ); + propEqual(['.' + domainAlias + '/path'], tracker.getDomains()), 'if domain alias with subdomain is already given should not add domainAlias'; + + + /** + * isSiteHostName () + */ // test wildcards tracker.setDomains( ['*.Example.com'] ); @@ -2255,21 +2289,129 @@ function PiwikTest() { // skip test if testing on localhost ok( window.location.hostname != 'localhost' ? !tracker.hook.test._isSiteHostName('localhost') : true, '!isSiteHostName("localhost")' ); - ok( !tracker.hook.test._isSiteHostName('google.com'), '!isSiteHostName("google.com")' ); - ok( tracker.hook.test._isSiteHostName('example.com'), 'isSiteHostName("example.com")' ); - ok( tracker.hook.test._isSiteHostName('www.example.com'), 'isSiteHostName("www.example.com")' ); - ok( tracker.hook.test._isSiteHostName('www.sub.example.com'), 'isSiteHostName("www.sub.example.com")' ); + ok( !isSiteHostName('google.com'), '!isSiteHostName("google.com")' ); + ok( isSiteHostName('example.com'), 'isSiteHostName("example.com")' ); + ok( isSiteHostName('www.example.com'), 'isSiteHostName("www.example.com")' ); + ok( isSiteHostName('www.sub.example.com'), 'isSiteHostName("www.sub.example.com")' ); tracker.setDomains( 'dev.piwik.org' ); - ok( !tracker.hook.test._isSiteHostName('piwik.org'), '!isSiteHostName("piwik.org")' ); - ok( tracker.hook.test._isSiteHostName('dev.piwik.org'), 'isSiteHostName("dev.piwik.org")' ); - ok( !tracker.hook.test._isSiteHostName('piwik.example.org'), '!isSiteHostName("piwik.example.org")'); - ok( !tracker.hook.test._isSiteHostName('dev.piwik.org.com'), '!isSiteHostName("dev.piwik.org.com")'); + ok( !isSiteHostName('piwik.org'), '!isSiteHostName("piwik.org")' ); + ok( isSiteHostName('dev.piwik.org'), 'isSiteHostName("dev.piwik.org")' ); + ok( !isSiteHostName('piwik.example.org'), '!isSiteHostName("piwik.example.org")'); + ok( !isSiteHostName('dev.piwik.org.com'), '!isSiteHostName("dev.piwik.org.com")'); tracker.setDomains( '.piwik.org' ); - ok( tracker.hook.test._isSiteHostName('piwik.org'), 'isSiteHostName("piwik.org")' ); - ok( tracker.hook.test._isSiteHostName('dev.piwik.org'), 'isSiteHostName("dev.piwik.org")' ); - ok( !tracker.hook.test._isSiteHostName('piwik.org.com'), '!isSiteHostName("piwik.org.com")'); + ok( isSiteHostName('piwik.org'), 'isSiteHostName("piwik.org")' ); + ok( isSiteHostName('dev.piwik.org'), 'isSiteHostName("dev.piwik.org")' ); + ok( !isSiteHostName('piwik.org.com'), '!isSiteHostName("piwik.org.com")'); + + /** + * isSiteHostPath () + */ + + // with path + tracker.setDomains( '.piwik.org/path' ); + ok( isSiteHostPath('piwik.org', '/path'), 'isSiteHostPath("piwik.org", "/path")' ); + ok( isSiteHostPath('piwik.org', '/path/'), 'isSiteHostPath("piwik.org", "/path/")' ); + ok( isSiteHostPath('piwik.org', '/path/test'), 'isSiteHostPath("piwik.org", "/path/test)' ); + ok( isSiteHostPath('dev.piwik.org', '/path'), 'isSiteHostPath("dev.piwik.org", "/path")' ); + ok( !isSiteHostPath('piwik.org', '/pat'), '!isSiteHostPath("piwik.org", "/pat")'); + ok( !isSiteHostPath('piwik.org', '.com'), '!isSiteHostPath("piwik.org", ".com")'); + ok( !isSiteHostPath('piwik.com', '/path'), '!isSiteHostPath("piwik.com", "/path")'); + ok( !isSiteHostPath('piwik.com', '/path/test'), '!isSiteHostPath("piwik.com", "/path/test")'); + ok( !isSiteHostPath('piwik.com', ''), '!isSiteHostPath("piwik.com", "/path/test")'); + + // no path + var domains = ['.piwik.org', 'piwik.org', '*.piwik.org', '.piwik.org/']; + for (var i in domains) { + var domain = domains[i]; + tracker.setDomains( domain ); + ok( isSiteHostPath('piwik.org', '/path'), 'isSiteHostPath("piwik.org", "/path"), domain: ' + domain ); + ok( isSiteHostPath('piwik.org', '/path/'), 'isSiteHostPath("piwik.org", "/path/"), domain: ' + domain ); + ok( isSiteHostPath('piwik.org', '/path/test'), 'isSiteHostPath("piwik.org", "/path/test), domain: ' + domain ); + + if (domain === 'piwik.org') { + ok( !isSiteHostPath('dev.piwik.org', '/path'), 'isSiteHostPath("dev.piwik.org", "/path"), domain: ' + domain ); + } else { + ok( isSiteHostPath('dev.piwik.org', '/path'), 'isSiteHostPath("dev.piwik.org", "/path"), domain: ' + domain ); + } + ok( isSiteHostPath('piwik.org', '/pat'), '!isSiteHostPath("piwik.org", "/pat"), domain: ' + domain ); + ok( isSiteHostPath('piwik.org', '.com'), '!isSiteHostPath("piwik.org", ".com"), domain: ' + domain); + ok( isSiteHostPath('piwik.org', '/foo'), '!isSiteHostPath("piwik.com", "/foo"), domain: ' + domain); + ok( !isSiteHostPath('piwik.com', '/path'), '!isSiteHostPath("piwik.com", "/path"), domain: ' + domain); + ok( !isSiteHostPath('piwik.com', '/path/test'), '!isSiteHostPath("piwik.com", "/path/test"), domain: ' + domain); + ok( !isSiteHostPath('piwik.com', ''), '!isSiteHostPath("piwik.com", "/path/test"), domain: ' + domain); + } + + // multiple paths / domains + tracker.setDomains( ['piwik.org/path', 'piwik.org/foo', 'piwik.org/bar/baz', '.piwik.pro/test'] ); + ok( isSiteHostPath('piwik.pro', '/test/bar'), 'isSiteHostPath("piwik.pro", "/test/bar")' ); + ok( !isSiteHostPath('piwik.org', '/foobar/'), 'isSiteHostPath("piwik.org", "/foobar/")' ); + ok( isSiteHostPath('piwik.org', '/foo/bar'), 'isSiteHostPath("piwik.org", "/foo/bar")' ); + ok( isSiteHostPath('piwik.org', '/bar/baz/foo'), 'isSiteHostPath("piwik.org", "/bar/baz/foo/")' ); + ok( !isSiteHostPath('piwik.org', '/bar/ba'), 'isSiteHostPath("piwik.org", "/bar/ba")' ); + ok( isSiteHostPath('piwik.org', '/path/test'), 'isSiteHostPath("piwik.org", "/path/test)' ); + ok( isSiteHostPath('dev.piwik.pro', '/test'), 'isSiteHostPath("dev.piwik.pro", "/test")' ); + ok( !isSiteHostPath('dev.piwik.pro', '/'), 'isSiteHostPath("dev.piwik.pro", "/")' ); + ok( !isSiteHostPath('piwik.pro', '/'), 'isSiteHostPath("piwik.pro", "/")' ); + ok( !isSiteHostPath('piwik.org', '/'), 'isSiteHostPath("piwik.org", "/")' ); + ok( !isSiteHostPath('piwik.org', '/anythingelse'), 'isSiteHostPath("piwik.org", "/anythingelse")' ); + + // all is compared lower case + tracker.setDomains( '.piwik.oRg/PaTh' ); + ok( isSiteHostPath('piwiK.org', '/pAth'), 'isSiteHostPath("piwik.org", "/path")' ); + ok( isSiteHostPath('piwik.org', '/patH/'), 'isSiteHostPath("piwik.org", "/path/")' ); + ok( isSiteHostPath('Piwik.ORG', '/PATH/TEST'), 'isSiteHostPath("piwik.org", "/path/test)' ); + + /** + * getLinkIfShouldBeProcessed () + */ + var getLinkIfShouldBeProcessed = tracker.hook.test._getLinkIfShouldBeProcessed; + function createLink(url) { + var link = document.createElement('a'); + link.href = url; + return link; + } + + tracker.setDomains( ['.piwik.org/path', '.piwik.org/foo', '.piwik.org/bar/baz', '.piwik.pro/test'] ); + + // they should not be detected as outlink as they match one of the domains + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo/bar')), 'getLinkIfShouldBeProcessed http://www.piwik.org/foo/bar matches .piwik.org/foo') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://piwik.org/foo/bar')), 'getLinkIfShouldBeProcessed http://piwik.org/foo/bar matches .piwik.org/foo') + equal(undefined, getLinkIfShouldBeProcessed(createLink('piwik.org/foo/bar')), 'getLinkIfShouldBeProcessed missing protocol only domain given') + equal(undefined, getLinkIfShouldBeProcessed(createLink('//piwik.org/foo/bar')), 'getLinkIfShouldBeProcessed no protcol but url starts with //') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo?x=1')), 'getLinkIfShouldBeProcessed url with query parameter should detect correct path') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo')), 'getLinkIfShouldBeProcessed path is same as allowed path') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo/')), 'getLinkIfShouldBeProcessed path is same as allowed path but with appended slash') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar/baz/')), 'getLinkIfShouldBeProcessed multiple directories with appended slash') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar/baz')), 'getLinkIfShouldBeProcessed multiple directories') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://WWW.PIWIK.ORG/BAR/BAZ')), 'getLinkIfShouldBeProcessed should test everything lowercase') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar/baz/x/y/z')), 'getLinkIfShouldBeProcessed many appended paths') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar/baz?test=1&foo=bar')), 'getLinkIfShouldBeProcessed another test with query parameter and multiple directories') + propEqual({ + "href": "http://www.piwik.org/foo/download.apk", + "type": "download" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo/download.apk')), 'getLinkIfShouldBeProcessed should detect download even if it is link to same domain') + propEqual({ + "href": "http://www.piwik.org/foobar/download.apk", + "type": "download" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foobar/download.apk')), 'getLinkIfShouldBeProcessed should detect download even if it goes to different domain/path') + propEqual({ + "href": "http://www.piwik.com/foobar/download.apk", + "type": "download" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.com/foobar/download.apk')), 'getLinkIfShouldBeProcessed should detect download even if it goes to different domain') + propEqual({ + "href": "http://www.piwik.pro/foo/", + "type": "link" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.pro/foo/')), 'getLinkIfShouldBeProcessed path matches but domain not so outlink') + propEqual({ + "href": "http://www.piwik.org/bar", + "type": "link" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar')), 'getLinkIfShouldBeProcessed domain matches but path not so outlink') + propEqual({ + "href": "http://www.piwik.org/footer", + "type": "link" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/footer')), 'getLinkIfShouldBeProcessed http://www.piwik.org/footer and there is domain piwik.org/foo but it should be outlink as path is different') }); test("Tracker getClassesRegExp()", function() { diff --git a/tests/javascript/piwiktest.js b/tests/javascript/piwiktest.js index 415bd57d8a..36cccc66a1 100644 --- a/tests/javascript/piwiktest.js +++ b/tests/javascript/piwiktest.js @@ -20,11 +20,13 @@ Piwik.addPlugin('testPlugin', { '_isObject : isObject,' + '_isString : isString,' + '_isSiteHostName : isSiteHostName,' + + '_isSiteHostPath : isSiteHostPath,' + '_getClassesRegExp : getClassesRegExp,' + '_hasCookies : hasCookies,' + '_getCookie : getCookie,' + '_getCookieName : getCookieName,' + '_setCookie : setCookie,' + + '_getLinkIfShouldBeProcessed : getLinkIfShouldBeProcessed,' + '_encode : encodeWrapper,' + '_decode : decodeWrapper,' + '_urldecode : urldecode,' + -- cgit v1.2.3 From c11251c1def82c79e6ea608b57dc8898d9ffe60f Mon Sep 17 00:00:00 2001 From: mattab Date: Fri, 4 Dec 2015 16:10:40 +1300 Subject: Minor: new test cases --- plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php | 6 ++++++ plugins/SitesManager/tests/Integration/SiteUrlsTest.php | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php b/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php index c61ae04858..0d5e0a68ca 100644 --- a/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php +++ b/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php @@ -72,6 +72,7 @@ class ReferrerTypeTest extends IntegrationTestCase $url = 'http://piwik.org/foo/bar'; $referrer = 'http://piwik.org'; + // $expectedType, $idSite, $url, $referrerUrl return array( // domain matches but path does not match for idsite1 array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer), @@ -86,8 +87,13 @@ class ReferrerTypeTest extends IntegrationTestCase array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz'), array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz/'), array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz?x=5'), + // /not/xyz belongs to different website + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer . '/not/xyz'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite2, $url, $referrer . '/not/xyz'), + // /foo/bar/baz belongs to different website array(Common::REFERRER_TYPE_WEBSITE, $this->idSite2, $url, $referrer . '/foo/bar/baz'), + // website as it is from different domain anyway array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer . '/foo/bar/baz'), diff --git a/plugins/SitesManager/tests/Integration/SiteUrlsTest.php b/plugins/SitesManager/tests/Integration/SiteUrlsTest.php index 894e94fc0f..39d628197e 100644 --- a/plugins/SitesManager/tests/Integration/SiteUrlsTest.php +++ b/plugins/SitesManager/tests/Integration/SiteUrlsTest.php @@ -158,7 +158,7 @@ class SiteUrlsTest extends IntegrationTestCase $urls = array( $idSite => array( - 'http://apache.piwik/test', 'http://apache.piWik', 'http://apache.piwik/foo/bAr/' + 'http://apache.piwik/test', 'http://apache.piWik', 'http://apache.piwik/foo/bAr/', 'http://apache.piwik/Foo/SECOND' ), $idSite2 => array( 'http://apache.piwik/test/', 'http://example.oRg', 'http://apache.piwik/foo/secOnd' @@ -172,7 +172,7 @@ class SiteUrlsTest extends IntegrationTestCase $expected = array( 'apache.piwik' => array( - '/foo/second/' => array($idSite2), + '/foo/second/' => array($idSite, $idSite2), '/foo/bar/' => array($idSite), '/third/' => array($idSite3), '/test/' => array($idSite, $idSite2), -- cgit v1.2.3 From 66a15fd6457df333fa3e8bdd0a5c80674ca99dc4 Mon Sep 17 00:00:00 2001 From: Thomas Steur Date: Fri, 4 Dec 2015 03:11:45 +0000 Subject: set a cookie path automatically if a configHostAlias matches a path --- js/piwik.js | 77 ++++++++++++++++++++++++++++++++++++++++-- piwik.js | 54 ++++++++++++++--------------- tests/javascript/enable_sqlite | 0 tests/javascript/index.php | 46 +++++++++++++++++++++++-- tests/javascript/piwiktest.js | 1 + 5 files changed, 146 insertions(+), 32 deletions(-) create mode 100644 tests/javascript/enable_sqlite diff --git a/js/piwik.js b/js/piwik.js index 66e5ff6b75..a2fc4c0595 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -3046,6 +3046,11 @@ if (typeof Piwik !== 'object') { return str.indexOf(suffix, str.length - suffix.length) !== -1; } + function removeCharactersFromEndOfString(str, numCharactersToRemove) { + str = String(str); + return str.substr(0, str.length - numCharactersToRemove); + } + /* * Extract pathname from URL. element.pathname is actually supported by pretty much all browsers including * IE6 apart from some rare very old ones @@ -4886,6 +4891,59 @@ if (typeof Piwik !== 'object') { }); } + /** + * Note: While we check whether the user is on a configHostAlias path we do not check whether the user is + * actually on the configHostAlias domain. This is already done where this method is called and for + * simplicity we do not check this again. + * + * Also we currently assume that all configHostAlias domains start with the same wild card of '*.', '.' or + * none. Eg either all like '*.piwik.org' or '.piwik.org' or 'piwik.org'. Piwik always adds '*.' so it + * should be fine. + */ + function findConfigCookiePathToUse(configHostAlias, currentUrl) + { + var aliasPath = getPathName(configHostAlias); + var currentPath = getPathName(currentUrl); + + if (!aliasPath || aliasPath === '/' || !currentPath || currentPath === '/') { + // no path set that would be useful for cookiePath + return; + } + + var aliasDomain = domainFixup(configHostAlias); + + if (isSiteHostPath(aliasDomain, '/')) { + // there is another configHostsAlias having same domain that allows all paths + // eg this alias is for piwik.org/support but there is another alias allowing + // piwik.org + return; + } + + if (stringEndsWith(aliasPath, '/')) { + aliasPath = removeCharactersFromEndOfString(aliasPath, 1); + } + + // eg if we're in the case of "apache.piwik/foo/bar" we check whether there is maybe + // also a config alias allowing "apache.piwik/foo". In this case we're not allowed to set + // the cookie for "/foo/bar" but "/foo" + var pathAliasParts = aliasPath.split('/'); + for (var i = 2; i < pathAliasParts.length; i++) { + var lessRestrctivePath = pathAliasParts.slice(0, i).join('/'); + if (isSiteHostPath(aliasDomain, lessRestrctivePath)) { + aliasPath = lessRestrctivePath; + break; + } + } + + if (!isSitePath(currentPath, aliasPath)) { + // current path of current URL does not match the alias + // eg user is on piwik.org/demo but configHostAlias is for piwik.org/support + return; + } + + return aliasPath; + } + /* * Browser features (plugins, resolution, cookies) */ @@ -5026,6 +5084,9 @@ if (typeof Piwik !== 'object') { getDomains: function () { return configHostsAlias; }, + getConfigCookiePath: function () { + return configCookiePath; + }, getConfigDownloadExtensions: function () { return configDownloadExtensions; }, @@ -5467,10 +5528,20 @@ if (typeof Piwik !== 'object') { var hasDomainAliasAlready = false, i; for (i in configHostsAlias) { + var configHostAliasDomain = domainFixup(String(configHostsAlias[i])); + if (Object.prototype.hasOwnProperty.call(configHostsAlias, i) - && isSameHost(domainAlias, domainFixup(configHostsAlias[i]))) { + && isSameHost(domainAlias, configHostAliasDomain)) { hasDomainAliasAlready = true; - break; + + if (!configCookiePath) { + var path = findConfigCookiePathToUse(configHostsAlias[i], locationHrefAlias); + if (path) { + this.setCookiePath(path); + } + + break; + } } } @@ -6322,7 +6393,7 @@ if (typeof Piwik !== 'object') { asyncTracker = new Tracker(); - var applyFirst = ['disableCookies', 'setTrackerUrl', 'setAPIUrl', 'setCookiePath', 'setCookieDomain', 'setUserId', 'setSiteId', 'enableLinkTracking']; + var applyFirst = ['disableCookies', 'setTrackerUrl', 'setAPIUrl', 'setCookiePath', 'setCookieDomain', 'setDomains', 'setUserId', 'setSiteId', 'enableLinkTracking']; _paq = applyMethodsInOrder(_paq, applyFirst); // apply the queue of actions diff --git a/piwik.js b/piwik.js index f0fbdf735f..c76fb8e4ef 100644 --- a/piwik.js +++ b/piwik.js @@ -35,32 +35,32 @@ return this.toAbsoluteUrl(Z)}},isSameDomain:function(Y){if(!Y||!Y.indexOf){retur }}}var ae=S.findNodesByTagName(ac,"embed");if(ae&&ae.length){return this.findMediaUrlInNode(ae[0])}}},trim:function(Y){if(Y&&String(Y)===Y){return Y.replace(/^\s+|\s+$/g,"")}return Y},isOrWasNodeInViewport:function(ad){if(!ad||!ad.getBoundingClientRect||ad.nodeType!==1){return true}var ac=ad.getBoundingClientRect();var ab=w.documentElement||{};var aa=ac.top<0;if(aa&&ad.offsetTop){aa=(ad.offsetTop+ac.height)>0}var Z=ab.clientWidth;if(I.innerWidth&&Z>I.innerWidth){Z=I.innerWidth}var Y=ab.clientHeight;if(I.innerHeight&&Y>I.innerHeight){Y=I.innerHeight}return((ac.bottom>0||aa)&&ac.right>0&&ac.left=0){cz=cz.slice(0,cy)}cy=cz.lastIndexOf("/");if(cy!==cz.length-1){cz=cz.slice(0,cy+1)}return cz+cx}function b2(cz,cx){var cy;cz=String(cz).toLowerCase();cx=String(cx).toLowerCase();if(cz===cx){return true}if(cx.slice(0,1)==="."){if(cz===cx.slice(1)){return true}cy=cz.length-cx.length;if((cy>0)&&(cz.slice(cy)===cx)){return true}}return false}function ck(cy,cx){cy=String(cy);return cy.indexOf(cx,cy.length-cx.length)!==-1}function bL(cx){var cy=document.createElement("a"); -if(cx.indexOf("//")!==0&&cx.indexOf("http")!==0){cx="http://"+cx}cy.href=n.toAbsoluteUrl(cx);if(cy.pathname){return cy.pathname}return""}function aF(cy,cx){var cz=(!cx||cx==="/");if(cz){return true}if(cy===cx){return true}if(!cy){return false}cx=String(cx).toLowerCase();cy=String(cy).toLowerCase();if(!ck(cy,"/")){cy+="/"}if(!ck(cx,"/")){cx+="/"}return cy.indexOf(cx)===0}function ab(cB,cD){var cy,cx,cz,cA,cC;for(cy=0;cy0)&&(cA.slice(cz)===cx)){return true}}}return false}function bQ(cx,cz){var cy=new Image(1,1);cy.onload=function(){v=0;if(typeof cz==="function"){cz()}};cy.src=ai+(ai.indexOf("?")<0?"?":"&")+cx}function cl(cy,cB,cx){if(!y(cx)||null===cx){cx=true}try{var cA=I.XMLHttpRequest?new I.XMLHttpRequest():I.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null; -cA.open("POST",ai,true);cA.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cx){bQ(cy,cB)}else{if(typeof cB==="function"){cB()}}};cA.setRequestHeader("Content-Type",bV);cA.send(cy)}catch(cz){if(cx){bQ(cy,cB)}}}function bH(cy){var cx=new Date();var cz=cx.getTime()+cy;if(!k||cz>k){k=cz}}function bO(cx){if(bD||!aH){return}bD=setTimeout(function cy(){bD=null;if(bj()){return}var cz=new Date(),cA=aH-(cz.getTime()-cg);cA=Math.min(aH,cA);bO(cA)},cx||aH)}function bc(){if(!bD){return}clearTimeout(bD);bD=null}function aL(){if(bj()){return}bO()}function af(){bc()}function cv(){if(aq||!aH){return}aq=true;X(I,"focus",aL);X(I,"blur",af);bO()}function bZ(cB){var cy=new Date();var cx=cy.getTime();cg=cx;if(bY&&cxcW){cN.visitCount++;cN.lastVisitTs=cN.currentVisitTs}if(!a0||!cF.length){for(cT in bR){if(Object.prototype.hasOwnProperty.call(bR,cT)){cF=K(cX,bR[cT]);if(cF.length){break}}}for(cT in a9){if(Object.prototype.hasOwnProperty.call(a9,cT)){cx=K(cX,a9[cT]);if(cx.length){break}}}}c0=c(aU);cI=cS.length?c(cS):"";if(c0.length&&!ay(c0)&&(!a0||!cI.length||ay(cI))){cS=aU -}if(cS.length||cF.length){cE=cH;cR=[cF,cx,cE,bt(cS.slice(0,cB))];ct(cO,JSON2.stringify(cR),cm,aW,cf)}}cz+="&idsite="+bx+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cy.getHours()+"&m="+cy.getMinutes()+"&s="+cy.getSeconds()+"&url="+m(bt(cX))+(aU.length?"&urlref="+m(bt(aU)):"")+((a3&&a3.length)?"&uid="+m(a3):"")+"&_id="+cN.uuid+"&_idts="+cN.createTs+"&_idvc="+cN.visitCount+"&_idn="+cN.newVisitor+(cF.length?"&_rcn="+m(cF):"")+(cx.length?"&_rck="+m(cx):"")+"&_refts="+cE+"&_viewts="+cN.lastVisitTs+(String(cN.lastEcommerceOrderTs).length?"&_ects="+cN.lastEcommerceOrderTs:"")+(String(cS).length?"&_ref="+m(bt(cS.slice(0,cB))):"")+(cK?"&cs="+m(cK):"")+"&send_image=0";for(cT in co){if(Object.prototype.hasOwnProperty.call(co,cT)){cz+="&"+cT+"="+co[cT]}}var cZ=[];if(cU){for(cT in cU){if(Object.prototype.hasOwnProperty.call(cU,cT)&&/^dimension\d+$/.test(cT)){var cD=cT.replace("dimension","");cZ.push(parseInt(cD,10));cZ.push(String(cD));cz+="&"+cT+"="+cU[cT];delete cU[cT]}}}if(cU&&s(cU)){cU=null -}for(cT in aT){if(Object.prototype.hasOwnProperty.call(aT,cT)){var cJ=(-1===cZ.indexOf(cT));if(cJ){cz+="&dimension"+cT+"="+aT[cT]}}}if(cU){cz+="&data="+m(JSON2.stringify(cU))}else{if(Y){cz+="&data="+m(JSON2.stringify(Y))}}function cG(c1,c2){var c3=JSON2.stringify(c1);if(c3.length>2){return"&"+c2+"="+m(c3)}return""}var cY=cw(bn);var cP=cw(bM);cz+=cG(cY,"cvar");cz+=cG(cP,"e_cvar");if(at){cz+=cG(at,"_cvar");for(cT in cQ){if(Object.prototype.hasOwnProperty.call(cQ,cT)){if(at[cT][0]===""||at[cT][1]===""){delete at[cT]}}}if(bi){ct(cL,JSON2.stringify(at),bP,aW,cf)}}if(aE){if(bN){cz+=">_ms="+bN}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){cz+=">_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}cN.lastEcommerceOrderTs=y(cA)&&String(cA).length?cA:cN.lastEcommerceOrderTs;ao(cN);bE();cz+=Q(cV);if(ch.length){cz+="&"+ch}if(r(bC)){cz=bC(cz)}return cz}bj=function aI(){var cx=new Date();if(cg+aH<=cx.getTime()){var cy=bS("ping=1",null,"ping");a8(cy,bb);return true}return false -};function aX(cA,cz,cE,cB,cx,cH){var cC="idgoal=0",cD,cy=new Date(),cF=[],cG;if(String(cA).length){cC+="&ec_id="+m(cA);cD=Math.round(cy.getTime()/1000)}cC+="&revenue="+cz;if(String(cE).length){cC+="&ec_st="+cE}if(String(cB).length){cC+="&ec_tx="+cB}if(String(cx).length){cC+="&ec_sh="+cx}if(String(cH).length){cC+="&ec_dt="+cH}if(ci){for(cG in ci){if(Object.prototype.hasOwnProperty.call(ci,cG)){if(!y(ci[cG][1])){ci[cG][1]=""}if(!y(ci[cG][2])){ci[cG][2]=""}if(!y(ci[cG][3])||String(ci[cG][3]).length===0){ci[cG][3]=0}if(!y(ci[cG][4])||String(ci[cG][4]).length===0){ci[cG][4]=1}cF.push(ci[cG])}}cC+="&ec_items="+m(JSON2.stringify(cF))}cC=bS(cC,Y,"ecommerce",cD);a8(cC,bb)}function bq(cx,cB,cA,cz,cy,cC){if(String(cx).length&&y(cB)){aX(cx,cB,cA,cz,cy,cC)}}function aY(cx){if(y(cx)){aX("",cx,"","","","")}}function br(cz,cA){var cx=new Date(),cy=bS("action_name="+m(V(cz||aQ)),cA,"log");a8(cy,bb)}function aC(cz,cy){var cA,cx="(^| )(piwik[_-]"+cy;if(cz){for(cA=0;cA0){cB=parseInt(cB,10);cE(cB)}})}function b1(){var cy,cz,cA={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"},cx=I.devicePixelRatio||1;if(!((new RegExp("MSIE")).test(e.userAgent))){if(e.mimeTypes&&e.mimeTypes.length){for(cy in cA){if(Object.prototype.hasOwnProperty.call(cA,cy)){cz=e.mimeTypes[cA[cy]]; -co[cy]=(cz&&cz.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&y(e.javaEnabled)&&e.javaEnabled()){co.java="1"}if(r(I.GearsFactory)){co.gears="1"}co.cookie=bw()}co.res=M.width*cx+"x"+M.height*cx}b1();aP();ao();return{getVisitorId:function(){return aA().uuid},getVisitorInfo:function(){return b7()},getAttributionInfo:function(){return be()},getAttributionCampaignName:function(){return be()[0]},getAttributionCampaignKeyword:function(){return be()[1]},getAttributionReferrerTimestamp:function(){return be()[2]},getAttributionReferrerUrl:function(){return be()[3]},setTrackerUrl:function(cx){ai=cx},getTrackerUrl:function(){return ai},getSiteId:function(){return bx},setSiteId:function(cx){bu(cx)},setUserId:function(cx){if(!y(cx)||!cx.length){return}a3=cx;bg=by(a3).substr(0,16)},getUserId:function(){return a3},setCustomData:function(cx,cy){if(L(cx)){Y=cx}else{if(!Y){Y={}}Y[cx]=cy}},getCustomData:function(){return Y},setCustomRequestProcessing:function(cx){bC=cx},appendToTrackingUrl:function(cx){ch=cx -},getRequest:function(cx){return bS(cx)},addPlugin:function(cx,cy){a[cx]=cy},setCustomDimension:function(cx,cy){cx=parseInt(cx,10);if(cx>0){if(!y(cy)){cy=""}if(!o(cy)){cy=String(cy)}aT[cx]=cy}},getCustomDimension:function(cx){cx=parseInt(cx,10);if(cx>0&&Object.prototype.hasOwnProperty.call(aT,cx)){return aT[cx]}},deleteCustomDimension:function(cx){cx=parseInt(cx,10);if(cx>0){delete aT[cx]}},setCustomVariable:function(cy,cx,cB,cz){var cA;if(!y(cz)){cz="visit"}if(!y(cx)){return}if(!y(cB)){cB=""}if(cy>0){cx=!o(cx)?String(cx):cx;cB=!o(cB)?String(cB):cB;cA=[cx.slice(0,aZ),cB.slice(0,aZ)];if(cz==="visit"||cz===2){b0();at[cy]=cA}else{if(cz==="page"||cz===3){bn[cy]=cA}else{if(cz==="event"){bM[cy]=cA}}}}},getCustomVariable:function(cy,cz){var cx;if(!y(cz)){cz="visit"}if(cz==="page"||cz===3){cx=bn[cy]}else{if(cz==="event"){cx=bM[cy]}else{if(cz==="visit"||cz===2){b0();cx=at[cy]}}}if(!y(cx)||(cx&&cx[0]==="")){return false}return cx},deleteCustomVariable:function(cx,cy){if(this.getCustomVariable(cx,cy)){this.setCustomVariable(cx,"","",cy) -}},storeCustomVariablesInCookie:function(){bi=true},setLinkTrackingTimer:function(cx){bb=cx},setDownloadExtensions:function(cx){if(o(cx)){cx=cx.split("|")}cn=cx},addDownloadExtensions:function(cy){var cx;if(o(cy)){cy=cy.split("|")}for(cx=0;cx1){if(console!==undefined&&console&&console.error){console.error("The method "+Z+' is registered more than once in "paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}}ae[Z]++}}}}return ad}X(I,"beforeunload",U,false);p(); -Date.prototype.getTimeAlias=Date.prototype.getTime;N=new F();var t=["disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setUserId","setSiteId","enableLinkTracking"];_paq=b(_paq,t);for(v=0;v<_paq.length;v++){if(_paq[v]){T(_paq[v])}}_paq=new x();d={addPlugin:function(Y,Z){a[Y]=Z},getTracker:function(Y,Z){if(!y(Z)){Z=this.getAsyncTracker().getSiteId()}if(!y(Y)){Y=this.getAsyncTracker().getTrackerUrl()}return new F(Y,Z)},getAsyncTracker:function(){return N}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return d})}return d}())}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c) -}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}; +}var ad=aa[2],Y=aa[3],ab=aa[4];if(!ab){ab=""}else{if(ab.indexOf("&segment=")===0){ab=ab.substr("&segment=".length)}}I.name=ag+"###"+ad+"###"+Y+"###"+ab}var af=I.name.split("###");return af.length===4&&af[0]===ag}function O(Z,af,ab){var ae=I.name.split("###"),ad=ae[1],Y=ae[2],ac=ae[3],aa=D(Z,af);i(aa+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(aa,ab,ad,Y,ac)})}function F(bH,bB){var bx=P(w.domain,I.location.href,z()),cf=A(bx[0]),bh=j(bx[1]),aW=j(bx[2]),cd=false,bL="GET",cs=bL,am="application/x-www-form-urlencoded; charset=UTF-8",bX=am,ai=bH||"",bc="",cj="",bz=bB||"",a5="",bi="",aG,aS=w.title,cp=["7z","aac","apk","arc","arj","asf","asx","avi","azw3","bin","csv","deb","dmg","doc","docx","epub","exe","flv","gif","gz","gzip","hqx","ibooks","jar","jpg","jpeg","js","mobi","mp2","mp3","mp4","mpg","mpeg","mov","movie","msi","msp","odb","odf","odg","ods","odt","ogg","ogv","pdf","phps","png","ppt","pptx","qt","qtm","ra","ram","rar","rpm","sea","sit","tar","tbz","tbz2","bz","bz2","tgz","torrent","txt","wav","wma","wmv","wpd","xls","xlsx","xml","z","zip"],ae=[cf],a6=[],bf=[],aJ=[],bd=500,b6,aH,bl,bj,Y,bT=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],bb=["pk_kwd","piwik_kwd","utm_term"],aT="_pk_",ch,aY,aU=false,cb,aQ,a2,b7=33955200000,bR=1800000,co=15768000000,aE=true,bP=0,bk=false,at=false,bE,bp={},bO={},aV={},a1=200,ck={},cq={},bD=[],bI=false,b0=false,Z=false,cr=false,aq=false,ci=null,bF,au,a7,bA=W,aX; +function cv(cF,cC,cB,cE,cA,cD){if(aU){return}var cz;if(cB){cz=new Date();cz.setTime(cz.getTime()+cB)}w.cookie=cF+"="+m(cC)+(cB?";expires="+cz.toGMTString():"")+";path="+(cE||"/")+(cA?";domain="+cA:"")+(cD?";secure":"")}function ah(cB){if(aU){return 0}var cz=new RegExp("(^|;)[ ]*"+cB+"=([^;]*)"),cA=cz.exec(w.cookie);return cA?H(cA[2]):0}function bv(cz){var cA;if(bj){cA=new RegExp("#.*");return cz.replace(cA,"")}return cz}function bo(cB,cz){var cC=l(cz),cA;if(cC){return cz}if(cz.slice(0,1)==="/"){return l(cB)+"://"+c(cB)+cz}cB=bv(cB);cA=cB.indexOf("?");if(cA>=0){cB=cB.slice(0,cA)}cA=cB.lastIndexOf("/");if(cA!==cB.length-1){cB=cB.slice(0,cA+1)}return cB+cz}function b4(cB,cz){var cA;cB=String(cB).toLowerCase();cz=String(cz).toLowerCase();if(cB===cz){return true}if(cz.slice(0,1)==="."){if(cB===cz.slice(1)){return true}cA=cB.length-cz.length;if((cA>0)&&(cB.slice(cA)===cz)){return true}}return false}function cm(cA,cz){cA=String(cA);return cA.indexOf(cz,cA.length-cz.length)!==-1}function aP(cA,cz){cA=String(cA); +return cA.substr(0,cA.length-cz)}function bN(cz){var cA=document.createElement("a");if(cz.indexOf("//")!==0&&cz.indexOf("http")!==0){cz="http://"+cz}cA.href=n.toAbsoluteUrl(cz);if(cA.pathname){return cA.pathname}return""}function aF(cA,cz){var cB=(!cz||cz==="/");if(cB){return true}if(cA===cz){return true}if(!cA){return false}cz=String(cz).toLowerCase();cA=String(cA).toLowerCase();if(!cm(cA,"/")){cA+="/"}if(!cm(cz,"/")){cz+="/"}return cA.indexOf(cz)===0}function ab(cD,cF){var cA,cz,cB,cC,cE;for(cA=0;cA0)&&(cC.slice(cB)===cz)){return true}}}return false}function bS(cz,cB){var cA=new Image(1,1);cA.onload=function(){v=0;if(typeof cB==="function"){cB()}};cA.src=ai+(ai.indexOf("?")<0?"?":"&")+cz}function cn(cA,cD,cz){if(!y(cz)||null===cz){cz=true +}try{var cC=I.XMLHttpRequest?new I.XMLHttpRequest():I.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;cC.open("POST",ai,true);cC.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cz){bS(cA,cD)}else{if(typeof cD==="function"){cD()}}};cC.setRequestHeader("Content-Type",bX);cC.send(cA)}catch(cB){if(cz){bS(cA,cD)}}}function bJ(cA){var cz=new Date();var cB=cz.getTime()+cA;if(!k||cB>k){k=cB}}function bQ(cz){if(bF||!aH){return}bF=setTimeout(function cA(){bF=null;if(bl()){return}var cB=new Date(),cC=aH-(cB.getTime()-ci);cC=Math.min(aH,cC);bQ(cC)},cz||aH)}function be(){if(!bF){return}clearTimeout(bF);bF=null}function aM(){if(bl()){return}bQ()}function af(){be()}function cx(){if(aq||!aH){return}aq=true;X(I,"focus",aM);X(I,"blur",af);bQ()}function b1(cD){var cA=new Date();var cz=cA.getTime();ci=cz;if(b0&&czcY){cP.visitCount++;cP.lastVisitTs=cP.currentVisitTs}if(!a2||!cH.length){for(cV in bT){if(Object.prototype.hasOwnProperty.call(bT,cV)){cH=K(cZ,bT[cV]);if(cH.length){break}}}for(cV in bb){if(Object.prototype.hasOwnProperty.call(bb,cV)){cz=K(cZ,bb[cV]);if(cz.length){break}}}}c2=c(aW);cK=cU.length?c(cU):""; +if(c2.length&&!ay(c2)&&(!a2||!cK.length||ay(cK))){cU=aW}if(cU.length||cH.length){cG=cJ;cT=[cH,cz,cG,bv(cU.slice(0,cD))];cv(cQ,JSON2.stringify(cT),co,aY,ch)}}cB+="&idsite="+bz+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cA.getHours()+"&m="+cA.getMinutes()+"&s="+cA.getSeconds()+"&url="+m(bv(cZ))+(aW.length?"&urlref="+m(bv(aW)):"")+((a5&&a5.length)?"&uid="+m(a5):"")+"&_id="+cP.uuid+"&_idts="+cP.createTs+"&_idvc="+cP.visitCount+"&_idn="+cP.newVisitor+(cH.length?"&_rcn="+m(cH):"")+(cz.length?"&_rck="+m(cz):"")+"&_refts="+cG+"&_viewts="+cP.lastVisitTs+(String(cP.lastEcommerceOrderTs).length?"&_ects="+cP.lastEcommerceOrderTs:"")+(String(cU).length?"&_ref="+m(bv(cU.slice(0,cD))):"")+(cM?"&cs="+m(cM):"")+"&send_image=0";for(cV in cq){if(Object.prototype.hasOwnProperty.call(cq,cV)){cB+="&"+cV+"="+cq[cV]}}var c1=[];if(cW){for(cV in cW){if(Object.prototype.hasOwnProperty.call(cW,cV)&&/^dimension\d+$/.test(cV)){var cF=cV.replace("dimension","");c1.push(parseInt(cF,10));c1.push(String(cF));cB+="&"+cV+"="+cW[cV]; +delete cW[cV]}}}if(cW&&s(cW)){cW=null}for(cV in aV){if(Object.prototype.hasOwnProperty.call(aV,cV)){var cL=(-1===c1.indexOf(cV));if(cL){cB+="&dimension"+cV+"="+aV[cV]}}}if(cW){cB+="&data="+m(JSON2.stringify(cW))}else{if(Y){cB+="&data="+m(JSON2.stringify(Y))}}function cI(c3,c4){var c5=JSON2.stringify(c3);if(c5.length>2){return"&"+c4+"="+m(c5)}return""}var c0=cy(bp);var cR=cy(bO);cB+=cI(c0,"cvar");cB+=cI(cR,"e_cvar");if(at){cB+=cI(at,"_cvar");for(cV in cS){if(Object.prototype.hasOwnProperty.call(cS,cV)){if(at[cV][0]===""||at[cV][1]===""){delete at[cV]}}}if(bk){cv(cN,JSON2.stringify(at),bR,aY,ch)}}if(aE){if(bP){cB+=">_ms="+bP}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){cB+=">_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}cP.lastEcommerceOrderTs=y(cC)&&String(cC).length?cC:cP.lastEcommerceOrderTs;ao(cP);bG();cB+=Q(cX);if(cj.length){cB+="&"+cj}if(r(bE)){cB=bE(cB)}return cB}bl=function aI(){var cz=new Date();if(ci+aH<=cz.getTime()){var cA=bU("ping=1",null,"ping"); +ba(cA,bd);return true}return false};function aZ(cC,cB,cG,cD,cz,cJ){var cE="idgoal=0",cF,cA=new Date(),cH=[],cI;if(String(cC).length){cE+="&ec_id="+m(cC);cF=Math.round(cA.getTime()/1000)}cE+="&revenue="+cB;if(String(cG).length){cE+="&ec_st="+cG}if(String(cD).length){cE+="&ec_tx="+cD}if(String(cz).length){cE+="&ec_sh="+cz}if(String(cJ).length){cE+="&ec_dt="+cJ}if(ck){for(cI in ck){if(Object.prototype.hasOwnProperty.call(ck,cI)){if(!y(ck[cI][1])){ck[cI][1]=""}if(!y(ck[cI][2])){ck[cI][2]=""}if(!y(ck[cI][3])||String(ck[cI][3]).length===0){ck[cI][3]=0}if(!y(ck[cI][4])||String(ck[cI][4]).length===0){ck[cI][4]=1}cH.push(ck[cI])}}cE+="&ec_items="+m(JSON2.stringify(cH))}cE=bU(cE,Y,"ecommerce",cF);ba(cE,bd)}function bs(cz,cD,cC,cB,cA,cE){if(String(cz).length&&y(cD)){aZ(cz,cD,cC,cB,cA,cE)}}function a0(cz){if(y(cz)){aZ("",cz,"","","","")}}function bt(cB,cC){var cz=new Date(),cA=bU("action_name="+m(V(cB||aS)),cC,"log");ba(cA,bd)}function aC(cB,cA){var cC,cz="(^| )(piwik[_-]"+cA;if(cB){for(cC=0;cC0){cD=parseInt(cD,10);cG(cD)}})}function aK(cD,cF){var cE=bN(cD);var cC=bN(cF);if(!cE||cE==="/"||!cC||cC==="/"){return}var cB=A(cD);if(ab(cB,"/")){return}if(cm(cE,"/")){cE=aP(cE,1)}var cG=cE.split("/");for(var cA=2;cA0){if(!y(cA)){cA=""}if(!o(cA)){cA=String(cA)}aV[cz]=cA}},getCustomDimension:function(cz){cz=parseInt(cz,10);if(cz>0&&Object.prototype.hasOwnProperty.call(aV,cz)){return aV[cz]}},deleteCustomDimension:function(cz){cz=parseInt(cz,10);if(cz>0){delete aV[cz]}},setCustomVariable:function(cA,cz,cD,cB){var cC;if(!y(cB)){cB="visit"}if(!y(cz)){return}if(!y(cD)){cD=""}if(cA>0){cz=!o(cz)?String(cz):cz;cD=!o(cD)?String(cD):cD;cC=[cz.slice(0,a1),cD.slice(0,a1)];if(cB==="visit"||cB===2){b2();at[cA]=cC}else{if(cB==="page"||cB===3){bp[cA]=cC}else{if(cB==="event"){bO[cA]=cC}}}}},getCustomVariable:function(cA,cB){var cz;if(!y(cB)){cB="visit"}if(cB==="page"||cB===3){cz=bp[cA]}else{if(cB==="event"){cz=bO[cA]}else{if(cB==="visit"||cB===2){b2(); +cz=at[cA]}}}if(!y(cz)||(cz&&cz[0]==="")){return false}return cz},deleteCustomVariable:function(cz,cA){if(this.getCustomVariable(cz,cA)){this.setCustomVariable(cz,"","",cA)}},storeCustomVariablesInCookie:function(){bk=true},setLinkTrackingTimer:function(cz){bd=cz},setDownloadExtensions:function(cz){if(o(cz)){cz=cz.split("|")}cp=cz},addDownloadExtensions:function(cA){var cz;if(o(cA)){cA=cA.split("|")}for(cz=0;cz1){if(console!==undefined&&console&&console.error){console.error("The method "+Z+' is registered more than once in "paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers') +}}ae[Z]++}}}}return ad}X(I,"beforeunload",U,false);p();Date.prototype.getTimeAlias=Date.prototype.getTime;N=new F();var t=["disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","enableLinkTracking"];_paq=b(_paq,t);for(v=0;v<_paq.length;v++){if(_paq[v]){T(_paq[v])}}_paq=new x();d={addPlugin:function(Y,Z){a[Y]=Z},getTracker:function(Y,Z){if(!y(Z)){Z=this.getAsyncTracker().getSiteId()}if(!y(Y)){Y=this.getAsyncTracker().getTrackerUrl()}return new F(Y,Z)},getAsyncTracker:function(){return N}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return d})}return d}())}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g); +c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}; /*! @license-end */ }; \ No newline at end of file diff --git a/tests/javascript/enable_sqlite b/tests/javascript/enable_sqlite new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/javascript/index.php b/tests/javascript/index.php index dfcf7ec831..0cb1d002b1 100644 --- a/tests/javascript/index.php +++ b/tests/javascript/index.php @@ -2242,8 +2242,8 @@ function PiwikTest() { } }); - test("Tracker setDomains(), isSiteHostName(), isSiteHostPath() and getLinkIfShouldBeProcessed()", function() { - expect(102); + test("Tracker setDomains(), isSiteHostName(), isSiteHostPath(), findConfigCookiePathToUse() and getLinkIfShouldBeProcessed()", function() { + expect(117); var tracker = Piwik.getTracker(); var initialDomains = tracker.getDomains(); @@ -2252,10 +2252,12 @@ function PiwikTest() { equal( typeof tracker.hook.test._isSiteHostName, 'function', "isSiteHostName" ); equal( typeof tracker.hook.test._isSiteHostPath, 'function', "isSiteHostPath" ); equal( typeof tracker.hook.test._getLinkIfShouldBeProcessed, 'function', "getLinkIfShouldBeProcessed" ); + equal( typeof tracker.hook.test._findConfigCookiePathToUse, 'function', "findConfigCookiePathToUse" ); var isSiteHostName = tracker.hook.test._isSiteHostName; var isSiteHostPath = tracker.hook.test._isSiteHostPath; var getLinkIfShouldBeProcessed = tracker.hook.test._getLinkIfShouldBeProcessed; + var findConfigCookiePathToUse = tracker.hook.test._findConfigCookiePathToUse; // tracker.setDomain() @@ -2412,6 +2414,46 @@ function PiwikTest() { "href": "http://www.piwik.org/footer", "type": "link" }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/footer')), 'getLinkIfShouldBeProcessed http://www.piwik.org/footer and there is domain piwik.org/foo but it should be outlink as path is different') + + + /** + * findConfigCookiePathToUse () + */ + + tracker.setDomains( ['.piwik.org', '.piwik.pro/foo/bar', '.piwik.pro/foo', '.piwik.com/test/foo', 'example.com/foo'] ); + + equal(null, findConfigCookiePathToUse('.piwik.org/test', 'http://piwik.org/test/two'), 'findConfigCookiePathToUse no cookiePath because there is a domain alias given allowing them all'); + equal('/foo', findConfigCookiePathToUse('.piwik.pro/foo', 'http://piwik.pro/foo/bar/test'), 'findConfigCookiePathToUse should find a match'); + equal('/foo', findConfigCookiePathToUse('.piwik.pro/foo/bar/test', 'http://piwik.pro/foo/bar/test'), 'findConfigCookiePathToUse should find a less restrictive path automatically'); + equal('/foo', findConfigCookiePathToUse('.piwik.pro/foo/bar/test', 'http://piwik.pro/foo'), 'findConfigCookiePathToUse should find a less restrictive path automatically, urlPath===domainPath'); + equal('/test/bar/test', findConfigCookiePathToUse('.piwik.com/test/bar/test', 'http://piwik.com/test/bar/test/'), 'findConfigCookiePathToUse should use exactly given path if no less restrictive version is available'); + equal('/test/foo', findConfigCookiePathToUse('.piwik.com/test/foo/test', 'http://piwik.com/test/foo/test'), 'findConfigCookiePathToUse should find a less restrictive path automatically, configAlias === currentUrl'); + equal('/test/foo', findConfigCookiePathToUse('.piwik.com/test/foo', 'http://piwik.com/test/foo/test'), 'findConfigCookiePathToUse should find a less restrictive path automatically'); + equal(null, findConfigCookiePathToUse('.piwik.pro/foo', 'http://piwik.pro/test'), 'findConfigCookiePathToUse should not return a path when user is actually not on that path'); + equal(null, findConfigCookiePathToUse('.piwik.pro/foo', 'http://piwik.pro'), 'findConfigCookiePathToUse when there is no path set we cannot use a configPath'); + + /** + * Test sets a good cookie path automatically + */ + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/tests'] ); + equal('/tests', tracker.getConfigCookiePath()), 'should set a cookie path automatically'; + + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/tests/javascript'] ); + equal('/tests/javascript', tracker.getConfigCookiePath()), 'should set a cookie path automatically, multiple directories'; + + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/tests/javascript', '.' + domainAlias + '/tests'] ); + equal('/tests', tracker.getConfigCookiePath()), 'should find shortest path for possible cookie path'; + + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/tests/javascript', '.example.com/tests'] ); + equal('/tests/javascript', tracker.getConfigCookiePath()), 'should not find a shorter path when no other domain matches'; + + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/another/one', '.example.org/tests/javascript', '.example.com/tests'] ); + equal(null, tracker.getConfigCookiePath()), 'should not set a path when no domain and no path matches'; }); test("Tracker getClassesRegExp()", function() { diff --git a/tests/javascript/piwiktest.js b/tests/javascript/piwiktest.js index 36cccc66a1..67157c6d96 100644 --- a/tests/javascript/piwiktest.js +++ b/tests/javascript/piwiktest.js @@ -27,6 +27,7 @@ Piwik.addPlugin('testPlugin', { '_getCookieName : getCookieName,' + '_setCookie : setCookie,' + '_getLinkIfShouldBeProcessed : getLinkIfShouldBeProcessed,' + + '_findConfigCookiePathToUse : findConfigCookiePathToUse,' + '_encode : encodeWrapper,' + '_decode : decodeWrapper,' + '_urldecode : urldecode,' + -- cgit v1.2.3 From 4d45f92a5e99ba207c5f241bf129693844150088 Mon Sep 17 00:00:00 2001 From: Thomas Steur Date: Fri, 4 Dec 2015 03:15:03 +0000 Subject: fix jslint --- js/piwik.js | 9 ++++----- piwik.js | 4 ++-- tests/javascript/index.php | 2 ++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/js/piwik.js b/js/piwik.js index a2fc4c0595..fa05e5e58d 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -1021,7 +1021,7 @@ if (typeof JSON2 !== 'object' && typeof window.JSON === 'object' && window.JSON. trackVisibleContentImpressions, isTrackOnlyVisibleContentEnabled, port, isUrlToCurrentDomain, isNodeAuthorizedToTriggerInteraction, replaceHrefIfInternalLink, getConfigDownloadExtensions, disableLinkTracking, substr, setAnyAttribute, wasContentTargetAttrReplaced, max, abs, childNodes, compareDocumentPosition, body, - getConfigVisitorCookieTimeout, getRemainingVisitorCookieTimeout, getDomains, + getConfigVisitorCookieTimeout, getRemainingVisitorCookieTimeout, getDomains, getConfigCookiePath, newVisitor, uuid, createTs, visitCount, currentVisitTs, lastVisitTs, lastEcommerceOrderTs, "", "\b", "\t", "\n", "\f", "\r", "\"", "\\", apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length, parse, prototype, push, replace, @@ -4927,7 +4927,8 @@ if (typeof Piwik !== 'object') { // also a config alias allowing "apache.piwik/foo". In this case we're not allowed to set // the cookie for "/foo/bar" but "/foo" var pathAliasParts = aliasPath.split('/'); - for (var i = 2; i < pathAliasParts.length; i++) { + var i; + for (i = 2; i < pathAliasParts.length; i++) { var lessRestrctivePath = pathAliasParts.slice(0, i).join('/'); if (isSiteHostPath(aliasDomain, lessRestrctivePath)) { aliasPath = lessRestrctivePath; @@ -5528,10 +5529,8 @@ if (typeof Piwik !== 'object') { var hasDomainAliasAlready = false, i; for (i in configHostsAlias) { - var configHostAliasDomain = domainFixup(String(configHostsAlias[i])); - if (Object.prototype.hasOwnProperty.call(configHostsAlias, i) - && isSameHost(domainAlias, configHostAliasDomain)) { + && isSameHost(domainAlias, domainFixup(String(configHostsAlias[i])))) { hasDomainAliasAlready = true; if (!configCookiePath) { diff --git a/piwik.js b/piwik.js index c76fb8e4ef..e16b81c991 100644 --- a/piwik.js +++ b/piwik.js @@ -52,10 +52,10 @@ if(!ax(cB)&&cz.wasContentTargetAttrReplaced){S.setAnyAttribute(cz,n.CONTENT_TARG for(cz=0;cz0){cD=parseInt(cD,10);cG(cD)}})}function aK(cD,cF){var cE=bN(cD);var cC=bN(cF);if(!cE||cE==="/"||!cC||cC==="/"){return}var cB=A(cD);if(ab(cB,"/")){return}if(cm(cE,"/")){cE=aP(cE,1)}var cG=cE.split("/");for(var cA=2;cA0){cD=parseInt(cD,10);cG(cD)}})}function aK(cD,cF){var cE=bN(cD);var cC=bN(cF);if(!cE||cE==="/"||!cC||cC==="/"){return}var cB=A(cD);if(ab(cB,"/")){return}if(cm(cE,"/")){cE=aP(cE,1)}var cG=cE.split("/");var cA;for(cA=2;cA0){if(!y(cA)){cA=""}if(!o(cA)){cA=String(cA)}aV[cz]=cA}},getCustomDimension:function(cz){cz=parseInt(cz,10);if(cz>0&&Object.prototype.hasOwnProperty.call(aV,cz)){return aV[cz]}},deleteCustomDimension:function(cz){cz=parseInt(cz,10);if(cz>0){delete aV[cz]}},setCustomVariable:function(cA,cz,cD,cB){var cC;if(!y(cB)){cB="visit"}if(!y(cz)){return}if(!y(cD)){cD=""}if(cA>0){cz=!o(cz)?String(cz):cz;cD=!o(cD)?String(cD):cD;cC=[cz.slice(0,a1),cD.slice(0,a1)];if(cB==="visit"||cB===2){b2();at[cA]=cC}else{if(cB==="page"||cB===3){bp[cA]=cC}else{if(cB==="event"){bO[cA]=cC}}}}},getCustomVariable:function(cA,cB){var cz;if(!y(cB)){cB="visit"}if(cB==="page"||cB===3){cz=bp[cA]}else{if(cB==="event"){cz=bO[cA]}else{if(cB==="visit"||cB===2){b2(); -cz=at[cA]}}}if(!y(cz)||(cz&&cz[0]==="")){return false}return cz},deleteCustomVariable:function(cz,cA){if(this.getCustomVariable(cz,cA)){this.setCustomVariable(cz,"","",cA)}},storeCustomVariablesInCookie:function(){bk=true},setLinkTrackingTimer:function(cz){bd=cz},setDownloadExtensions:function(cz){if(o(cz)){cz=cz.split("|")}cp=cz},addDownloadExtensions:function(cA){var cz;if(o(cA)){cA=cA.split("|")}for(cz=0;cz Date: Fri, 4 Dec 2015 03:18:56 +0000 Subject: added documentation for cookieConfigPath detection --- js/piwik.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/js/piwik.js b/js/piwik.js index fa05e5e58d..94816854a8 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -5520,7 +5520,12 @@ if (typeof Piwik !== 'object') { /** * Set array of domains to be treated as local. Also supports path, eg '.piwik.org/subsite1'. In this * case all links that don't go to '*.piwik.org/subsite1/ *' would be treated as outlinks. - * For example a link to 'piwik.org/' or 'piwik.org/subsite2' both would be treated as outlinks + * For example a link to 'piwik.org/' or 'piwik.org/subsite2' both would be treated as outlinks. + * + * We might automatically set a cookieConfigPath to avoid creating several cookies under one domain + * if there is a hostAlias defined with a path. Say a user is visiting 'http://piwik.org/subsite1' + * and '.piwik.org/subsite1' is set as a hostsAlias. Piwik will automatically use '/subsite1' as + * cookieConfigPath. * * @param string|array hostsAlias */ -- cgit v1.2.3 From ea0ee79d05e7d27df4c360e5a28bcfa2834ab183 Mon Sep 17 00:00:00 2001 From: Thomas Steur Date: Fri, 4 Dec 2015 03:42:43 +0000 Subject: there was a weird character after || --- js/piwik.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/piwik.js b/js/piwik.js index 94816854a8..344e5436e9 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -4905,7 +4905,7 @@ if (typeof Piwik !== 'object') { var aliasPath = getPathName(configHostAlias); var currentPath = getPathName(currentUrl); - if (!aliasPath || aliasPath === '/' || !currentPath || currentPath === '/') { + if (!aliasPath || aliasPath === '/' || !currentPath || currentPath === '/') { // no path set that would be useful for cookiePath return; } -- cgit v1.2.3