PageSourceSearch

https://recipecan.com/wp-content/plugins/wp-power-analytics/assets/js/tracker.js?ver=5.4.8.4

js recipecan.com collected 2026-10-02 05:23:29 UTC 4,671 bytes, 161 lines download raw bytes

1/**
2 * WP Power Analytics — Frontend Tracker v4
3 *
4 * Strategy for reliable duration on shared hosting:
5 * 1. Page load → POST to record hit, get back row_id
6 * 2. Every 5 seconds: update time_on_page IN MEMORY (no network)
7 * 3. Every 30 seconds OR on page hide: send heartbeat with current time
8 * 4. On unload/pagehide: send final time synchronously
9 *
10 * Uses active-time-only (pauses when tab is hidden).
11 */
12(function () {
13  'use strict';
14
15  if (!window.wpta_obj) return;
16
17  /* ── Session ID ── */
18  function getSession() {
19    var KEY = 'wpta_sid';
20    var sid = sessionStorage.getItem(KEY);
21    if (!sid) {
22      sid = 'ss_' + Math.random().toString(36).substr(2, 14) + '_' + Date.now();
23      sessionStorage.setItem(KEY, sid);
24    }
25    return sid;
26  }
27
28  var SESSION    = getSession();
29  var ROW_ID     = null;    // set after initial hit confirms insert
30  var EXIT_SENT  = false;
31  var HB_TIMER   = null;
32
33  /* ── Active-time accumulator (pauses when tab hidden) ── */
34  var activeMs     = 0;
35  var activeStart  = null; // null = not counting
36
37  function startTimer()  { if (!activeStart) activeStart = Date.now(); }
38  function pauseTimer()  {
39    if (activeStart) {
40      activeMs += Date.now() - activeStart;
41      activeStart = null;
42    }
43  }
44  function getSeconds()  {
45    var ms = activeMs + (activeStart ? Date.now() - activeStart : 0);
46    return Math.max(1, Math.round(ms / 1000));
47  }
48
49  /* ── POST with JSON callback ── */
50  function post(params, cb) {
51    var fd = new FormData();
52    Object.keys(params).forEach(function(k){ fd.append(k, params[k]); });
53    var xhr = new XMLHttpRequest();
54    xhr.open('POST', wpta_obj.ajax_url, true);
55    xhr.onload = function() {
56      if (cb) try { cb(JSON.parse(xhr.responseText)); } catch(e) {}
57    };
58    xhr.send(fd);
59  }
60
61  /* ── Reliable beacon (tries sendBeacon first, falls back to sync XHR) ── */
62  function sendBeaconData(params) {
63    var fd = new FormData();
64    Object.keys(params).forEach(function(k){ fd.append(k, params[k]); });
65
66    // Try sendBeacon (async, survives page close)
67    if (navigator.sendBeacon && navigator.sendBeacon(wpta_obj.ajax_url, fd)) {
68      return;
69    }
70    // Fallback: synchronous XHR (blocks until complete)
71    try {
72      var xhr = new XMLHttpRequest();
73      xhr.open('POST', wpta_obj.ajax_url, false);
74      xhr.send(fd);
75    } catch(e) {}
76  }
77
78  /* ── Heartbeat: update time_on_page for this row ── */
79  function sendHeartbeat() {
80    if (!ROW_ID || EXIT_SENT) return;
81    pauseTimer();
82    var secs = getSeconds();
83    startTimer();
84    post({
85      action:       'wpta_heartbeat',
86      nonce:        wpta_obj.nonce,
87      row_id:       ROW_ID,
88      time_on_page: secs,
89    });
90  }
91
92  /* ── Exit: final time update, one shot only ── */
93  function sendExit() {
94    if (!ROW_ID || EXIT_SENT) return;
95    EXIT_SENT = true;
96    pauseTimer();
97    clearInterval(HB_TIMER);
98    sendBeaconData({
99      action:       'wpta_exit',
100      nonce:        wpta_obj.nonce,
101      row_id:       ROW_ID,
102      time_on_page: getSeconds(),
103    });
104  }
105
106  /* ── Visibility change ── */
107  document.addEventListener('visibilitychange', function() {
108    if (document.hidden) {
109      sendHeartbeat(); // capture time before backgrounding
110      pauseTimer();
111    } else {
112      startTimer();   // resume when user comes back
113    }
114  });
115
116  /* ── Unload / navigate away ── */
117  window.addEventListener('pagehide',     sendExit);
118  window.addEventListener('beforeunload', sendExit);
119
120  /* ── Click on any link: send heartbeat immediately before navigation ── */
121  document.addEventListener('click', function(e) {
122    var a = e.target.closest ? e.target.closest('a') : null;
123    if (!a) {
124      // IE/old: walk up
125      var t = e.target;
126      while (t && t.tagName !== 'A') t = t.parentElement;
127      a = t;
128    }
129    if (a && a.href && a.href.indexOf(window.location.hostname) > -1) {
130      sendHeartbeat();
131    }
132  }, true);
133
134  /* ── Initial page-view ── */
135  function init() {
136    startTimer();
137    post({
138      action:   'wpta_track',
139      nonce:    wpta_obj.nonce,
140      session:  SESSION,
141      url:      window.location.href,
142      title:    document.title,
143      referrer: document.referrer,
144    }, function(r) {
145      if (r && r.success && r.data && r.data.row_id) {
146        ROW_ID = r.data.row_id;
147        // Heartbeat every 30 seconds
148        HB_TIMER = setInterval(function() {
149          if (!document.hidden) sendHeartbeat();
150        }, 30000);
151      }
152    });
153  }
154
155  if (document.readyState === 'complete') {
156    init();
157  } else {
158    window.addEventListener('load', init);
159  }
160
161})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.