Skip to content

Commit

Permalink
Add required logic for view/engagement analytics without an iframe
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason Rogers committed Oct 27, 2017
1 parent 4dd3658 commit e524f96
Show file tree
Hide file tree
Showing 17 changed files with 752 additions and 175 deletions.
10 changes: 7 additions & 3 deletions extensions/amp-addthis/0.1/addthis-utils/classify.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,16 @@ export const classifyPage = (pageInfo, metaElements) => {
return bitmask;
};

export const classifyReferrer = (parsedReferrer, parsedHref) => {
export const classifyReferrer = (
referrerString,
parsedReferrer,
parsedHref
) => {
// The default is a direct view.
let bitmask = REFERRER_BITS.DIRECT;

// If there was a referrer, try to categorize it
if (parsedReferrer) {
if (referrerString && parsedReferrer) {
// Compare domain only (SLD + TLD)
if (parsedReferrer.host === parsedHref.host) {
bitmask |= REFERRER_BITS.ON_DOMAIN;
Expand All @@ -212,7 +216,7 @@ export const classifyReferrer = (parsedReferrer, parsedHref) => {
}

// Run some naive checks to see if visitor came from a search.
if (isSearchUrl(referrer)) {
if (isSearchUrl(referrerString)) {
bitmask |= REFERRER_BITS.SEARCH;
}
}
Expand Down
61 changes: 61 additions & 0 deletions extensions/amp-addthis/0.1/addthis-utils/eng.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Copyright 2017 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Services} from '../../../../src/services';
import {addParamsToUrl} from '../../../../src/url';

import {API_SERVER} from '../constants';
import {getSessionId} from './session';
import {pixelDrop} from './pixel';

const getEngData = ({monitors, loc, ampDoc, pubId}) => {
const {
dwellMonitor,
scrollMonitor,
clickMonitor,
activeToolsMonitor,
} = monitors;
const {host, pathname, hash} = loc;
const viewport = Services.viewportForDoc(ampDoc);

return {
al: activeToolsMonitor.getActivePcos().join(',') || undefined,
amp: 1,
dc: 1,
dp: host,
dt: dwellMonitor.getDwellTime(),
fp: pathname.replace(hash, ''),
ict: clickMonitor.getIframeClickString(),
ivh: scrollMonitor.getInitialViewHeight(),
pct: clickMonitor.getPageClicks(),
pfm: ampDoc.win.navigator.sendBeacon ? 0 : 1,
ph: viewport.getHeight(),
pub: pubId,
sh: scrollMonitor.getScrollHeight(),
sid: getSessionId(),
};
};

export const callEng = props => {
const data = getEngData(props);
const url = addParamsToUrl(`${API_SERVER}/live/red_lojson/100eng.json`, data);
const {ampDoc} = props;

if (ampDoc.win.navigator.sendBeacon) {
ampDoc.win.navigator.sendBeacon(url, '{}');
} else {
pixelDrop(url, ampDoc);
}
};
68 changes: 20 additions & 48 deletions extensions/amp-addthis/0.1/addthis-utils/lojson.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Services} from '../../../src/services';
import {parseUrl, addParamsToUrl} from '../../../src/url';
import {toArray} from '../../../src/types';
import {parseUrl} from '../../../../src/url';
import {toArray} from '../../../../src/types';

import {API_SERVER} from '../constants';
import {
Expand All @@ -30,8 +29,8 @@ import {
getFragmentId,
} from './fragment';
import {getMetaElements} from './meta';
import {createCUID} from './cuid';
import {dropPixelGroups} from './pixel';
import {getSessionId} from './session';
import {callPixelEndpoint} from './pixel';

const VIEW_EVENT_CHANNEL = 100;
const nonTrackedDomainMatcher = /\.gov|\.mil/;
Expand All @@ -57,84 +56,57 @@ const getLojsonData = ({
protocol,
port,
};
const parsedReferrer = referrer ? parseUrl(referrer) : null;
const [langWithoutLocale, locale] = atConfig.ui_language.split('-');
const parsedReferrer = referrer ? parseUrl(referrer) : {};
const langParts = atConfig.ui_language.split('-');
const langWithoutLocale = langParts[0];
const locale = langParts.slice(1);
const service = getServiceFromUrlFragment(pageInfo.du);
const {win} = ampDoc;
const metaElements = toArray(getMetaElements(win.doc));
const metaElements = toArray(getMetaElements(win.document));
const isDNTEnabled = win.navigator.doNotTrack &&
win.navigator.doNotTrack !== 'unspecified' &&
win.navigator.doNotTrack !== 'no' &&
win.navigator.doNotTrack !== '0';

// const trackingInfo = {
// ...pageInfo,
// cb: classifyPage(metaElements),
// dp: host,
// dr: parsedReferrer.host,
// fcu: service ? '' : getFragmentId(pageInfo.du),
// fp: parseUrl(clearOurFragment(pageInfo.du)).pathname,
// fr: parsedReferrer ? parsedReferrer.pathname : undefined,
// ln: langWithoutLocale,
// lnlc: locale,
// mk: getKeywordsString(metaElements),
// pd: isProductPage(win.doc, metaElements),
// pub: pubId,
// rb: classifyReferrer(parsedReferrer, parseUrl(pageInfo.du)),
// sid: createCUID(),
// sr: service,
// };

return {
amp: 1,
bl: 0 |
(atConfig.use_cookies !== false ? 1 : 0) |
(atConfig.track_textcopy === true ? 2 : 0) |
(atConfig.track_addressbar === true ? 4 : 0),
cb: classifyPage(metaElements),
cb: classifyPage(pageInfo, metaElements),
colc: Date.now(),
ct: atConfig.track_clickback !== false &&
atConfig.track_linkback !== false ? 1 : 0,
dc: 1,
dp: host,
dr: host === parsedReferrer.host ? undefined : parsedReferrer.host,
fcu: service ? '' : getFragmentId(pageInfo.du),
fp: parseUrl(clearOurFragment(pageInfo.du)).pathname,
fr: parsedReferrer ? parsedReferrer.pathname : '',
fr: parsedReferrer.pathname || '',
gen: VIEW_EVENT_CHANNEL,
ln: langWithoutLocale,
lnlc: locale,
mk: getKeywordsString(metaElements),
of: isDNTEnabled ? 4 :
nonTrackedDomainMatcher.test(hostname) ? 1 :
0,
pd: isProductPage(win.doc, metaElements) ? 1 : 0,
pd: isProductPage(win.document, metaElements) ? 1 : 0,
pub: pubId,
rb: classifyReferrer(parsedReferrer, parseUrl(pageInfo.du)),
sid: createCUID(),
rb: classifyReferrer(referrer, parsedReferrer, parseUrl(pageInfo.du)),
sid: getSessionId(),
skipb: 1,
sr: service,
};
};

export const callLojson = props => {
const data = getLojsonData(props);
const url = addParamsToUrl(`${API_SERVER}/live/red_lojson/300lo.json`, data);
const {ampDoc} = props;
const endpoint = `${API_SERVER}/live/red_lojson/300lo.json`;

Services.xhrFor(ampDoc.win).fetchJson(url, {
mode: 'cors',
method: 'GET',
// This should be cacheable across publisher domains, so don't append
// __amp_source_origin to the URL.
ampCors: false,
credentials: 'include',
}).then(res => res.json()).then(json => {
const {pixels = []} = json;
if (pixels.length > 0) {
dropPixelGroups(pixels, {
sid: data.sid,
ampDoc,
});
}
callPixelEndpoint({
ampDoc: props.ampDoc,
endpoint,
data,
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright 2017 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {RE_ALPHA} from '../../constants';

const RE_NUMDASH = /[0-9\-].*/;

export class ActiveToolsMonitor {
constructor() {
this.activePcos_ = {};
}

record({widget}) {
// Get the "clean" PCO (no numbers or dashes) from the widget.
const pco = (widget.id || widget.pco || '').replace(RE_NUMDASH, '');

// PCOs must be alpha strings, and we don't want duplicates.
if (!pco || this.activePcos_[pco] || !RE_ALPHA.test(pco)) {
return;
}

this.activePcos_[pco] = pco;
}

getActivePcos() {
return Object.keys(this.activePcos_);
}
}
75 changes: 75 additions & 0 deletions extensions/amp-addthis/0.1/addthis-utils/monitors/click-monitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright 2017 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {listen} from '../../../../../src/event-helper';

export class ClickMonitor {
constructor() {
this.iframeClickMap_ = {};
this.pageClicks_ = 0;
this.lastSelection_ = null;
this.win_ = null;
}

startForDoc(ampDoc) {
this.win_ = ampDoc.win;
this.lastSelection_ = this.win_.document.activeElement;

listen(this.win_, 'blur', this.checkSelection_.bind(this));
listen(this.win_, 'click', this.onPageClick_.bind(this));
}

checkSelection_() {
const {activeElement} = this.win_.document;

if (!activeElement) {
return;
}

const changeOccurred = activeElement !== this.lastSelection_;

if (activeElement.tagName === 'IFRAME' && changeOccurred) {
this.incrementFrameClick_(activeElement);
}

this.lastSelection_ = activeElement;
}

onPageClick_() {
this.pageClicks_++;
this.lastSelection_ = this.win_.document.activeElement;
}

incrementFrameClick_(activeElement) {
const trimSrc = activeElement.src.split('://').pop();

if (!this.iframeClickMap_[trimSrc]) {
this.iframeClickMap_[trimSrc] = 1;
} else {
this.iframeClickMap_[trimSrc]++;
}
}

getPageClicks() {
return this.pageClicks_;
}

getIframeClickString() {
return Object.keys(this.iframeClickMap_).map(key => {
return `${key}|${this.iframeClickMap_[key]}`;
}).join(',');
}
}
40 changes: 40 additions & 0 deletions extensions/amp-addthis/0.1/addthis-utils/monitors/dwell-monitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright 2017 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {Services} from '../../../../../src/services';

export class DwellMonitor {
constructor() {
this.dwellTime_ = 0;
this.viewer_ = null;
}

startForDoc(ampDoc) {
this.viewer_ = Services.viewerForDoc(ampDoc);
this.viewer_.onVisibilityChanged(this.listener.bind(this));
}

listener() {
if (!this.viewer_.isVisible()) {
const lastVisibleTime = this.viewer_.getLastVisibleTime() || 0;
this.dwellTime_ += (Date.now() - lastVisibleTime);
}
}

getDwellTime() {
return this.dwellTime_;
}
}
19 changes: 19 additions & 0 deletions extensions/amp-addthis/0.1/addthis-utils/monitors/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright 2017 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {ScrollMonitor} from './scroll-monitor';
export {ClickMonitor} from './click-monitor';
export {DwellMonitor} from './dwell-monitor';
export {ActiveToolsMonitor} from './active-tools-monitor';

0 comments on commit e524f96

Please sign in to comment.