posthog-js
Version:
Posthog-js allows you to automatically capture usage and send events to PostHog.
293 lines • 13.2 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Heatmaps = void 0;
var rageclick_1 = __importDefault(require("./extensions/rageclick"));
var globals_1 = require("@posthog/browser-common/utils/globals");
var autocapture_utils_1 = require("@posthog/browser-common/utils/autocapture-utils");
var constants_1 = require("./constants");
var core_1 = require("@posthog/core");
var logger_1 = require("@posthog/browser-common/utils/logger");
var element_utils_1 = require("@posthog/browser-common/utils/element-utils");
var dead_clicks_autocapture_1 = require("./extensions/dead-clicks-autocapture");
var core_2 = require("@posthog/core");
var general_utils_1 = require("@posthog/browser-common/utils/general-utils");
var request_utils_1 = require("@posthog/browser-common/utils/request-utils");
var event_utils_1 = require("@posthog/browser-common/utils/event-utils");
var DEFAULT_FLUSH_INTERVAL = 5000;
var logger = (0, logger_1.createLogger)('[Heatmaps]');
function elementOrParentPositionMatches(el, matches, breakOnElement) {
var _a, _b, _c;
var curEl = el;
while (curEl && (0, element_utils_1.isElementNode)(curEl) && !(0, element_utils_1.isTag)(curEl, 'body')) {
if (curEl === breakOnElement) {
return false;
}
var position = void 0;
try {
position = (_c = ((_b = (_a = curEl.ownerDocument) === null || _a === void 0 ? void 0 : _a.defaultView) !== null && _b !== void 0 ? _b : globals_1.window)) === null || _c === void 0 ? void 0 : _c.getComputedStyle(curEl).position;
}
catch (_d) {
return false;
}
if ((0, core_2.includes)(matches, position)) {
return true;
}
curEl = (0, autocapture_utils_1.getParentElement)(curEl);
}
return false;
}
function isValidMouseEvent(e) {
return (0, core_1.isObject)(e) && 'clientX' in e && 'clientY' in e && (0, core_1.isNumber)(e.clientX) && (0, core_1.isNumber)(e.clientY);
}
function shouldPoll(document) {
return (document === null || document === void 0 ? void 0 : document.visibilityState) === 'visible';
}
var Heatmaps = /** @class */ (function () {
function Heatmaps(instance) {
var _a;
this._enabledServerSide = false;
this._initialized = false;
this._flushInterval = null;
this.instance = instance;
this._enabledServerSide = !!((_a = this.instance.persistence) === null || _a === void 0 ? void 0 : _a.props[constants_1.HEATMAPS_ENABLED_SERVER_SIDE]);
this.rageclicks = new rageclick_1.default(instance.config.rageclick);
}
Object.defineProperty(Heatmaps.prototype, "_config", {
get: function () {
return this.instance.config;
},
enumerable: false,
configurable: true
});
Heatmaps.prototype.initialize = function () {
this.startIfEnabled();
};
Object.defineProperty(Heatmaps.prototype, "flushIntervalMilliseconds", {
get: function () {
var flushInterval = DEFAULT_FLUSH_INTERVAL;
if ((0, core_1.isObject)(this._config.capture_heatmaps) && this._config.capture_heatmaps.flush_interval_milliseconds) {
flushInterval = this._config.capture_heatmaps.flush_interval_milliseconds;
}
return flushInterval;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Heatmaps.prototype, "isEnabled", {
get: function () {
if (!(0, core_1.isNullish)(this._config.capture_heatmaps)) {
return this._config.capture_heatmaps !== false;
}
if (!(0, core_1.isNullish)(this._config.enable_heatmaps)) {
return this._config.enable_heatmaps;
}
return this._enabledServerSide;
},
enumerable: false,
configurable: true
});
Heatmaps.prototype.startIfEnabled = function () {
var _a;
if (this.isEnabled) {
// nested if here since we only want to run the else
// if this.enabled === false
// not if this method is called more than once
if (this._initialized) {
return;
}
logger.info('starting...');
this._setupListeners();
this._onVisibilityChange();
}
else {
clearInterval((_a = this._flushInterval) !== null && _a !== void 0 ? _a : undefined);
this._removeListeners();
this.getAndClearBuffer();
}
};
Heatmaps.prototype.onRemoteConfig = function (result) {
var _a;
if (!result.ok) {
// Failure behaves like a response without a heatmaps key.
return;
}
var response = result.config;
if (!('heatmaps' in response)) {
return;
}
var optIn = !!response['heatmaps'];
if (this.instance.persistence) {
this.instance.persistence.register((_a = {},
_a[constants_1.HEATMAPS_ENABLED_SERVER_SIDE] = optIn,
_a));
}
// store this in-memory in case persistence is disabled
this._enabledServerSide = optIn;
this.startIfEnabled();
};
Heatmaps.prototype.getAndClearBuffer = function () {
var buffer = this._buffer;
this._buffer = undefined;
return buffer;
};
Heatmaps.prototype._onDeadClick = function (click) {
// heatmaps only plots click coordinates; this also rejects swipe candidates (never
// expected on this path), since a swipe's TouchEvent has no clientX/clientY
if (!isValidMouseEvent(click.originalEvent)) {
return;
}
this._onClick(click.originalEvent, 'deadclick');
};
Heatmaps.prototype._onVisibilityChange = function () {
// always clear the interval just in case
if (this._flushInterval) {
clearInterval(this._flushInterval);
}
this._flushInterval = shouldPoll(globals_1.document)
? setInterval(this._flush.bind(this), this.flushIntervalMilliseconds)
: null;
};
Heatmaps.prototype._setupListeners = function () {
var _this = this;
if (!globals_1.window || !globals_1.document) {
return;
}
this._flushHandler = this._flush.bind(this);
(0, general_utils_1.addEventListener)(globals_1.window, constants_1.DOM_EVENT_BEFOREUNLOAD, this._flushHandler);
this._onClickHandler = function (e) { return _this._onClick((e || (globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.event))); };
(0, general_utils_1.addEventListener)(globals_1.document, 'click', this._onClickHandler, { capture: true });
this._onMouseMoveHandler = function (e) { return _this._onMouseMove((e || (globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.event))); };
(0, general_utils_1.addEventListener)(globals_1.document, 'mousemove', this._onMouseMoveHandler, { capture: true });
this._deadClicksCapture = new dead_clicks_autocapture_1.DeadClicksAutocapture(this.instance, dead_clicks_autocapture_1.isDeadClicksEnabledForHeatmaps, this._onDeadClick.bind(this));
this._deadClicksCapture.startIfEnabledOrStop();
this._onVisibilityChange_handler = this._onVisibilityChange.bind(this);
(0, general_utils_1.addEventListener)(globals_1.document, constants_1.DOM_EVENT_VISIBILITYCHANGE, this._onVisibilityChange_handler);
this._initialized = true;
};
Heatmaps.prototype._removeListeners = function () {
var _a;
if (!globals_1.window || !globals_1.document) {
return;
}
if (this._flushHandler) {
globals_1.window.removeEventListener(constants_1.DOM_EVENT_BEFOREUNLOAD, this._flushHandler);
}
if (this._onClickHandler) {
globals_1.document.removeEventListener('click', this._onClickHandler, { capture: true });
}
if (this._onMouseMoveHandler) {
globals_1.document.removeEventListener('mousemove', this._onMouseMoveHandler, { capture: true });
}
if (this._onVisibilityChange_handler) {
globals_1.document.removeEventListener(constants_1.DOM_EVENT_VISIBILITYCHANGE, this._onVisibilityChange_handler);
}
clearTimeout(this._mouseMoveTimeout);
(_a = this._deadClicksCapture) === null || _a === void 0 ? void 0 : _a.stop();
this._initialized = false;
};
Heatmaps.prototype._getProperties = function (e, type) {
// We need to know if the target element is fixed or not
// If fixed then we won't account for scrolling
// If not then we will account for scrolling
var scrollY = this.instance.scrollManager.scrollY();
var scrollX = this.instance.scrollManager.scrollX();
var scrollElement = this.instance.scrollManager.scrollElement();
var isFixedOrSticky = elementOrParentPositionMatches((0, autocapture_utils_1.getEventTarget)(e), ['fixed', 'sticky'], scrollElement);
return {
x: e.clientX + (isFixedOrSticky ? 0 : scrollX),
y: e.clientY + (isFixedOrSticky ? 0 : scrollY),
target_fixed: isFixedOrSticky,
type: type,
};
};
Heatmaps.prototype._onClick = function (e, type) {
var _a;
if (type === void 0) { type = 'click'; }
if ((0, element_utils_1.isElementInToolbar)(e.target) || !isValidMouseEvent(e)) {
return;
}
var properties = this._getProperties(e, type);
if (((_a = this.rageclicks) === null || _a === void 0 ? void 0 : _a.isRageClick(e.clientX, e.clientY, new Date().getTime())) &&
(0, autocapture_utils_1.shouldCaptureRageclick)((0, autocapture_utils_1.getEventTarget)(e), this.instance.config.rageclick)) {
this._capture(__assign(__assign({}, properties), { type: 'rageclick' }));
}
this._capture(properties);
};
Heatmaps.prototype._onMouseMove = function (e) {
var _this = this;
if ((0, element_utils_1.isElementInToolbar)(e.target) || !isValidMouseEvent(e)) {
return;
}
clearTimeout(this._mouseMoveTimeout);
this._mouseMoveTimeout = setTimeout(function () {
_this._capture(_this._getProperties(e, 'mousemove'));
}, 500);
};
Heatmaps.prototype._capture = function (properties) {
if (!globals_1.window) {
return;
}
var href = this._config.disable_capture_url_hashes ? (0, core_1.stripUrlHash)(globals_1.window.location.href) : globals_1.window.location.href;
// mask url query params
var maskPersonalDataProperties = this._config.mask_personal_data_properties;
var customPersonalDataProperties = this._config.custom_personal_data_properties;
var paramsToMask = maskPersonalDataProperties
? __spreadArray(__spreadArray([], __read(event_utils_1.PERSONAL_DATA_CAMPAIGN_PARAMS), false), __read((customPersonalDataProperties || [])), false) : [];
var url = (0, request_utils_1.maskQueryParams)(href, paramsToMask, event_utils_1.MASKED);
this._buffer = this._buffer || {};
if (!this._buffer[url]) {
this._buffer[url] = [];
}
this._buffer[url].push(properties);
};
Heatmaps.prototype._flush = function () {
if (!this._buffer || (0, core_1.isEmptyObject)(this._buffer)) {
return;
}
this.instance.capture('$$heatmap', {
$heatmap_data: this.getAndClearBuffer(),
});
};
return Heatmaps;
}());
exports.Heatmaps = Heatmaps;
//# sourceMappingURL=heatmaps.js.map