diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2024-12-18 00:49:41 +0100 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2024-12-18 00:49:41 +0100 |
| commit | 8eed49067b82c7ec017ace069427185a2e135e41 (patch) | |
| tree | 122550d0c07538cb4c8a10d9baece4648cbf3008 /src/clicks_display.ts | |
| parent | 3b198d300fd634326dcab2557c8afb18adeba144 (diff) | |
Add display and fix many issues
Diffstat (limited to 'src/clicks_display.ts')
| -rw-r--r-- | src/clicks_display.ts | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/src/clicks_display.ts b/src/clicks_display.ts new file mode 100644 index 0000000..ff54705 --- /dev/null +++ b/src/clicks_display.ts @@ -0,0 +1,137 @@ +import h337 from "heatmap.js" + +namespace Clicks { + export type ClickEvent = { + x: number + y: number + tag: string + } + + export type UrlEvent = { + url: string + } + + export type Event = ClickEvent | UrlEvent + + export interface Format { + format: string + version: string + width: number + height: number + brands: string + events: Event[] + } +} + +class PageData { + readonly url: string + readonly data: h337.HeatmapData<h337.DataPoint> + + constructor(url: string, data: h337.DataPoint[]) { + this.url = url + let maxValue = 0 + for (const d of data) { + if (d.value > maxValue) maxValue = d.value + } + this.data = { + data: data, + min: 0, + max: maxValue, + } + } +} + +class Display { + private status: HTMLElement + private frame: HTMLIFrameElement + private heatmap: HTMLElement + private data: PageData[] | undefined + private map: h337.Heatmap<"value", "x", "y"> + + constructor() { + this.status = document.getElementById("status")!! + this.frame = document.getElementById("frame")!! as HTMLIFrameElement + this.heatmap = document.getElementById("heatmap")!! + this.map = h337.create({ + container: this.heatmap + }) + } + + loadFile(file: File) { + this.setStatus("Loading...") + const reader = new FileReader() + reader.addEventListener('load', () => { + const data = JSON.parse(reader.result as string) as Clicks.Format + if (data.format !== "clicks" || data.version !== "1.0") { + this.setStatus("Unknown format") + return + } + this.data = [] + const scaleX = this.frame.clientWidth + const scaleY = data.width + let url: string | undefined + let clicks: h337.DataPoint[] = [] + let map = new Map<string, number>() + for (const event of data.events) { + if ("url" in event) { + if (url !== undefined) { + console.log(clicks) + this.data.push(new PageData(url, clicks)) + } + url = event.url + clicks = [] + map.clear() + } else { + const ce = event as Clicks.ClickEvent + const x = Math.round(ce.x * scaleX) + const y = Math.round(ce.y * scaleY) + const pos = `${x}x${y}` + const index = map.get(pos) + if (index === undefined) { + map.set(pos, clicks!!.length) + clicks.push({ x: x, y: y, value: 1}) + } else { + clicks[index].value++ + } + } + } + if (url !== undefined) { + console.log(clicks) + this.data.push(new PageData(url, clicks)) + } + + this.setStatus("Loaded") + if (this.data.length > 0) + this.show(this.data[0]) + }) + reader.readAsText(file) + } + + private setStatus(newStatus: string) { + this.status.innerText = newStatus + } + + private show(data: PageData) { + this.frame.src = data.url + this.heatmap.style.transform = 'translateY(0px)' + this.frame.addEventListener('load', () => { + this.frame.contentDocument?.addEventListener('scroll', () => { + const y = this.frame.contentDocument!!.documentElement.scrollTop + this.heatmap.style.transform = `translateY(${-y}px)` + }) + }) + this.map.setData(data.data) + } +} + +document.addEventListener('DOMContentLoaded', () => { + const file = document.getElementById('file') + const display = new Display() + file?.addEventListener('change', (event: Event) => { + const files = (event.target as HTMLInputElement | null)!!.files + if (files === null) return + if (files.length > 0) { + display.loadFile(files[0]) + } + }) +})
\ No newline at end of file |
