change(tracker): change input node checks

This commit is contained in:
nick-delirium 2023-01-16 14:42:22 +01:00
parent 7df87f1c9b
commit 126de51eb5
12 changed files with 39 additions and 45 deletions

View file

@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package",
"version": "4.1.9",
"version": "4.1.9-beta.2",
"keywords": [
"logging",
"replay"

View file

@ -24,21 +24,21 @@ export function isRootNode(node: Node): node is Document | DocumentFragment {
}
type TagTypeMap = {
HTML: HTMLHtmlElement
BODY: HTMLBodyElement
IMG: HTMLImageElement
INPUT: HTMLInputElement
TEXTAREA: HTMLTextAreaElement
SELECT: HTMLSelectElement
LABEL: HTMLLabelElement
IFRAME: HTMLIFrameElement
STYLE: HTMLStyleElement
style: SVGStyleElement
LINK: HTMLLinkElement
html: HTMLHtmlElement
body: HTMLBodyElement
img: HTMLImageElement
input: HTMLInputElement
textarea: HTMLTextAreaElement
select: HTMLSelectElement
label: HTMLLabelElement
iframe: HTMLIFrameElement
style: HTMLStyleElement | SVGStyleElement
link: HTMLLinkElement
}
export function hasTag<T extends keyof TagTypeMap>(
el: Node,
tagName: T,
): el is TagTypeMap[typeof tagName] {
return el.nodeName === tagName
// @ts-ignore
return el.localName === tagName
}

View file

@ -144,7 +144,7 @@ export default abstract class Observer {
}
if (
name === 'value' &&
hasTag(node, 'INPUT') &&
hasTag(node, 'input') &&
node.type !== 'button' &&
node.type !== 'reset' &&
node.type !== 'submit'
@ -155,7 +155,7 @@ export default abstract class Observer {
this.app.send(RemoveNodeAttribute(id, name))
return
}
if (name === 'style' || (name === 'href' && hasTag(node, 'LINK'))) {
if (name === 'style' || (name === 'href' && hasTag(node, 'link'))) {
this.app.send(SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref()))
return
}
@ -166,7 +166,7 @@ export default abstract class Observer {
}
private sendNodeData(id: number, parentElement: Element, data: string): void {
if (hasTag(parentElement, 'STYLE') || hasTag(parentElement, 'style')) {
if (hasTag(parentElement, 'style')) {
this.app.send(SetCSSDataURLBased(id, data, this.app.getBaseHref()))
return
}
@ -242,7 +242,7 @@ export default abstract class Observer {
// Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
// TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
// TODO: Clean the logic (though now it workd fine)
if (!hasTag(node, 'HTML') || !this.isTopContext) {
if (!hasTag(node, 'html') || !this.isTopContext) {
if (parent === null) {
// Sometimes one observation contains attribute mutations for the removimg node, which gets ignored here.
// That shouldn't affect the visual rendering ( should it? maybe when transition applied? )

View file

@ -34,7 +34,7 @@ export default class TopObserver extends Observer {
// IFrames
this.app.nodes.attachNodeCallback((node) => {
if (
hasTag(node, 'IFRAME') &&
hasTag(node, 'iframe') &&
((this.options.captureIFrames && !hasOpenreplayAttribute(node, 'obscured')) ||
hasOpenreplayAttribute(node, 'capture'))
) {

View file

@ -87,7 +87,7 @@ export default function (app: App | null) {
app.observer.attachContextCallback(patchContext)
app.nodes.attachNodeCallback((node: Node): void => {
if (!(hasTag(node, 'STYLE') || hasTag(node, 'style')) || !node.sheet) {
if (!hasTag(node, 'style') || !node.sheet) {
return
}
if (node.textContent !== null && node.textContent.trim().length > 0) {

View file

@ -12,7 +12,7 @@ export default function (app: App): void {
let blurred = false
app.nodes.attachNodeCallback((node) => {
if (!hasTag(node, 'BODY')) {
if (!hasTag(node, 'body')) {
return
}
app.nodes.attachNodeListener(node, 'focus', (e: FocusEvent): void => {
@ -35,7 +35,7 @@ export default function (app: App): void {
})
app.attachStartCallback(() => {
let elem = document.activeElement
while (elem && hasTag(elem, 'IFRAME') && elem.contentDocument) {
while (elem && hasTag(elem, 'iframe') && elem.contentDocument) {
elem = elem.contentDocument.activeElement
}
if (elem && elem !== elem.ownerDocument.body) {

View file

@ -104,7 +104,7 @@ export default function (app: App): void {
})
app.nodes.attachNodeCallback((node: Node): void => {
if (!hasTag(node, 'IMG')) {
if (!hasTag(node, 'img')) {
return
}
app.nodes.attachNodeListener(node, 'error', () => sendImgError(node))

View file

@ -8,10 +8,10 @@ const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', '
// TODO: take into consideration "contenteditable" attribute
type TextEditableElement = HTMLInputElement | HTMLTextAreaElement
function isTextEditable(node: any): node is TextEditableElement {
if (hasTag(node, 'TEXTAREA')) {
if (hasTag(node, 'textarea')) {
return true
}
if (!hasTag(node, 'INPUT')) {
if (!hasTag(node, 'input')) {
return false
}
@ -19,7 +19,7 @@ function isTextEditable(node: any): node is TextEditableElement {
}
function isCheckable(node: any): node is HTMLInputElement {
if (!hasTag(node, 'INPUT')) {
if (!hasTag(node, 'input')) {
return false
}
const type = node.type
@ -31,7 +31,7 @@ const labelElementFor: (element: TextEditableElement) => HTMLLabelElement | unde
? (node) => {
let p: Node | null = node
while ((p = p.parentNode) !== null) {
if (hasTag(p, 'LABEL')) {
if (hasTag(p, 'label')) {
return p
}
}
@ -43,7 +43,7 @@ const labelElementFor: (element: TextEditableElement) => HTMLLabelElement | unde
: (node) => {
let p: Node | null = node
while ((p = p.parentNode) !== null) {
if (hasTag(p, 'LABEL')) {
if (hasTag(p, 'label')) {
return p
}
}
@ -142,12 +142,8 @@ export default function (app: App, opts: Partial<Options>): void {
app.ticker.attach((): void => {
inputValues.forEach((value, id) => {
const node = app.nodes.getNode(id)
if (!node) return
if (!isTextEditable(node)) {
inputValues.delete(id)
return
}
const node = app.nodes.getNode(id) as HTMLInputElement
if (!node) return inputValues.delete(id)
if (value !== node.value) {
inputValues.set(id, node.value)
if (!registeredTargets.has(id)) {
@ -158,12 +154,8 @@ export default function (app: App, opts: Partial<Options>): void {
}
})
checkableValues.forEach((checked, id) => {
const node = app.nodes.getNode(id)
if (!node) return
if (!isCheckable(node)) {
checkableValues.delete(id)
return
}
const node = app.nodes.getNode(id) as HTMLInputElement
if (!node) return checkableValues.delete(id)
if (checked !== node.checked) {
checkableValues.set(id, node.checked)
app.send(SetInputChecked(id, node.checked))
@ -179,7 +171,7 @@ export default function (app: App, opts: Partial<Options>): void {
return
}
// TODO: support multiple select (?): use selectedOptions; Need send target?
if (hasTag(node, 'SELECT')) {
if (hasTag(node, 'select')) {
sendInputValue(id, node)
app.attachEventListener(node, 'change', () => {
sendInputValue(id, node)

View file

@ -86,7 +86,7 @@ export default function (app: App): void {
if (dl !== null) {
return dl
}
if (hasTag(target, 'INPUT')) {
if (hasTag(target, 'input')) {
return getInputLabel(target)
}
if (isClickable(target)) {

View file

@ -21,7 +21,7 @@ function getPaintBlocks(resources: ResourcesTimeMap): Array<PaintBlock> {
for (let i = 0; i < elements.length; i++) {
const element = elements[i]
let src = ''
if (hasTag(element, 'IMG')) {
if (hasTag(element, 'img')) {
src = element.currentSrc || element.src
}
if (!src) {

View file

@ -57,6 +57,7 @@ export default class QueueSender {
private sendBatch(batch: Uint8Array): void {
this.busy = true
// @ts-ignore
fetch(this.ingestURL, {
body: batch,
method: 'POST',
@ -66,7 +67,7 @@ export default class QueueSender {
},
keepalive: batch.length < KEEPALIVE_SIZE_LIMIT,
})
.then((r) => {
.then((r: Record<string, any>) => {
if (r.status === 401) {
// TODO: continuous session ?
this.busy = false
@ -81,7 +82,7 @@ export default class QueueSender {
this.attemptsCount = 0
this.sendNext()
})
.catch((e) => {
.catch((e: any) => {
console.warn('OpenReplay:', e)
this.retry(batch)
})

View file

@ -69,7 +69,8 @@ function initiateFailure(reason: string): void {
let sendIntervalID: ReturnType<typeof setInterval> | null = null
let restartTimeoutID: ReturnType<typeof setTimeout>
self.onmessage = ({ data }: MessageEvent<ToWorkerData>): any => {
// @ts-ignore
self.onmessage = ({ data }: any): any => {
if (data == null) {
finalize()
return