Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 1 | // Copyright 2019 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Patrick Brosset | 5282f41 | 2020-11-13 16:32:09 | [diff] [blame] | 5 | export type PathCommands = Array<string|number>; |
| 6 | |
| 7 | export interface Quad { |
| 8 | p1: Position; |
| 9 | p2: Position; |
| 10 | p3: Position; |
| 11 | p4: Position; |
| 12 | } |
| 13 | |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 14 | export interface Position { |
| 15 | x: number; |
| 16 | y: number; |
| 17 | } |
| 18 | |
| 19 | export interface Bounds { |
| 20 | minX: number; |
| 21 | maxX: number; |
| 22 | minY: number; |
| 23 | maxY: number; |
| 24 | width?: number; |
| 25 | height?: number; |
| 26 | allPoints: Position[]; |
| 27 | } |
| 28 | |
| 29 | export interface AreaBounds { |
| 30 | name: string; |
| 31 | bounds: {allPoints: Position[]}; |
| 32 | } |
| 33 | |
| 34 | interface ViewportSize { |
| 35 | width: number; |
| 36 | height: number; |
| 37 | } |
| 38 | |
| 39 | export interface ResetData { |
| 40 | viewportSize: ViewportSize; |
Alex Rudenko | c30660f | 2021-09-13 11:45:30 | [diff] [blame] | 41 | viewportSizeForMediaQueries?: ViewportSize; |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 42 | deviceScaleFactor: number; |
| 43 | pageScaleFactor: number; |
| 44 | pageZoomFactor: number; |
| 45 | emulationScaleFactor: number; |
| 46 | scrollX: number; |
| 47 | scrollY: number; |
| 48 | } |
| 49 | |
| 50 | // Overlay class should be used to implement various tools and provide |
| 51 | // access to globals via the window object it receives in the constructor. |
| 52 | // Old logic is kept temporarily while the tools are being migrated. |
| 53 | export class Overlay { |
| 54 | protected viewportSize = {width: 800, height: 600}; |
Alex Rudenko | c30660f | 2021-09-13 11:45:30 | [diff] [blame] | 55 | protected viewportSizeForMediaQueries?: ViewportSize; |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 56 | protected deviceScaleFactor = 1; |
| 57 | protected emulationScaleFactor = 1; |
| 58 | protected pageScaleFactor = 1; |
| 59 | protected pageZoomFactor = 1; |
| 60 | protected scrollX = 0; |
| 61 | protected scrollY = 0; |
| 62 | protected style: CSSStyleSheet[]; |
| 63 | protected canvas?: HTMLCanvasElement; |
| 64 | protected canvasWidth: number = 0; |
| 65 | protected canvasHeight: number = 0; |
| 66 | protected platform?: string; |
Sigurd Schneider | 4b024d2 | 2021-02-15 13:27:03 | [diff] [blame] | 67 | // eslint-disable-next-line @typescript-eslint/naming-convention |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 68 | private _window?: Window; |
Sigurd Schneider | 4b024d2 | 2021-02-15 13:27:03 | [diff] [blame] | 69 | // eslint-disable-next-line @typescript-eslint/naming-convention |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 70 | private _document?: Document; |
Sigurd Schneider | 4b024d2 | 2021-02-15 13:27:03 | [diff] [blame] | 71 | // eslint-disable-next-line @typescript-eslint/naming-convention |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 72 | private _context?: CanvasRenderingContext2D|null; |
Sigurd Schneider | 4b024d2 | 2021-02-15 13:27:03 | [diff] [blame] | 73 | // eslint-disable-next-line @typescript-eslint/naming-convention |
Alex Rudenko | 6dface0 | 2020-10-09 07:26:44 | [diff] [blame] | 74 | private _installed = false; |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 75 | |
| 76 | constructor(window: Window, style: CSSStyleSheet[] = []) { |
| 77 | this._window = window; |
| 78 | this._document = window.document; |
| 79 | if (!Array.isArray(style)) { |
| 80 | style = [style]; |
| 81 | } |
| 82 | this.style = style; |
| 83 | } |
| 84 | |
| 85 | setCanvas(canvas: HTMLCanvasElement) { |
| 86 | this.canvas = canvas; |
| 87 | this._context = canvas.getContext('2d'); |
| 88 | } |
| 89 | |
Alex Rudenko | 6dface0 | 2020-10-09 07:26:44 | [diff] [blame] | 90 | install() { |
| 91 | for (const style of this.style) { |
| 92 | adoptStyleSheet(style); |
| 93 | } |
| 94 | this._installed = true; |
| 95 | } |
| 96 | |
| 97 | uninstall() { |
| 98 | for (const style of this.style) { |
| 99 | document.adoptedStyleSheets = document.adoptedStyleSheets.filter(s => s !== style); |
| 100 | } |
| 101 | this._installed = false; |
| 102 | } |
| 103 | |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 104 | reset(resetData?: ResetData) { |
| 105 | if (resetData) { |
| 106 | this.viewportSize = resetData.viewportSize; |
Alex Rudenko | c30660f | 2021-09-13 11:45:30 | [diff] [blame] | 107 | this.viewportSizeForMediaQueries = resetData.viewportSizeForMediaQueries; |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 108 | this.deviceScaleFactor = resetData.deviceScaleFactor; |
| 109 | this.pageScaleFactor = resetData.pageScaleFactor; |
| 110 | this.pageZoomFactor = resetData.pageZoomFactor; |
| 111 | this.emulationScaleFactor = resetData.emulationScaleFactor; |
| 112 | this.scrollX = Math.round(resetData.scrollX); |
| 113 | this.scrollY = Math.round(resetData.scrollY); |
| 114 | } |
| 115 | this.resetCanvas(); |
| 116 | } |
| 117 | |
| 118 | resetCanvas() { |
| 119 | if (!this.canvas || !this._context) { |
| 120 | return; |
| 121 | } |
| 122 | |
| 123 | this.canvas.width = this.deviceScaleFactor * this.viewportSize.width; |
| 124 | this.canvas.height = this.deviceScaleFactor * this.viewportSize.height; |
| 125 | this.canvas.style.width = this.viewportSize.width + 'px'; |
| 126 | this.canvas.style.height = this.viewportSize.height + 'px'; |
| 127 | |
| 128 | this._context.scale(this.deviceScaleFactor, this.deviceScaleFactor); |
| 129 | |
| 130 | this.canvasWidth = this.viewportSize.width; |
| 131 | this.canvasHeight = this.viewportSize.height; |
| 132 | } |
| 133 | |
| 134 | setPlatform(platform: string) { |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 135 | this.platform = platform; |
| 136 | this.document.body.classList.add('platform-' + platform); |
Alex Rudenko | 6dface0 | 2020-10-09 07:26:44 | [diff] [blame] | 137 | if (!this._installed) { |
| 138 | this.install(); |
| 139 | } |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | dispatch(message: unknown[]) { |
| 143 | const functionName = message.shift() as string; |
| 144 | // eslint-disable-next-line @typescript-eslint/no-explicit-any |
| 145 | (this as any)[functionName].apply(this, message); |
| 146 | } |
| 147 | |
| 148 | eventHasCtrlOrMeta(event: KeyboardEvent) { |
| 149 | return this.platform === 'mac' ? (event.metaKey && !event.ctrlKey) : (event.ctrlKey && !event.metaKey); |
| 150 | } |
| 151 | |
| 152 | get context(): CanvasRenderingContext2D { |
| 153 | if (!this._context) { |
| 154 | throw new Error('Context object is missing'); |
| 155 | } |
| 156 | return this._context; |
| 157 | } |
| 158 | |
| 159 | get document(): Document { |
| 160 | if (!this._document) { |
| 161 | throw new Error('Document object is missing'); |
| 162 | } |
| 163 | return this._document; |
| 164 | } |
| 165 | |
| 166 | get window(): Window { |
| 167 | if (!this._window) { |
| 168 | throw new Error('Window object is missing'); |
| 169 | } |
| 170 | return this._window; |
| 171 | } |
Alex Rudenko | 6dface0 | 2020-10-09 07:26:44 | [diff] [blame] | 172 | |
| 173 | get installed(): boolean { |
| 174 | return this._installed; |
| 175 | } |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 176 | } |
| 177 | |
| 178 | export function log(text: string) { |
| 179 | let element = document.getElementById('log'); |
| 180 | if (!element) { |
| 181 | element = createChild(document.body, 'div'); |
| 182 | element.id = 'log'; |
| 183 | } |
| 184 | createChild(element, 'div').textContent = text; |
| 185 | } |
| 186 | |
| 187 | export function createChild(parent: HTMLElement, tagName: string, className?: string): HTMLElement { |
| 188 | const element = createElement(tagName, className); |
| 189 | element.addEventListener('click', function(e: Event) { |
| 190 | e.stopPropagation(); |
| 191 | }, false); |
| 192 | parent.appendChild(element); |
| 193 | return element; |
| 194 | } |
| 195 | |
| 196 | export function createTextChild(parent: HTMLElement, text: string): Text { |
| 197 | const element = document.createTextNode(text); |
| 198 | parent.appendChild(element); |
| 199 | return element; |
| 200 | } |
| 201 | |
| 202 | export function createElement(tagName: string, className?: string) { |
| 203 | const element = document.createElement(tagName); |
| 204 | if (className) { |
| 205 | element.className = className; |
| 206 | } |
| 207 | return element; |
| 208 | } |
| 209 | |
| 210 | export function ellipsify(str: string, maxLength: number) { |
| 211 | if (str.length <= maxLength) { |
| 212 | return String(str); |
| 213 | } |
| 214 | return str.substr(0, maxLength - 1) + '\u2026'; |
| 215 | } |
| 216 | |
| 217 | export function constrainNumber(num: number, min: number, max: number): number { |
| 218 | if (num < min) { |
| 219 | num = min; |
| 220 | } else if (num > max) { |
| 221 | num = max; |
| 222 | } |
| 223 | return num; |
| 224 | } |
| 225 | |
| 226 | declare global { |
Tim van der Lippe | 75c2c9c | 2020-12-01 12:50:53 | [diff] [blame] | 227 | // eslint-disable-next-line @typescript-eslint/no-unused-vars |
Alex Rudenko | c294d47 | 2020-10-02 07:07:49 | [diff] [blame] | 228 | interface Document { |
| 229 | adoptedStyleSheets: CSSStyleSheet[]; |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | export function adoptStyleSheet(styleSheet: CSSStyleSheet) { |
| 234 | document.adoptedStyleSheets = [...document.adoptedStyleSheets, styleSheet]; |
| 235 | } |