rrweb
Version:
record and replay the web
361 lines (358 loc) • 12.9 kB
JavaScript
import { __spread, __assign } from '../../node_modules/tslib/tslib.es6.js';
import { IncrementalSource, MouseInteractions, MediaInteractions } from '../types.js';
import { on, throttle, isTouchEvent, mirror, isBlocked, getWindowHeight, getWindowWidth, hookSetter } from '../utils.js';
import MutationBuffer from './mutation.js';
function initMutationObserver(cb, blockClass, inlineStylesheet, maskInputOptions) {
var mutationBuffer = new MutationBuffer(cb, blockClass, inlineStylesheet, maskInputOptions);
var observer = new MutationObserver(mutationBuffer.processMutations);
observer.observe(document, {
attributes: true,
attributeOldValue: true,
characterData: true,
characterDataOldValue: true,
childList: true,
subtree: true,
});
return observer;
}
function initMoveObserver(cb, sampling) {
if (sampling.mousemove === false) {
return function () { };
}
var threshold = typeof sampling.mousemove === 'number' ? sampling.mousemove : 50;
var positions = [];
var timeBaseline;
var wrappedCb = throttle(function (isTouch) {
var totalOffset = Date.now() - timeBaseline;
cb(positions.map(function (p) {
p.timeOffset -= totalOffset;
return p;
}), isTouch ? IncrementalSource.TouchMove : IncrementalSource.MouseMove);
positions = [];
timeBaseline = null;
}, 500);
var updatePosition = throttle(function (evt) {
var target = evt.target;
var _a = isTouchEvent(evt)
? evt.changedTouches[0]
: evt, clientX = _a.clientX, clientY = _a.clientY;
if (!timeBaseline) {
timeBaseline = Date.now();
}
positions.push({
x: clientX,
y: clientY,
id: mirror.getId(target),
timeOffset: Date.now() - timeBaseline,
});
wrappedCb(isTouchEvent(evt));
}, threshold, {
trailing: false,
});
var handlers = [
on('mousemove', updatePosition),
on('touchmove', updatePosition),
];
return function () {
handlers.forEach(function (h) { return h(); });
};
}
function initMouseInteractionObserver(cb, blockClass, sampling) {
if (sampling.mouseInteraction === false) {
return function () { };
}
var disableMap = sampling.mouseInteraction === true ||
sampling.mouseInteraction === undefined
? {}
: sampling.mouseInteraction;
var handlers = [];
var getHandler = function (eventKey) {
return function (event) {
if (isBlocked(event.target, blockClass)) {
return;
}
var id = mirror.getId(event.target);
var _a = isTouchEvent(event)
? event.changedTouches[0]
: event, clientX = _a.clientX, clientY = _a.clientY;
cb({
type: MouseInteractions[eventKey],
id: id,
x: clientX,
y: clientY,
});
};
};
Object.keys(MouseInteractions)
.filter(function (key) {
return Number.isNaN(Number(key)) &&
!key.endsWith('_Departed') &&
disableMap[key] !== false;
})
.forEach(function (eventKey) {
var eventName = eventKey.toLowerCase();
var handler = getHandler(eventKey);
handlers.push(on(eventName, handler));
});
return function () {
handlers.forEach(function (h) { return h(); });
};
}
function initScrollObserver(cb, blockClass, sampling) {
var updatePosition = throttle(function (evt) {
if (!evt.target || isBlocked(evt.target, blockClass)) {
return;
}
var id = mirror.getId(evt.target);
if (evt.target === document) {
var scrollEl = (document.scrollingElement || document.documentElement);
cb({
id: id,
x: scrollEl.scrollLeft,
y: scrollEl.scrollTop,
});
}
else {
cb({
id: id,
x: evt.target.scrollLeft,
y: evt.target.scrollTop,
});
}
}, sampling.scroll || 100);
return on('scroll', updatePosition);
}
function initViewportResizeObserver(cb) {
var updateDimension = throttle(function () {
var height = getWindowHeight();
var width = getWindowWidth();
cb({
width: Number(width),
height: Number(height),
});
}, 200);
return on('resize', updateDimension, window);
}
var INPUT_TAGS = ['INPUT', 'TEXTAREA', 'SELECT'];
var lastInputValueMap = new WeakMap();
function initInputObserver(cb, blockClass, ignoreClass, maskInputOptions, sampling) {
function eventHandler(event) {
var target = event.target;
if (!target ||
!target.tagName ||
INPUT_TAGS.indexOf(target.tagName) < 0 ||
isBlocked(target, blockClass)) {
return;
}
var type = target.type;
if (type === 'password' ||
target.classList.contains(ignoreClass)) {
return;
}
var text = target.value;
var isChecked = false;
if (type === 'radio' || type === 'checkbox') {
isChecked = target.checked;
}
else if (maskInputOptions[target.tagName.toLowerCase()] ||
maskInputOptions[type]) {
text = '*'.repeat(text.length);
}
cbWithDedup(target, { text: text, isChecked: isChecked });
var name = target.name;
if (type === 'radio' && name && isChecked) {
document
.querySelectorAll("input[type=\"radio\"][name=\"" + name + "\"]")
.forEach(function (el) {
if (el !== target) {
cbWithDedup(el, {
text: el.value,
isChecked: !isChecked,
});
}
});
}
}
function cbWithDedup(target, v) {
var lastInputValue = lastInputValueMap.get(target);
if (!lastInputValue ||
lastInputValue.text !== v.text ||
lastInputValue.isChecked !== v.isChecked) {
lastInputValueMap.set(target, v);
var id = mirror.getId(target);
cb(__assign(__assign({}, v), { id: id }));
}
}
var events = sampling.input === 'last' ? ['change'] : ['input', 'change'];
var handlers = events.map(function (eventName) { return on(eventName, eventHandler); });
var propertyDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
var hookProperties = [
[HTMLInputElement.prototype, 'value'],
[HTMLInputElement.prototype, 'checked'],
[HTMLSelectElement.prototype, 'value'],
[HTMLTextAreaElement.prototype, 'value'],
];
if (propertyDescriptor && propertyDescriptor.set) {
handlers.push.apply(handlers, __spread(hookProperties.map(function (p) {
return hookSetter(p[0], p[1], {
set: function () {
eventHandler({ target: this });
},
});
})));
}
return function () {
handlers.forEach(function (h) { return h(); });
};
}
function initStyleSheetObserver(cb) {
var insertRule = CSSStyleSheet.prototype.insertRule;
CSSStyleSheet.prototype.insertRule = function (rule, index) {
var id = mirror.getId(this.ownerNode);
if (id !== -1) {
cb({
id: id,
adds: [{ rule: rule, index: index }],
});
}
return insertRule.apply(this, arguments);
};
var deleteRule = CSSStyleSheet.prototype.deleteRule;
CSSStyleSheet.prototype.deleteRule = function (index) {
var id = mirror.getId(this.ownerNode);
if (id !== -1) {
cb({
id: id,
removes: [{ index: index }],
});
}
return deleteRule.apply(this, arguments);
};
return function () {
CSSStyleSheet.prototype.insertRule = insertRule;
CSSStyleSheet.prototype.deleteRule = deleteRule;
};
}
function initMediaInteractionObserver(mediaInteractionCb, blockClass) {
var handler = function (type) { return function (event) {
var target = event.target;
if (!target || isBlocked(target, blockClass)) {
return;
}
mediaInteractionCb({
type: type === 'play' ? MediaInteractions.Play : MediaInteractions.Pause,
id: mirror.getId(target),
});
}; };
var handlers = [on('play', handler('play')), on('pause', handler('pause'))];
return function () {
handlers.forEach(function (h) { return h(); });
};
}
function mergeHooks(o, hooks) {
var mutationCb = o.mutationCb, mousemoveCb = o.mousemoveCb, mouseInteractionCb = o.mouseInteractionCb, scrollCb = o.scrollCb, viewportResizeCb = o.viewportResizeCb, inputCb = o.inputCb, mediaInteractionCb = o.mediaInteractionCb, styleSheetRuleCb = o.styleSheetRuleCb;
o.mutationCb = function () {
var p = [];
for (var _i = 0; _i < arguments.length; _i++) {
p[_i] = arguments[_i];
}
if (hooks.mutation) {
hooks.mutation.apply(hooks, __spread(p));
}
mutationCb.apply(void 0, __spread(p));
};
o.mousemoveCb = function () {
var p = [];
for (var _i = 0; _i < arguments.length; _i++) {
p[_i] = arguments[_i];
}
if (hooks.mousemove) {
hooks.mousemove.apply(hooks, __spread(p));
}
mousemoveCb.apply(void 0, __spread(p));
};
o.mouseInteractionCb = function () {
var p = [];
for (var _i = 0; _i < arguments.length; _i++) {
p[_i] = arguments[_i];
}
if (hooks.mouseInteraction) {
hooks.mouseInteraction.apply(hooks, __spread(p));
}
mouseInteractionCb.apply(void 0, __spread(p));
};
o.scrollCb = function () {
var p = [];
for (var _i = 0; _i < arguments.length; _i++) {
p[_i] = arguments[_i];
}
if (hooks.scroll) {
hooks.scroll.apply(hooks, __spread(p));
}
scrollCb.apply(void 0, __spread(p));
};
o.viewportResizeCb = function () {
var p = [];
for (var _i = 0; _i < arguments.length; _i++) {
p[_i] = arguments[_i];
}
if (hooks.viewportResize) {
hooks.viewportResize.apply(hooks, __spread(p));
}
viewportResizeCb.apply(void 0, __spread(p));
};
o.inputCb = function () {
var p = [];
for (var _i = 0; _i < arguments.length; _i++) {
p[_i] = arguments[_i];
}
if (hooks.input) {
hooks.input.apply(hooks, __spread(p));
}
inputCb.apply(void 0, __spread(p));
};
o.mediaInteractionCb = function () {
var p = [];
for (var _i = 0; _i < arguments.length; _i++) {
p[_i] = arguments[_i];
}
if (hooks.mediaInteaction) {
hooks.mediaInteaction.apply(hooks, __spread(p));
}
mediaInteractionCb.apply(void 0, __spread(p));
};
o.styleSheetRuleCb = function () {
var p = [];
for (var _i = 0; _i < arguments.length; _i++) {
p[_i] = arguments[_i];
}
if (hooks.styleSheetRule) {
hooks.styleSheetRule.apply(hooks, __spread(p));
}
styleSheetRuleCb.apply(void 0, __spread(p));
};
}
function initObservers(o, hooks) {
if (hooks === void 0) { hooks = {}; }
mergeHooks(o, hooks);
var mutationObserver = initMutationObserver(o.mutationCb, o.blockClass, o.inlineStylesheet, o.maskInputOptions);
var mousemoveHandler = initMoveObserver(o.mousemoveCb, o.sampling);
var mouseInteractionHandler = initMouseInteractionObserver(o.mouseInteractionCb, o.blockClass, o.sampling);
var scrollHandler = initScrollObserver(o.scrollCb, o.blockClass, o.sampling);
var viewportResizeHandler = initViewportResizeObserver(o.viewportResizeCb);
var inputHandler = initInputObserver(o.inputCb, o.blockClass, o.ignoreClass, o.maskInputOptions, o.sampling);
var mediaInteractionHandler = initMediaInteractionObserver(o.mediaInteractionCb, o.blockClass);
var styleSheetObserver = initStyleSheetObserver(o.styleSheetRuleCb);
return function () {
mutationObserver.disconnect();
mousemoveHandler();
mouseInteractionHandler();
scrollHandler();
viewportResizeHandler();
inputHandler();
mediaInteractionHandler();
styleSheetObserver();
};
}
export default initObservers;
export { INPUT_TAGS };