PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.3
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.3
4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 All 201 releases
betterdocs / assets / static / public / js / analytics-tracker.js

analytics-tracker.js in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.3, at assets/static/public/js/analytics-tracker.js

156 lines 3.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * BetterDocs lightweight analytics tracker.
3 *
4 * On a single doc: records a view after DOMContentLoaded, and tracks max scroll
5 * depth, sending a "scroll" beacon on page hide (reading-completion). Both go
6 * via fetch + keepalive with the X-WP-Nonce header (see the 401 fix in DECISIONS).
7 * No jQuery, no dependencies.
8 */
9 ( function () {
10 'use strict';
11
12 var cfg = window.betterDocsTracker;
13 if ( ! cfg || ! cfg.rest_url || ! cfg.post_id ) {
14 return;
15 }
16
17 var postId = parseInt( cfg.post_id, 10 );
18 if ( ! postId ) {
19 return;
20 }
21
22 function post( body ) {
23 try {
24 fetch( cfg.rest_url, {
25 method: 'POST',
26 headers: {
27 'Content-Type': 'application/json',
28 'X-WP-Nonce': cfg.nonce || ''
29 },
30 body: JSON.stringify( body ),
31 keepalive: true,
32 credentials: 'same-origin'
33 } );
34 } catch ( e ) {}
35 }
36
37 function sendView() {
38 var unique = 0;
39 try {
40 var key = 'bd_v_' + postId;
41 if ( ! window.localStorage.getItem( key ) ) {
42 unique = 1;
43 window.localStorage.setItem( key, '1' );
44 }
45 } catch ( e ) {
46 unique = 0;
47 }
48
49 var referrer = '';
50 try {
51 referrer = document.referrer || '';
52 } catch ( e ) {}
53
54 post( { post_id: postId, u: unique, referrer: referrer } );
55
56 forwardGA4( 'betterdocs_doc_view', {} );
57 }
58
59 // Forward an event to GA4 when the integration is on. cfg.ga4.method decides how:
60 // datalayer -> raw window.dataLayer push (for sites already using GTM)
61 // gtag -> gtag('event', …) via the gtag.js BetterDocs injected
62 // mp -> nothing here; the server sends it via the Measurement Protocol
63 function forwardGA4( event, extra ) {
64 var ga4 = cfg.ga4;
65 if ( ! ga4 || ! ga4.enabled ) {
66 return;
67 }
68 // Shared params — the event name is passed separately to gtag, embedded for dataLayer.
69 var params = { doc_id: postId };
70 for ( var k in extra ) {
71 if ( Object.prototype.hasOwnProperty.call( extra, k ) ) {
72 params[ k ] = extra[ k ];
73 }
74 }
75 try {
76 if ( ga4.method === 'gtag' ) {
77 if ( typeof window.gtag === 'function' ) {
78 window.gtag( 'event', event, params );
79 }
80 } else if ( ga4.method === 'datalayer' ) {
81 window.dataLayer = window.dataLayer || [];
82 var dl = { event: event, doc_id: postId };
83 for ( var j in extra ) {
84 if ( Object.prototype.hasOwnProperty.call( extra, j ) ) {
85 dl[ j ] = extra[ j ];
86 }
87 }
88 window.dataLayer.push( dl );
89 }
90 // method 'mp' is handled server-side; nothing to emit client-side.
91 } catch ( e ) {}
92 }
93
94 // --- reading completion (max scroll depth) ---
95 var maxDepth = 0;
96 var scrollSent = false;
97
98 function computeDepth() {
99 try {
100 var doc = document.documentElement;
101 var body = document.body;
102 var scrollTop = window.pageYOffset || doc.scrollTop || body.scrollTop || 0;
103 var winH = window.innerHeight || doc.clientHeight;
104 var docH = Math.max(
105 body.scrollHeight, doc.scrollHeight,
106 body.offsetHeight, doc.offsetHeight,
107 doc.clientHeight
108 );
109 if ( docH <= winH ) {
110 return 100; // fits on screen = fully read
111 }
112 var depth = Math.round( ( ( scrollTop + winH ) / docH ) * 100 );
113 return Math.max( 0, Math.min( 100, depth ) );
114 } catch ( e ) {
115 return 0;
116 }
117 }
118
119 function onScroll() {
120 var d = computeDepth();
121 if ( d > maxDepth ) {
122 maxDepth = d;
123 }
124 }
125
126 function sendScroll() {
127 if ( scrollSent ) {
128 return;
129 }
130 scrollSent = true;
131 var depth = Math.max( maxDepth, computeDepth() );
132 post( { post_id: postId, event: 'scroll', depth: depth } );
133 if ( depth >= 90 ) {
134 forwardGA4( 'betterdocs_doc_complete', { depth: depth } );
135 }
136 }
137
138 function init() {
139 sendView();
140 onScroll();
141 window.addEventListener( 'scroll', onScroll, { passive: true } );
142 window.addEventListener( 'pagehide', sendScroll );
143 document.addEventListener( 'visibilitychange', function () {
144 if ( document.visibilityState === 'hidden' ) {
145 sendScroll();
146 }
147 } );
148 }
149
150 if ( document.readyState === 'loading' ) {
151 document.addEventListener( 'DOMContentLoaded', init );
152 } else {
153 init();
154 }
155 } )();
156