change(tracker): change input node checks
This commit is contained in:
parent
7df87f1c9b
commit
126de51eb5
12 changed files with 39 additions and 45 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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? )
|
||||
|
|
|
|||
|
|
@ -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'))
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue