Clean up and refactor side wide shared JS code (#4387)
* cleanup and refactor side wide shared JS code * clean up Google Analytics related code * clean up 'dnt.js' code by merging it into 'common/google-analytics.js' * forgot to uncomment a line of code * move 'react-ga-proxy.js' to 'common' dir * split code/modules further * updates * Update source/js/common/inject-react/join-us.js Co-Authored-By: Pomax <pomax@nihongoresources.com> * Update source/js/buyers-guide/analytics-events.js Co-Authored-By: Pomax <pomax@nihongoresources.com> Co-authored-by: Pomax <pomax@nihongoresources.com>
This commit is contained in:
Родитель
7cbfa4fa85
Коммит
07d2aa0a56
|
@ -1,65 +0,0 @@
|
|||
export default {
|
||||
initialize: function(gaIdentifier = undefined) {
|
||||
if (!gaIdentifier) {
|
||||
console.warn(`No GA identifier found: skipping bootstrap step`);
|
||||
}
|
||||
|
||||
var _dntStatus = navigator.doNotTrack || navigator.msDoNotTrack;
|
||||
var fxMatch = navigator.userAgent.match(/Firefox\/(\d+)/);
|
||||
var ie10Match = navigator.userAgent.match(/MSIE 10/i);
|
||||
var w8Match = navigator.appVersion.match(/Windows NT 6.2/);
|
||||
|
||||
if (fxMatch && Number(fxMatch[1]) < 32) {
|
||||
_dntStatus = `Unspecified`;
|
||||
} else if (ie10Match && w8Match) {
|
||||
_dntStatus = `Unspecified`;
|
||||
} else {
|
||||
_dntStatus =
|
||||
{
|
||||
"0": `Disabled`,
|
||||
"1": `Enabled`
|
||||
}[_dntStatus] || `Unspecified`;
|
||||
}
|
||||
|
||||
if (_dntStatus !== `Enabled`) {
|
||||
(function(i, s, o, g, r, a, m) {
|
||||
i[`GoogleAnalyticsObject`] = r;
|
||||
(i[r] =
|
||||
i[r] ||
|
||||
function() {
|
||||
(i[r].q = i[r].q || []).push(arguments);
|
||||
}),
|
||||
(i[r].l = 1 * new Date());
|
||||
(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
|
||||
a.async = 1;
|
||||
a.src = g;
|
||||
m.parentNode.insertBefore(a, m);
|
||||
})(
|
||||
window,
|
||||
document,
|
||||
`script`,
|
||||
`https://www.google-analytics.com/analytics.js`,
|
||||
`ga`
|
||||
);
|
||||
|
||||
ga(`create`, gaIdentifier, `auto`);
|
||||
ga(`send`, `pageview`);
|
||||
}
|
||||
},
|
||||
|
||||
sendGAEvent(category = ``, action = ``, label = ``) {
|
||||
if (!window.ga) {
|
||||
return;
|
||||
}
|
||||
window.ga(`send`, category, `navigation`, action, label);
|
||||
window.ga(
|
||||
`send`,
|
||||
`event`,
|
||||
`navigation`,
|
||||
`page footer cta`,
|
||||
document.querySelectorAll(`.cms h1`).length > 0
|
||||
? `${document.querySelectorAll(`.cms h1`)[0].innerText} - footer cta`
|
||||
: ``
|
||||
);
|
||||
}
|
||||
};
|
|
@ -1,5 +1,4 @@
|
|||
import ReactGA from "../react-ga-proxy";
|
||||
import DNT from "../dnt.js";
|
||||
import { ReactGA, GoogleAnalytics } from "../common";
|
||||
|
||||
function getQuerySelectorEvents(pageTitle, productName) {
|
||||
return {
|
||||
|
@ -127,7 +126,7 @@ function setupElementGA(element, eventData) {
|
|||
|
||||
const ProductGA = {
|
||||
init: () => {
|
||||
if (!DNT.allowTracking) {
|
||||
if (GoogleAnalytics.doNotTrack) {
|
||||
// explicit check on DNT left in, to prevent
|
||||
// a whole heap of code from executing.
|
||||
return;
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import ReactGA from "../react-ga-proxy.js";
|
||||
import Storage from "../storage.js";
|
||||
import {
|
||||
bindCommonEventHandlers,
|
||||
GoogleAnalytics,
|
||||
initializePrimaryNav,
|
||||
injectCommonReactComponents,
|
||||
ReactGA
|
||||
} from "../common";
|
||||
|
||||
import primaryNav from "./components/primary-nav/primary-nav.js";
|
||||
import navNewsletter from "../nav-newsletter.js";
|
||||
import CreepVote from "./components/creep-vote/creep-vote.jsx";
|
||||
import Creepometer from "./components/creepometer/creepometer.jsx";
|
||||
import JoinUs from "../components/join/join.jsx";
|
||||
|
||||
import copyToClipboard from "../../js/copy-to-clipboard.js";
|
||||
import HomepageSlider from "./homepage-c-slider.js";
|
||||
|
@ -55,25 +59,14 @@ let main = {
|
|||
networkSiteURL = `https://${env.HEROKU_APP_NAME}.herokuapp.com`;
|
||||
}
|
||||
|
||||
// TODO: this should probably tap into the analytics.js file that
|
||||
// we use in main.js so that we only have one place where top level
|
||||
// changes to how analytics works need to be made.
|
||||
|
||||
const gaMeta = document.querySelector(`meta[name="ga-identifier"]`);
|
||||
if (gaMeta) {
|
||||
let gaIdentifier = gaMeta.getAttribute(`content`);
|
||||
ReactGA.initialize(gaIdentifier); // UA-87658599-6 by default
|
||||
ReactGA.pageview(window.location.pathname);
|
||||
AnalyticsEvents.init();
|
||||
} else {
|
||||
console.warn(`No GA identifier found: skipping bootstrap step`);
|
||||
}
|
||||
GoogleAnalytics.init();
|
||||
AnalyticsEvents.init();
|
||||
|
||||
this.enableCopyLinks();
|
||||
this.injectReactComponents();
|
||||
|
||||
primaryNav.init();
|
||||
navNewsletter.init(networkSiteURL, csrfToken);
|
||||
bindCommonEventHandlers();
|
||||
initializePrimaryNav(networkSiteURL, csrfToken, primaryNav);
|
||||
|
||||
if (document.getElementById(`view-home`)) {
|
||||
HomepageSlider.init();
|
||||
|
@ -120,6 +113,8 @@ let main = {
|
|||
|
||||
// Embed various React components based on the existence of containers within the current page
|
||||
injectReactComponents() {
|
||||
injectCommonReactComponents(apps, networkSiteURL, csrfToken);
|
||||
|
||||
document.querySelectorAll(`.creep-vote-target`).forEach(element => {
|
||||
let csrf = element.querySelector(`input[name=csrfmiddlewaretoken]`);
|
||||
let productName = element.dataset.productName;
|
||||
|
@ -170,24 +165,6 @@ let main = {
|
|||
})
|
||||
);
|
||||
});
|
||||
|
||||
document.querySelectorAll(`.join-us`).forEach(element => {
|
||||
const props = element.dataset;
|
||||
const sid = props.signupId || 0;
|
||||
props.apiUrl = `${networkSiteURL}/api/campaign/signups/${sid}/`;
|
||||
|
||||
props.csrfToken = props.csrfToken || csrfToken;
|
||||
props.isHidden = false;
|
||||
|
||||
apps.push(
|
||||
new Promise(resolve => {
|
||||
ReactDOM.render(
|
||||
<JoinUs {...props} whenLoaded={() => resolve()} />,
|
||||
element
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import ReactGA from "../../../react-ga-proxy";
|
||||
import { ReactGA } from "../../../common";
|
||||
import copyToClipboard from "../../../../js/copy-to-clipboard.js";
|
||||
|
||||
const SocialShareLink = props => {
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import { ReactGA } from "./react-ga-proxy.js";
|
||||
|
||||
/**
|
||||
* Check browser's "do not track" setting
|
||||
* @return {Boolean} if browser's "do not track" setting is on
|
||||
*/
|
||||
const checkDoNotTrack = () => {
|
||||
let _dntStatus = navigator.doNotTrack || navigator.msDoNotTrack,
|
||||
fxMatch = navigator.userAgent.match(/Firefox\/(\d+)/),
|
||||
ie10Match = navigator.userAgent.match(/MSIE 10/i),
|
||||
w8Match = navigator.appVersion.match(/Windows NT 6.2/);
|
||||
|
||||
if (fxMatch && Number(fxMatch[1]) < 32) {
|
||||
_dntStatus = `Unspecified`;
|
||||
} else if (ie10Match && w8Match) {
|
||||
_dntStatus = `Unspecified`;
|
||||
} else {
|
||||
_dntStatus =
|
||||
{ "0": `Disabled`, "1": `Enabled` }[_dntStatus] || `Unspecified`;
|
||||
}
|
||||
|
||||
return _dntStatus === `Enabled`;
|
||||
};
|
||||
|
||||
const DO_NOT_TRACK = checkDoNotTrack();
|
||||
|
||||
/**
|
||||
* Initialize Google Analytics and tracking pageviews
|
||||
*/
|
||||
const init = () => {
|
||||
const gaMeta = document.querySelector(`meta[name="ga-identifier"]`);
|
||||
|
||||
if (!gaMeta) return;
|
||||
|
||||
let gaIdentifier = gaMeta.getAttribute(`content`);
|
||||
|
||||
if (!gaIdentifier) {
|
||||
console.warn(`No GA identifier found: skipping bootstrap step`);
|
||||
}
|
||||
|
||||
if (!DO_NOT_TRACK) {
|
||||
ReactGA.initialize(gaIdentifier);
|
||||
ReactGA.pageview(window.location.pathname);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Object that includes analytics related configs and functions
|
||||
*/
|
||||
export const GoogleAnalytics = {
|
||||
doNotTrack: DO_NOT_TRACK,
|
||||
init: init
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* The following modules are the common modules which need to be called
|
||||
* on all pages on the main Foundation site, apps, and microsites.
|
||||
* e.g., Foundation site, PNI, MozFest etc
|
||||
*/
|
||||
export { bindCommonEventHandlers } from "./template-js-handles";
|
||||
export { GoogleAnalytics } from "./google-analytics.js";
|
||||
export { initializePrimaryNav } from "./initialize-primary-nav.js";
|
||||
export { injectCommonReactComponents } from "./inject-react";
|
||||
export { ReactGA } from "./react-ga-proxy.js";
|
|
@ -0,0 +1,12 @@
|
|||
import navNewsletter from "../nav-newsletter.js";
|
||||
|
||||
/**
|
||||
* Initiate primary nav scripts
|
||||
* @param {String} siteUrl Foundation site base URL
|
||||
* @param {String} csrfToken CSRF Token
|
||||
* @param {Object} primaryNavModule primary nav module to initiate
|
||||
*/
|
||||
export const initializePrimaryNav = (siteUrl, csrfToken, primaryNavModule) => {
|
||||
primaryNavModule.init();
|
||||
navNewsletter.init(siteUrl, csrfToken);
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
import injectJoinUs from "./join-us.js";
|
||||
|
||||
/**
|
||||
* Inject React components
|
||||
* @param {Array} apps The existing array we are using to to track all ReactDOM.render calls
|
||||
* @param {String} siteUrl Foundation site base URL
|
||||
* @param {String} csrfToken CSRF Token
|
||||
*/
|
||||
export const injectCommonReactComponents = (apps, siteUrl, csrfToken) => {
|
||||
injectJoinUs(apps, siteUrl, csrfToken);
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import JoinUs from "../../components/join/join.jsx";
|
||||
|
||||
/**
|
||||
* Inject newsletter signup forms
|
||||
* @param {Array} apps The existing array we are using to to track all ReactDOM.render calls
|
||||
* @param {String} siteUrl Foundation site base URL
|
||||
* @param {String} csrfToken CSRF Token
|
||||
*/
|
||||
export default (apps, siteUrl, csrfToken) => {
|
||||
// excluding `.join-us.on-nav` because it's taken care of by nav-newsletter.js
|
||||
document.querySelectorAll(`.join-us:not(.on-nav)`).forEach(element => {
|
||||
const props = element.dataset;
|
||||
const sid = props.signupId || 0;
|
||||
|
||||
props.apiUrl = `${siteUrl}/api/campaign/signups/${sid}/`;
|
||||
props.csrfToken = props.csrfToken || csrfToken;
|
||||
props.isHidden = false;
|
||||
|
||||
apps.push(
|
||||
new Promise(resolve => {
|
||||
ReactDOM.render(
|
||||
<JoinUs {...props} whenLoaded={() => resolve()} />,
|
||||
element
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
import ReactGAModule from "react-ga";
|
||||
import { GoogleAnalytics } from "./google-analytics.js";
|
||||
|
||||
// A no-operation object with the same API surface
|
||||
// as ReactGA, for when tracking is not appreciated:
|
||||
const noop = {
|
||||
initialize: () => {},
|
||||
pageview: () => {},
|
||||
event: () => {}
|
||||
};
|
||||
|
||||
const TrackingObject = GoogleAnalytics.doNotTrack ? noop : ReactGAModule;
|
||||
|
||||
export { TrackingObject as ReactGA };
|
|
@ -0,0 +1,18 @@
|
|||
import { ReactGA } from "../react-ga-proxy.js";
|
||||
|
||||
/**
|
||||
* Bind click handler to #donate-footer-btn
|
||||
*/
|
||||
export default () => {
|
||||
let donateFooterBtn = document.getElementById(`donate-footer-btn`);
|
||||
|
||||
if (donateFooterBtn) {
|
||||
donateFooterBtn.addEventListener(`click`, () => {
|
||||
ReactGA.event({
|
||||
category: `donate`,
|
||||
action: `donate button tap`,
|
||||
label: `${document.title} footer`
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
import bindFooterDonateButtonHandler from "./footer-donate-button";
|
||||
|
||||
/**
|
||||
* Bind event handlers
|
||||
*/
|
||||
export const bindCommonEventHandlers = () => {
|
||||
bindFooterDonateButtonHandler();
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import ReactGA from "react-ga";
|
||||
import { ReactGA } from "../../common";
|
||||
import ReactDOM from "react-dom";
|
||||
import classNames from "classnames";
|
||||
import CountrySelect from "../petition/country-select.jsx";
|
||||
|
@ -452,7 +452,7 @@ export default class JoinUs extends React.Component {
|
|||
!this.state.apiSubmitted &&
|
||||
!this.privacy.checked &&
|
||||
!this.isFlowForm() && (
|
||||
<span class="form-error-glyph privacy-error d-flex" />
|
||||
<span className="form-error-glyph privacy-error d-flex" />
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
|
@ -515,7 +515,7 @@ export default class JoinUs extends React.Component {
|
|||
{this.renderSubmitButton()}
|
||||
{this.isFlowForm() && (
|
||||
<button
|
||||
class="btn btn-primary btn-dismiss flex-1"
|
||||
className="btn btn-primary btn-dismiss flex-1"
|
||||
onClick={() => this.props.handleSignUp(false)}
|
||||
type="button"
|
||||
>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import ReactGA from "react-ga";
|
||||
import { ReactGA } from "../../common";
|
||||
import classNames from "classnames";
|
||||
import DonationModal from "./donation-modal.jsx";
|
||||
import FloatingLabelInput from "./floating-label-input.jsx";
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
let _dntStatus = navigator.doNotTrack || navigator.msDoNotTrack,
|
||||
fxMatch = navigator.userAgent.match(/Firefox\/(\d+)/),
|
||||
ie10Match = navigator.userAgent.match(/MSIE 10/i),
|
||||
w8Match = navigator.appVersion.match(/Windows NT 6.2/);
|
||||
|
||||
if (fxMatch && Number(fxMatch[1]) < 32) {
|
||||
_dntStatus = `Unspecified`;
|
||||
} else if (ie10Match && w8Match) {
|
||||
_dntStatus = `Unspecified`;
|
||||
} else {
|
||||
_dntStatus = { "0": `Disabled`, "1": `Enabled` }[_dntStatus] || `Unspecified`;
|
||||
}
|
||||
|
||||
const DNT = {
|
||||
allowTracking: _dntStatus !== `Enabled`
|
||||
};
|
||||
|
||||
export default DNT;
|
|
@ -1,12 +1,16 @@
|
|||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "React" }] */
|
||||
|
||||
import React from "react";
|
||||
import ReactGA from "react-ga";
|
||||
import ReactDOM from "react-dom";
|
||||
import Analytics from "./analytics.js";
|
||||
import * as Sentry from "@sentry/browser";
|
||||
import {
|
||||
bindCommonEventHandlers,
|
||||
GoogleAnalytics,
|
||||
initializePrimaryNav,
|
||||
injectCommonReactComponents,
|
||||
ReactGA
|
||||
} from "./common";
|
||||
|
||||
import JoinUs from "./components/join/join.jsx";
|
||||
import Petition from "./components/petition/petition.jsx";
|
||||
import MultipageNavMobile from "./components/multipage-nav-mobile/multipage-nav-mobile.jsx";
|
||||
import News from "./components/news/news.jsx";
|
||||
|
@ -14,7 +18,6 @@ import PulseProjectList from "./components/pulse-project-list/pulse-project-list
|
|||
import ShareButtonGroup from "./components/share-button-group/share-button-group.jsx";
|
||||
|
||||
import primaryNav from "./primary-nav.js";
|
||||
import navNewsletter from "./nav-newsletter.js";
|
||||
import bindMozFestGA from "./mozfest-ga.js";
|
||||
import bindMozFestEventHandlers from "./mozfest-event-handlers";
|
||||
import youTubeRegretsTunnel from "./youtube-regrets.js";
|
||||
|
@ -60,11 +63,7 @@ let main = {
|
|||
networkSiteURL = `https://${env.HEROKU_APP_NAME}.herokuapp.com`;
|
||||
}
|
||||
|
||||
const gaMeta = document.querySelector(`meta[name="ga-identifier"]`);
|
||||
if (gaMeta) {
|
||||
let gaIdentifier = gaMeta.getAttribute(`content`);
|
||||
Analytics.initialize(gaIdentifier);
|
||||
}
|
||||
GoogleAnalytics.init();
|
||||
|
||||
this.injectReactComponents();
|
||||
this.bindGlobalHandlers();
|
||||
|
@ -205,8 +204,7 @@ let main = {
|
|||
// Call once to get scroll position on initial page load.
|
||||
onScroll();
|
||||
|
||||
primaryNav.init();
|
||||
navNewsletter.init(networkSiteURL, csrfToken);
|
||||
initializePrimaryNav(networkSiteURL, csrfToken, primaryNav);
|
||||
youTubeRegretsTunnel.init();
|
||||
|
||||
// Extra tracking
|
||||
|
@ -222,31 +220,26 @@ let main = {
|
|||
});
|
||||
}
|
||||
|
||||
let donateFooterBtn = document.getElementById(`donate-footer-btn`);
|
||||
if (donateFooterBtn) {
|
||||
donateFooterBtn.addEventListener(`click`, () => {
|
||||
ReactGA.event({
|
||||
category: `donate`,
|
||||
action: `donate button tap`,
|
||||
label: `${document.title} footer`
|
||||
});
|
||||
});
|
||||
}
|
||||
bindCommonEventHandlers();
|
||||
},
|
||||
|
||||
bindGAEventTrackers() {
|
||||
let seeMorePage = document.querySelector(`#see-more-modular-page`);
|
||||
|
||||
if (seeMorePage) {
|
||||
let label = ``;
|
||||
let pageHeader = document.querySelector(`.cms h1`);
|
||||
|
||||
if (pageHeader) {
|
||||
label = `${pageHeader.innerText} - footer cta`;
|
||||
}
|
||||
|
||||
seeMorePage.addEventListener(`click`, () => {
|
||||
let label = ``;
|
||||
let pageHeader = document.querySelector(`.cms h1`);
|
||||
|
||||
if (pageHeader) {
|
||||
label = `${pageHeader.innerText} - footer cta`;
|
||||
}
|
||||
|
||||
Analytics.sendGAEvent(`navigation`, `page footer cta`, label);
|
||||
ReactGA.event({
|
||||
category: `navigation`,
|
||||
action: `page footer cta`,
|
||||
label: label
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -271,25 +264,7 @@ let main = {
|
|||
|
||||
// Embed various React components based on the existence of containers within the current page
|
||||
injectReactComponents() {
|
||||
// Embed additional instances of the Join Us box that don't need an API exposed (eg: Homepage)
|
||||
document.querySelectorAll(`.join-us:not(.on-nav)`).forEach(element => {
|
||||
var props = element.dataset;
|
||||
|
||||
props.apiUrl = `${networkSiteURL}/api/campaign/signups/${props.signupId ||
|
||||
0}/`;
|
||||
|
||||
props.csrfToken = props.csrfToken || csrfToken;
|
||||
props.isHidden = false;
|
||||
|
||||
apps.push(
|
||||
new Promise(resolve => {
|
||||
ReactDOM.render(
|
||||
<JoinUs {...props} whenLoaded={() => resolve()} />,
|
||||
element
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
injectCommonReactComponents(apps, networkSiteURL, csrfToken);
|
||||
|
||||
// petition elements
|
||||
var subscribed = false;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ReactGA from "./react-ga-proxy.js";
|
||||
import { ReactGA } from "./common";
|
||||
|
||||
const bindMozfestGAEventTrackers = () => {
|
||||
let homeWatchVideoButton = document.querySelector(
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from "react";
|
||||
import ReactGA from "react-ga";
|
||||
import ReactDOM from "react-dom";
|
||||
import JoinUs from "./components/join/join.jsx";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ReactGA from "react-ga";
|
||||
import { ReactGA } from "./common";
|
||||
import navNewsletter from "./nav-newsletter.js";
|
||||
|
||||
let primaryNav = {
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import DNT from "./dnt.js";
|
||||
import ReactGA from "react-ga";
|
||||
|
||||
// A no-operation object with the same API surface
|
||||
// as ReactGA, for when tracking is not appreciated:
|
||||
const noop = {
|
||||
initialize: () => {},
|
||||
pageview: () => {},
|
||||
event: () => {}
|
||||
};
|
||||
|
||||
const TrackingObject = DNT.allowTracking ? ReactGA : noop;
|
||||
|
||||
export default TrackingObject;
|
Загрузка…
Ссылка в новой задаче