diff --git a/build/build.js b/build/build.js index 0de7868..9115551 100644 --- a/build/build.js +++ b/build/build.js @@ -80,6 +80,7 @@ async function buildAllPlugin() { var plugins = [ {name: 'search', input: 'search/index.js'}, {name: 'ga', input: 'ga.js'}, + {name: 'gtag', input: 'gtag.js'}, {name: 'matomo', input: 'matomo.js'}, {name: 'emoji', input: 'emoji.js'}, {name: 'external-script', input: 'external-script.js'}, diff --git a/docs/plugins.md b/docs/plugins.md index 3db7ff5..dce1f4e 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -71,6 +71,8 @@ This plugin ignores diacritical marks when performing a full text search (e.g., ## Google Analytics +> Google's Universal Analytics service will no longer process new data in standard properties beginning July 1, 2023. Prepare now by setting up and switching over to a Google Analytics 4 property and docsify's gtag.js plugin. + Install the plugin and configure the track id. ```html @@ -91,6 +93,31 @@ Configure by `data-ga`. ``` +## Google Analytics 4 (GA4) + +Install the plugin and configure the track id. + +```html + + + +``` + ## Emoji Renders a larger collection of emoji shorthand codes. Without this plugin, Docsify is able to render only a limited number of emoji shorthand codes. diff --git a/src/plugins/gtag.js b/src/plugins/gtag.js new file mode 100644 index 0000000..aec713c --- /dev/null +++ b/src/plugins/gtag.js @@ -0,0 +1,72 @@ +/* eslint-disable no-console */ +// From ./ga.js + +function appendScript(id) { + const script = document.createElement('script'); + script.async = true; + script.src = 'https://www.googletagmanager.com/gtag/js?id=' + id; + document.body.appendChild(script); +} + +// global site tag instance initialized +function initGlobalSiteTag(id) { + appendScript(id); + + window.dataLayer = window.dataLayer || []; + window.gtag = + window.gtag || + function () { + window.dataLayer.push(arguments); + }; + + window.gtag('js', new Date()); + window.gtag('config', id); +} + +// add additional products to your tag +// https://developers.google.com/tag-platform/gtagjs/install +function initAdditionalTag(id) { + window.gtag('config', id); +} + +function init(ids) { + if (Array.isArray(ids)) { + // set the first id to be a global site tag + initGlobalSiteTag(ids[0]); + + // the rest ids + ids.forEach((id, index) => { + if (index > 0) { + initAdditionalTag(id); + } + }); + } else { + initGlobalSiteTag(ids); + } +} + +function collect() { + if (!window.gtag) { + init($docsify.gtag); + } + + // usage: https://developers.google.com/analytics/devguides/collection/gtagjs/pages + window.gtag('event', 'page_view', { + /* eslint-disable camelcase */ + page_title: document.title, + page_location: location.href, + page_path: location.pathname, + /* eslint-disable camelcase */ + }); +} + +const install = function (hook) { + if (!$docsify.gtag) { + console.error('[Docsify] gtag is required.'); + return; + } + + hook.beforeEach(collect); +}; + +$docsify.plugins = [].concat(install, $docsify.plugins); diff --git a/test/e2e/gtag.test.js b/test/e2e/gtag.test.js new file mode 100644 index 0000000..c3ebbcd --- /dev/null +++ b/test/e2e/gtag.test.js @@ -0,0 +1,97 @@ +// Modules, constants, and variables +// npm run test:e2e gtag.test.js +// ----------------------------------------------------------------------------- +const docsifyInit = require('../helpers/docsify-init'); +const { test, expect } = require('./fixtures/docsify-init-fixture'); + +const gtagList = [ + 'AW-YYYYYY', // Google Ads + 'DC-ZZZZZZ', // Floodlight + 'G-XXXXXX', // Google Analytics 4 (GA4) + 'UA-XXXXXX', // Google Universal Analytics (GA3) +]; + +// Suite +// ----------------------------------------------------------------------------- +test.describe('Gtag Plugin Tests', () => { + // page request listened, print collect url + function pageRequestListened(page) { + page.on('request', request => { + if (request.url().indexOf('www.google-analytics.com') !== -1) { + // console.log(request.url()); + } + }); + + page.on('response', response => { + const request = response.request(); + // googleads.g.doubleclick.net + // www.google-analytics.com + // www.googletagmanager.com + const reg = + /googleads\.g\.doubleclick\.net|www\.google-analytics\.com|www\.googletagmanager\.com/g; + if (request.url().match(reg)) { + // console.log(request.url(), response.status()); + } + }); + } + + // Tests + // --------------------------------------------------------------------------- + test('single gtag', async ({ page }) => { + pageRequestListened(page); + + const docsifyInitConfig = { + config: { + gtag: gtagList[0], + }, + scriptURLs: ['/lib/plugins/gtag.min.js'], + styleURLs: ['/lib/themes/vue.css'], + }; + + await docsifyInit({ + ...docsifyInitConfig, + }); + + const $docsify = await page.evaluate(() => window.$docsify); + + // Verify config options + expect(typeof $docsify).toEqual('object'); + + // console.log($docsify.gtag, $docsify.gtag === ''); + + // Tests + expect($docsify.gtag).not.toEqual(''); + }); + + test('multi gtag', async ({ page }) => { + pageRequestListened(page); + + const docsifyInitConfig = { + config: { + gtag: gtagList, + }, + scriptURLs: ['/lib/plugins/gtag.min.js'], + styleURLs: ['/lib/themes/vue.css'], + }; + + await docsifyInit({ + ...docsifyInitConfig, + }); + + const $docsify = await page.evaluate(() => window.$docsify); + + // Verify config options + expect(typeof $docsify).toEqual('object'); + + // console.log($docsify.gtag, $docsify.gtag === ''); + + // Tests + expect($docsify.gtag).not.toEqual(''); + }); + + test('data-ga attribute', async ({ page }) => { + pageRequestListened(page); + + // TODO + }); +});