tango-app-ui-analyse-traffic 3.8.22 → 3.8.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,10 +3,17 @@ import * as i0 from "@angular/core";
3
3
  export class MagnifierDirective {
4
4
  el;
5
5
  renderer;
6
- lens;
6
+ lens = null;
7
+ zoomImg = null;
7
8
  zoom = 2; // Zoom factor
8
9
  lensWidth = 300; // lens width
9
10
  lensHeight = 700; // lens height (taller)
11
+ // Geometry is cached on enter / scroll / resize so each move does no layout
12
+ // read and no repaint — only two compositor transforms.
13
+ rect = null;
14
+ currentSrc = '';
15
+ rafId = null;
16
+ pendingEvent = null;
10
17
  constructor(el, renderer) {
11
18
  this.el = el;
12
19
  this.renderer = renderer;
@@ -16,60 +23,136 @@ export class MagnifierDirective {
16
23
  }
17
24
  createLens() {
18
25
  this.lens = this.renderer.createElement('div');
19
- this.renderer.setStyle(this.lens, 'position', 'absolute');
26
+ this.renderer.setStyle(this.lens, 'position', 'fixed');
27
+ this.renderer.setStyle(this.lens, 'left', '0');
28
+ this.renderer.setStyle(this.lens, 'top', '0');
20
29
  this.renderer.setStyle(this.lens, 'width', `${this.lensWidth}px`);
21
30
  this.renderer.setStyle(this.lens, 'height', `${this.lensHeight}px`);
22
31
  this.renderer.setStyle(this.lens, 'border', '2px solid red');
23
- this.renderer.setStyle(this.lens, 'background-color', 'rgba(255,255,255,0.5)');
32
+ this.renderer.setStyle(this.lens, 'overflow', 'hidden');
33
+ this.renderer.setStyle(this.lens, 'background-color', '#fff');
24
34
  this.renderer.setStyle(this.lens, 'pointer-events', 'none');
25
35
  this.renderer.setStyle(this.lens, 'z-index', '9999');
26
- this.renderer.setStyle(this.lens, 'transition', 'all 0.1s ease');
36
+ this.renderer.setStyle(this.lens, 'transition', 'opacity 0.12s ease');
37
+ this.renderer.setStyle(this.lens, 'opacity', '0');
27
38
  this.renderer.setStyle(this.lens, 'visibility', 'hidden');
39
+ this.renderer.setStyle(this.lens, 'will-change', 'transform');
40
+ // The zoomed image lives inside the lens. Moving IT with transform is a
41
+ // compositor-only operation — no background-position repaint per frame.
42
+ this.zoomImg = this.renderer.createElement('img');
43
+ this.renderer.setStyle(this.zoomImg, 'position', 'absolute');
44
+ this.renderer.setStyle(this.zoomImg, 'left', '0');
45
+ this.renderer.setStyle(this.zoomImg, 'top', '0');
46
+ this.renderer.setStyle(this.zoomImg, 'max-width', 'none');
47
+ this.renderer.setStyle(this.zoomImg, 'pointer-events', 'none');
48
+ this.renderer.setStyle(this.zoomImg, 'will-change', 'transform');
49
+ this.renderer.appendChild(this.lens, this.zoomImg);
28
50
  this.renderer.appendChild(document.body, this.lens);
29
51
  }
52
+ onMouseEnter(e) {
53
+ this.refreshGeometry();
54
+ this.renderer.setStyle(this.lens, 'visibility', 'visible');
55
+ this.renderer.setStyle(this.lens, 'opacity', '1');
56
+ this.scheduleUpdate(e);
57
+ }
30
58
  onMouseMove(e) {
59
+ this.scheduleUpdate(e);
60
+ }
61
+ // Coalesce rapid mouse moves into one update per animation frame.
62
+ scheduleUpdate(e) {
63
+ this.pendingEvent = e;
64
+ if (this.rafId !== null) {
65
+ return;
66
+ }
67
+ this.rafId = requestAnimationFrame(() => {
68
+ this.rafId = null;
69
+ if (this.pendingEvent) {
70
+ this.update(this.pendingEvent);
71
+ }
72
+ });
73
+ }
74
+ // The only place that reads layout / sets the heavy image source + size.
75
+ refreshGeometry() {
76
+ if (!this.lens || !this.zoomImg) {
77
+ return;
78
+ }
31
79
  const image = this.el.nativeElement;
32
- const rect = image.getBoundingClientRect();
33
- const scrollX = window.pageXOffset;
34
- const scrollY = window.pageYOffset;
35
- const x = e.pageX - rect.left - scrollX;
36
- const y = e.pageY - rect.top - scrollY;
37
- const lensX = e.pageX - this.lensWidth / 2;
38
- const lensY = e.pageY - this.lensHeight / 2;
39
- // Move lens
40
- this.renderer.setStyle(this.lens, 'left', `${lensX}px`);
41
- this.renderer.setStyle(this.lens, 'top', `${lensY}px`);
42
- // Set background image and zoom
43
- this.renderer.setStyle(this.lens, 'backgroundImage', `url(${image.src})`);
44
- this.renderer.setStyle(this.lens, 'backgroundSize', `${image.width * this.zoom}px ${image.height * this.zoom}px`);
45
- const bgX = x * this.zoom - this.lensWidth / 2;
46
- const bgY = y * this.zoom - this.lensHeight / 2;
47
- this.renderer.setStyle(this.lens, 'backgroundPosition', `-${bgX}px -${bgY}px`);
48
- this.renderer.setStyle(this.lens, 'visibility', 'visible');
80
+ this.rect = image.getBoundingClientRect();
81
+ if (image.src !== this.currentSrc) {
82
+ this.currentSrc = image.src;
83
+ this.renderer.setAttribute(this.zoomImg, 'src', image.src);
84
+ }
85
+ this.renderer.setStyle(this.zoomImg, 'width', `${this.rect.width * this.zoom}px`);
86
+ this.renderer.setStyle(this.zoomImg, 'height', `${this.rect.height * this.zoom}px`);
87
+ }
88
+ update(e) {
89
+ if (!this.lens || !this.zoomImg || !this.rect) {
90
+ return;
91
+ }
92
+ const rect = this.rect;
93
+ // Cursor position within the displayed image (clientX/Y pair with fixed lens).
94
+ let x = e.clientX - rect.left;
95
+ let y = e.clientY - rect.top;
96
+ // Clamp to the image so the lens never shows empty space at the edges.
97
+ x = Math.max(0, Math.min(x, rect.width));
98
+ y = Math.max(0, Math.min(y, rect.height));
99
+ // Lens follows the cursor (compositor transform — no layout).
100
+ const lensX = e.clientX - this.lensWidth / 2;
101
+ const lensY = e.clientY - this.lensHeight / 2;
102
+ this.renderer.setStyle(this.lens, 'transform', `translate(${lensX}px, ${lensY}px)`);
103
+ // Zoomed image pans within the lens (compositor transform — no repaint).
104
+ const imgX = this.lensWidth / 2 - x * this.zoom;
105
+ const imgY = this.lensHeight / 2 - y * this.zoom;
106
+ this.renderer.setStyle(this.zoomImg, 'transform', `translate(${imgX}px, ${imgY}px)`);
107
+ }
108
+ // Geometry can change while hovering; refresh without touching positions.
109
+ onViewportChange() {
110
+ if (this.lens && this.lens.style.visibility === 'visible') {
111
+ this.refreshGeometry();
112
+ }
49
113
  }
50
114
  onMouseLeave() {
115
+ if (this.rafId !== null) {
116
+ cancelAnimationFrame(this.rafId);
117
+ this.rafId = null;
118
+ }
119
+ this.pendingEvent = null;
120
+ this.renderer.setStyle(this.lens, 'opacity', '0');
51
121
  this.renderer.setStyle(this.lens, 'visibility', 'hidden');
52
122
  }
53
123
  ngOnDestroy() {
54
- // Cleanup lens element
124
+ if (this.rafId !== null) {
125
+ cancelAnimationFrame(this.rafId);
126
+ this.rafId = null;
127
+ }
55
128
  if (this.lens && this.lens.parentNode) {
56
129
  this.renderer.removeChild(document.body, this.lens);
57
130
  this.lens = null;
131
+ this.zoomImg = null;
58
132
  }
59
133
  }
60
134
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MagnifierDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
61
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: MagnifierDirective, selector: "[appMagnifier]", host: { listeners: { "mousemove": "onMouseMove($event)", "mouseleave": "onMouseLeave()" } }, ngImport: i0 });
135
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: MagnifierDirective, selector: "[appMagnifier]", host: { listeners: { "mouseenter": "onMouseEnter($event)", "mousemove": "onMouseMove($event)", "window:scroll": "onViewportChange()", "window:resize": "onViewportChange()", "mouseleave": "onMouseLeave()" } }, ngImport: i0 });
62
136
  }
63
137
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MagnifierDirective, decorators: [{
64
138
  type: Directive,
65
139
  args: [{
66
140
  selector: '[appMagnifier]',
67
141
  }]
68
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { onMouseMove: [{
142
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { onMouseEnter: [{
143
+ type: HostListener,
144
+ args: ['mouseenter', ['$event']]
145
+ }], onMouseMove: [{
69
146
  type: HostListener,
70
147
  args: ['mousemove', ['$event']]
148
+ }], onViewportChange: [{
149
+ type: HostListener,
150
+ args: ['window:scroll']
151
+ }, {
152
+ type: HostListener,
153
+ args: ['window:resize']
71
154
  }], onMouseLeave: [{
72
155
  type: HostListener,
73
156
  args: ['mouseleave']
74
157
  }] } });
75
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFnbmlmaWVyLmRpcmVjdGl2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RhbmdvLWFuYWx5c2UtdHJhZmZpYy9zcmMvbGliL2RpcmVjdGl2ZS9tYWduaWZpZXIuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQWMsWUFBWSxFQUFnQyxNQUFNLGVBQWUsQ0FBQzs7QUFLbEcsTUFBTSxPQUFPLGtCQUFrQjtJQU1UO0lBQXdCO0lBTHBDLElBQUksQ0FBcUI7SUFDekIsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFRLGNBQWM7SUFDL0IsU0FBUyxHQUFHLEdBQUcsQ0FBQyxDQUFHLGFBQWE7SUFDaEMsVUFBVSxHQUFHLEdBQUcsQ0FBQyxDQUFFLHVCQUF1QjtJQUVsRCxZQUFvQixFQUFjLEVBQVUsUUFBbUI7UUFBM0MsT0FBRSxHQUFGLEVBQUUsQ0FBWTtRQUFVLGFBQVEsR0FBUixRQUFRLENBQVc7SUFBRyxDQUFDO0lBRW5FLFFBQVE7UUFDTixJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDcEIsQ0FBQztJQUVPLFVBQVU7UUFDaEIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUUvQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUMxRCxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxHQUFHLElBQUksQ0FBQyxTQUFTLElBQUksQ0FBQyxDQUFDO1FBQ2xFLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLENBQUM7UUFDcEUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxRQUFRLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDN0QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxrQkFBa0IsRUFBRSx1QkFBdUIsQ0FBQyxDQUFDO1FBQy9FLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDNUQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDckQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxZQUFZLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxZQUFZLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFFMUQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUdELFdBQVcsQ0FBQyxDQUFhO1FBQ3ZCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDO1FBQ3BDLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBRTNDLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUM7UUFDbkMsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FBQztRQUVuQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDO1FBQ3hDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsR0FBRyxPQUFPLENBQUM7UUFFdkMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsU0FBUyxHQUFHLENBQUMsQ0FBQztRQUMzQyxNQUFNLEtBQUssR0FBRyxDQUFDLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxVQUFVLEdBQUcsQ0FBQyxDQUFDO1FBRTVDLFlBQVk7UUFDWixJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLE1BQU0sRUFBRSxHQUFHLEtBQUssSUFBSSxDQUFDLENBQUM7UUFDeEQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxLQUFLLElBQUksQ0FBQyxDQUFDO1FBRXZELGdDQUFnQztRQUNoQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLGlCQUFpQixFQUFFLE9BQU8sS0FBSyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUM7UUFDMUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQ3BCLElBQUksQ0FBQyxJQUFJLEVBQ1QsZ0JBQWdCLEVBQ2hCLEdBQUcsS0FBSyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsSUFBSSxNQUFNLEtBQUssQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksSUFBSSxDQUM3RCxDQUFDO1FBRUYsTUFBTSxHQUFHLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUM7UUFDL0MsTUFBTSxHQUFHLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUM7UUFFaEQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxvQkFBb0IsRUFBRSxJQUFJLEdBQUcsT0FBTyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQy9FLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsWUFBWSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFHRCxZQUFZO1FBQ1YsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxZQUFZLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDNUQsQ0FBQztJQUVELFdBQVc7UUFDVCx1QkFBdUI7UUFDdkIsSUFBSSxJQUFJLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFO1lBQ3JDLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3BELElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1NBQ2xCO0lBQ0gsQ0FBQzt3R0F4RVUsa0JBQWtCOzRGQUFsQixrQkFBa0I7OzRGQUFsQixrQkFBa0I7a0JBSDlCLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLGdCQUFnQjtpQkFDM0I7dUdBOEJDLFdBQVc7c0JBRFYsWUFBWTt1QkFBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUM7Z0JBa0NyQyxZQUFZO3NCQURYLFlBQVk7dUJBQUMsWUFBWSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IERpcmVjdGl2ZSwgRWxlbWVudFJlZiwgSG9zdExpc3RlbmVyLCBSZW5kZXJlcjIsIE9uSW5pdCwgT25EZXN0cm95IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcblxyXG5ARGlyZWN0aXZlKHtcclxuICBzZWxlY3RvcjogJ1thcHBNYWduaWZpZXJdJyxcclxufSlcclxuZXhwb3J0IGNsYXNzIE1hZ25pZmllckRpcmVjdGl2ZSBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcclxuICBwcml2YXRlIGxlbnM6IEhUTUxFbGVtZW50IHwgbnVsbDtcclxuICBwcml2YXRlIHpvb20gPSAyOyAgICAgICAgLy8gWm9vbSBmYWN0b3JcclxuICBwcml2YXRlIGxlbnNXaWR0aCA9IDMwMDsgICAvLyBsZW5zIHdpZHRoXHJcbiAgcHJpdmF0ZSBsZW5zSGVpZ2h0ID0gNzAwOyAgLy8gbGVucyBoZWlnaHQgKHRhbGxlcilcclxuXHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBlbDogRWxlbWVudFJlZiwgcHJpdmF0ZSByZW5kZXJlcjogUmVuZGVyZXIyKSB7fVxyXG5cclxuICBuZ09uSW5pdCgpIHtcclxuICAgIHRoaXMuY3JlYXRlTGVucygpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBjcmVhdGVMZW5zKCkge1xyXG4gICAgdGhpcy5sZW5zID0gdGhpcy5yZW5kZXJlci5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHJcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ3Bvc2l0aW9uJywgJ2Fic29sdXRlJyk7XHJcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ3dpZHRoJywgYCR7dGhpcy5sZW5zV2lkdGh9cHhgKTtcclxuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAnaGVpZ2h0JywgYCR7dGhpcy5sZW5zSGVpZ2h0fXB4YCk7XHJcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ2JvcmRlcicsICcycHggc29saWQgcmVkJyk7XHJcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ2JhY2tncm91bmQtY29sb3InLCAncmdiYSgyNTUsMjU1LDI1NSwwLjUpJyk7XHJcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ3BvaW50ZXItZXZlbnRzJywgJ25vbmUnKTtcclxuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAnei1pbmRleCcsICc5OTk5Jyk7XHJcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ3RyYW5zaXRpb24nLCAnYWxsIDAuMXMgZWFzZScpO1xyXG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmxlbnMsICd2aXNpYmlsaXR5JywgJ2hpZGRlbicpO1xyXG5cclxuICAgIHRoaXMucmVuZGVyZXIuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuYm9keSwgdGhpcy5sZW5zKTtcclxuICB9XHJcblxyXG4gIEBIb3N0TGlzdGVuZXIoJ21vdXNlbW92ZScsIFsnJGV2ZW50J10pXHJcbiAgb25Nb3VzZU1vdmUoZTogTW91c2VFdmVudCk6IHZvaWQge1xyXG4gICAgY29uc3QgaW1hZ2UgPSB0aGlzLmVsLm5hdGl2ZUVsZW1lbnQ7XHJcbiAgICBjb25zdCByZWN0ID0gaW1hZ2UuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcblxyXG4gICAgY29uc3Qgc2Nyb2xsWCA9IHdpbmRvdy5wYWdlWE9mZnNldDtcclxuICAgIGNvbnN0IHNjcm9sbFkgPSB3aW5kb3cucGFnZVlPZmZzZXQ7XHJcblxyXG4gICAgY29uc3QgeCA9IGUucGFnZVggLSByZWN0LmxlZnQgLSBzY3JvbGxYO1xyXG4gICAgY29uc3QgeSA9IGUucGFnZVkgLSByZWN0LnRvcCAtIHNjcm9sbFk7XHJcblxyXG4gICAgY29uc3QgbGVuc1ggPSBlLnBhZ2VYIC0gdGhpcy5sZW5zV2lkdGggLyAyO1xyXG4gICAgY29uc3QgbGVuc1kgPSBlLnBhZ2VZIC0gdGhpcy5sZW5zSGVpZ2h0IC8gMjtcclxuXHJcbiAgICAvLyBNb3ZlIGxlbnNcclxuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAnbGVmdCcsIGAke2xlbnNYfXB4YCk7XHJcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ3RvcCcsIGAke2xlbnNZfXB4YCk7XHJcblxyXG4gICAgLy8gU2V0IGJhY2tncm91bmQgaW1hZ2UgYW5kIHpvb21cclxuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAnYmFja2dyb3VuZEltYWdlJywgYHVybCgke2ltYWdlLnNyY30pYCk7XHJcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKFxyXG4gICAgICB0aGlzLmxlbnMsXHJcbiAgICAgICdiYWNrZ3JvdW5kU2l6ZScsXHJcbiAgICAgIGAke2ltYWdlLndpZHRoICogdGhpcy56b29tfXB4ICR7aW1hZ2UuaGVpZ2h0ICogdGhpcy56b29tfXB4YFxyXG4gICAgKTtcclxuXHJcbiAgICBjb25zdCBiZ1ggPSB4ICogdGhpcy56b29tIC0gdGhpcy5sZW5zV2lkdGggLyAyO1xyXG4gICAgY29uc3QgYmdZID0geSAqIHRoaXMuem9vbSAtIHRoaXMubGVuc0hlaWdodCAvIDI7XHJcblxyXG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmxlbnMsICdiYWNrZ3JvdW5kUG9zaXRpb24nLCBgLSR7YmdYfXB4IC0ke2JnWX1weGApO1xyXG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmxlbnMsICd2aXNpYmlsaXR5JywgJ3Zpc2libGUnKTtcclxuICB9XHJcblxyXG4gIEBIb3N0TGlzdGVuZXIoJ21vdXNlbGVhdmUnKVxyXG4gIG9uTW91c2VMZWF2ZSgpIHtcclxuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAndmlzaWJpbGl0eScsICdoaWRkZW4nKTtcclxuICB9XHJcblxyXG4gIG5nT25EZXN0cm95KCkge1xyXG4gICAgLy8gQ2xlYW51cCBsZW5zIGVsZW1lbnRcclxuICAgIGlmICh0aGlzLmxlbnMgJiYgdGhpcy5sZW5zLnBhcmVudE5vZGUpIHtcclxuICAgICAgdGhpcy5yZW5kZXJlci5yZW1vdmVDaGlsZChkb2N1bWVudC5ib2R5LCB0aGlzLmxlbnMpO1xyXG4gICAgICB0aGlzLmxlbnMgPSBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG4iXX0=
158
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFnbmlmaWVyLmRpcmVjdGl2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RhbmdvLWFuYWx5c2UtdHJhZmZpYy9zcmMvbGliL2RpcmVjdGl2ZS9tYWduaWZpZXIuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQWMsWUFBWSxFQUFnQyxNQUFNLGVBQWUsQ0FBQzs7QUFLbEcsTUFBTSxPQUFPLGtCQUFrQjtJQWNUO0lBQXdCO0lBYnBDLElBQUksR0FBdUIsSUFBSSxDQUFDO0lBQ2hDLE9BQU8sR0FBNEIsSUFBSSxDQUFDO0lBQ3hDLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBUSxjQUFjO0lBQy9CLFNBQVMsR0FBRyxHQUFHLENBQUMsQ0FBRyxhQUFhO0lBQ2hDLFVBQVUsR0FBRyxHQUFHLENBQUMsQ0FBRSx1QkFBdUI7SUFFbEQsNEVBQTRFO0lBQzVFLHdEQUF3RDtJQUNoRCxJQUFJLEdBQW1CLElBQUksQ0FBQztJQUM1QixVQUFVLEdBQUcsRUFBRSxDQUFDO0lBQ2hCLEtBQUssR0FBa0IsSUFBSSxDQUFDO0lBQzVCLFlBQVksR0FBc0IsSUFBSSxDQUFDO0lBRS9DLFlBQW9CLEVBQWMsRUFBVSxRQUFtQjtRQUEzQyxPQUFFLEdBQUYsRUFBRSxDQUFZO1FBQVUsYUFBUSxHQUFSLFFBQVEsQ0FBVztJQUFHLENBQUM7SUFFbkUsUUFBUTtRQUNOLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUNwQixDQUFDO0lBRU8sVUFBVTtRQUNoQixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQy9DLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3ZELElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQy9DLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzlDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLENBQUM7UUFDbEUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxRQUFRLEVBQUUsR0FBRyxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUMsQ0FBQztRQUNwRSxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFFBQVEsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUM3RCxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUN4RCxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLGtCQUFrQixFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQzlELElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDNUQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDckQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxZQUFZLEVBQUUsb0JBQW9CLENBQUMsQ0FBQztRQUN0RSxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxHQUFHLENB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ImltcG9ydCB7IERpcmVjdGl2ZSwgRWxlbWVudFJlZiwgSG9zdExpc3RlbmVyLCBSZW5kZXJlcjIsIE9uSW5pdCwgT25EZXN0cm95IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBEaXJlY3RpdmUoe1xuICBzZWxlY3RvcjogJ1thcHBNYWduaWZpZXJdJyxcbn0pXG5leHBvcnQgY2xhc3MgTWFnbmlmaWVyRGlyZWN0aXZlIGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuICBwcml2YXRlIGxlbnM6IEhUTUxFbGVtZW50IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgem9vbUltZzogSFRNTEltYWdlRWxlbWVudCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHpvb20gPSAyOyAgICAgICAgLy8gWm9vbSBmYWN0b3JcbiAgcHJpdmF0ZSBsZW5zV2lkdGggPSAzMDA7ICAgLy8gbGVucyB3aWR0aFxuICBwcml2YXRlIGxlbnNIZWlnaHQgPSA3MDA7ICAvLyBsZW5zIGhlaWdodCAodGFsbGVyKVxuXG4gIC8vIEdlb21ldHJ5IGlzIGNhY2hlZCBvbiBlbnRlciAvIHNjcm9sbCAvIHJlc2l6ZSBzbyBlYWNoIG1vdmUgZG9lcyBubyBsYXlvdXRcbiAgLy8gcmVhZCBhbmQgbm8gcmVwYWludCDigJQgb25seSB0d28gY29tcG9zaXRvciB0cmFuc2Zvcm1zLlxuICBwcml2YXRlIHJlY3Q6IERPTVJlY3QgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjdXJyZW50U3JjID0gJyc7XG4gIHByaXZhdGUgcmFmSWQ6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHBlbmRpbmdFdmVudDogTW91c2VFdmVudCB8IG51bGwgPSBudWxsO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgZWw6IEVsZW1lbnRSZWYsIHByaXZhdGUgcmVuZGVyZXI6IFJlbmRlcmVyMikge31cblxuICBuZ09uSW5pdCgpIHtcbiAgICB0aGlzLmNyZWF0ZUxlbnMoKTtcbiAgfVxuXG4gIHByaXZhdGUgY3JlYXRlTGVucygpIHtcbiAgICB0aGlzLmxlbnMgPSB0aGlzLnJlbmRlcmVyLmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAncG9zaXRpb24nLCAnZml4ZWQnKTtcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ2xlZnQnLCAnMCcpO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAndG9wJywgJzAnKTtcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ3dpZHRoJywgYCR7dGhpcy5sZW5zV2lkdGh9cHhgKTtcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ2hlaWdodCcsIGAke3RoaXMubGVuc0hlaWdodH1weGApO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAnYm9yZGVyJywgJzJweCBzb2xpZCByZWQnKTtcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ292ZXJmbG93JywgJ2hpZGRlbicpO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAnYmFja2dyb3VuZC1jb2xvcicsICcjZmZmJyk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmxlbnMsICdwb2ludGVyLWV2ZW50cycsICdub25lJyk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmxlbnMsICd6LWluZGV4JywgJzk5OTknKTtcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ3RyYW5zaXRpb24nLCAnb3BhY2l0eSAwLjEycyBlYXNlJyk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmxlbnMsICdvcGFjaXR5JywgJzAnKTtcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMubGVucywgJ3Zpc2liaWxpdHknLCAnaGlkZGVuJyk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmxlbnMsICd3aWxsLWNoYW5nZScsICd0cmFuc2Zvcm0nKTtcblxuICAgIC8vIFRoZSB6b29tZWQgaW1hZ2UgbGl2ZXMgaW5zaWRlIHRoZSBsZW5zLiBNb3ZpbmcgSVQgd2l0aCB0cmFuc2Zvcm0gaXMgYVxuICAgIC8vIGNvbXBvc2l0b3Itb25seSBvcGVyYXRpb24g4oCUIG5vIGJhY2tncm91bmQtcG9zaXRpb24gcmVwYWludCBwZXIgZnJhbWUuXG4gICAgdGhpcy56b29tSW1nID0gdGhpcy5yZW5kZXJlci5jcmVhdGVFbGVtZW50KCdpbWcnKTtcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMuem9vbUltZywgJ3Bvc2l0aW9uJywgJ2Fic29sdXRlJyk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLnpvb21JbWcsICdsZWZ0JywgJzAnKTtcbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMuem9vbUltZywgJ3RvcCcsICcwJyk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLnpvb21JbWcsICdtYXgtd2lkdGgnLCAnbm9uZScpO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy56b29tSW1nLCAncG9pbnRlci1ldmVudHMnLCAnbm9uZScpO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy56b29tSW1nLCAnd2lsbC1jaGFuZ2UnLCAndHJhbnNmb3JtJyk7XG4gICAgdGhpcy5yZW5kZXJlci5hcHBlbmRDaGlsZCh0aGlzLmxlbnMsIHRoaXMuem9vbUltZyk7XG5cbiAgICB0aGlzLnJlbmRlcmVyLmFwcGVuZENoaWxkKGRvY3VtZW50LmJvZHksIHRoaXMubGVucyk7XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdtb3VzZWVudGVyJywgWyckZXZlbnQnXSlcbiAgb25Nb3VzZUVudGVyKGU6IE1vdXNlRXZlbnQpOiB2b2lkIHtcbiAgICB0aGlzLnJlZnJlc2hHZW9tZXRyeSgpO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAndmlzaWJpbGl0eScsICd2aXNpYmxlJyk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmxlbnMsICdvcGFjaXR5JywgJzEnKTtcbiAgICB0aGlzLnNjaGVkdWxlVXBkYXRlKGUpO1xuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignbW91c2Vtb3ZlJywgWyckZXZlbnQnXSlcbiAgb25Nb3VzZU1vdmUoZTogTW91c2VFdmVudCk6IHZvaWQge1xuICAgIHRoaXMuc2NoZWR1bGVVcGRhdGUoZSk7XG4gIH1cblxuICAvLyBDb2FsZXNjZSByYXBpZCBtb3VzZSBtb3ZlcyBpbnRvIG9uZSB1cGRhdGUgcGVyIGFuaW1hdGlvbiBmcmFtZS5cbiAgcHJpdmF0ZSBzY2hlZHVsZVVwZGF0ZShlOiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgdGhpcy5wZW5kaW5nRXZlbnQgPSBlO1xuICAgIGlmICh0aGlzLnJhZklkICE9PSBudWxsKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMucmFmSWQgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4ge1xuICAgICAgdGhpcy5yYWZJZCA9IG51bGw7XG4gICAgICBpZiAodGhpcy5wZW5kaW5nRXZlbnQpIHtcbiAgICAgICAgdGhpcy51cGRhdGUodGhpcy5wZW5kaW5nRXZlbnQpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLy8gVGhlIG9ubHkgcGxhY2UgdGhhdCByZWFkcyBsYXlvdXQgLyBzZXRzIHRoZSBoZWF2eSBpbWFnZSBzb3VyY2UgKyBzaXplLlxuICBwcml2YXRlIHJlZnJlc2hHZW9tZXRyeSgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMubGVucyB8fCAhdGhpcy56b29tSW1nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGltYWdlID0gdGhpcy5lbC5uYXRpdmVFbGVtZW50IGFzIEhUTUxJbWFnZUVsZW1lbnQ7XG4gICAgdGhpcy5yZWN0ID0gaW1hZ2UuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgaWYgKGltYWdlLnNyYyAhPT0gdGhpcy5jdXJyZW50U3JjKSB7XG4gICAgICB0aGlzLmN1cnJlbnRTcmMgPSBpbWFnZS5zcmM7XG4gICAgICB0aGlzLnJlbmRlcmVyLnNldEF0dHJpYnV0ZSh0aGlzLnpvb21JbWcsICdzcmMnLCBpbWFnZS5zcmMpO1xuICAgIH1cbiAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMuem9vbUltZywgJ3dpZHRoJywgYCR7dGhpcy5yZWN0LndpZHRoICogdGhpcy56b29tfXB4YCk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLnpvb21JbWcsICdoZWlnaHQnLCBgJHt0aGlzLnJlY3QuaGVpZ2h0ICogdGhpcy56b29tfXB4YCk7XG4gIH1cblxuICBwcml2YXRlIHVwZGF0ZShlOiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmxlbnMgfHwgIXRoaXMuem9vbUltZyB8fCAhdGhpcy5yZWN0KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHJlY3QgPSB0aGlzLnJlY3Q7XG5cbiAgICAvLyBDdXJzb3IgcG9zaXRpb24gd2l0aGluIHRoZSBkaXNwbGF5ZWQgaW1hZ2UgKGNsaWVudFgvWSBwYWlyIHdpdGggZml4ZWQgbGVucykuXG4gICAgbGV0IHggPSBlLmNsaWVudFggLSByZWN0LmxlZnQ7XG4gICAgbGV0IHkgPSBlLmNsaWVudFkgLSByZWN0LnRvcDtcbiAgICAvLyBDbGFtcCB0byB0aGUgaW1hZ2Ugc28gdGhlIGxlbnMgbmV2ZXIgc2hvd3MgZW1wdHkgc3BhY2UgYXQgdGhlIGVkZ2VzLlxuICAgIHggPSBNYXRoLm1heCgwLCBNYXRoLm1pbih4LCByZWN0LndpZHRoKSk7XG4gICAgeSA9IE1hdGgubWF4KDAsIE1hdGgubWluKHksIHJlY3QuaGVpZ2h0KSk7XG5cbiAgICAvLyBMZW5zIGZvbGxvd3MgdGhlIGN1cnNvciAoY29tcG9zaXRvciB0cmFuc2Zvcm0g4oCUIG5vIGxheW91dCkuXG4gICAgY29uc3QgbGVuc1ggPSBlLmNsaWVudFggLSB0aGlzLmxlbnNXaWR0aCAvIDI7XG4gICAgY29uc3QgbGVuc1kgPSBlLmNsaWVudFkgLSB0aGlzLmxlbnNIZWlnaHQgLyAyO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAndHJhbnNmb3JtJywgYHRyYW5zbGF0ZSgke2xlbnNYfXB4LCAke2xlbnNZfXB4KWApO1xuXG4gICAgLy8gWm9vbWVkIGltYWdlIHBhbnMgd2l0aGluIHRoZSBsZW5zIChjb21wb3NpdG9yIHRyYW5zZm9ybSDigJQgbm8gcmVwYWludCkuXG4gICAgY29uc3QgaW1nWCA9IHRoaXMubGVuc1dpZHRoIC8gMiAtIHggKiB0aGlzLnpvb207XG4gICAgY29uc3QgaW1nWSA9IHRoaXMubGVuc0hlaWdodCAvIDIgLSB5ICogdGhpcy56b29tO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy56b29tSW1nLCAndHJhbnNmb3JtJywgYHRyYW5zbGF0ZSgke2ltZ1h9cHgsICR7aW1nWX1weClgKTtcbiAgfVxuXG4gIC8vIEdlb21ldHJ5IGNhbiBjaGFuZ2Ugd2hpbGUgaG92ZXJpbmc7IHJlZnJlc2ggd2l0aG91dCB0b3VjaGluZyBwb3NpdGlvbnMuXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzpzY3JvbGwnKVxuICBASG9zdExpc3RlbmVyKCd3aW5kb3c6cmVzaXplJylcbiAgb25WaWV3cG9ydENoYW5nZSgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5sZW5zICYmIHRoaXMubGVucy5zdHlsZS52aXNpYmlsaXR5ID09PSAndmlzaWJsZScpIHtcbiAgICAgIHRoaXMucmVmcmVzaEdlb21ldHJ5KCk7XG4gICAgfVxuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignbW91c2VsZWF2ZScpXG4gIG9uTW91c2VMZWF2ZSgpIHtcbiAgICBpZiAodGhpcy5yYWZJZCAhPT0gbnVsbCkge1xuICAgICAgY2FuY2VsQW5pbWF0aW9uRnJhbWUodGhpcy5yYWZJZCk7XG4gICAgICB0aGlzLnJhZklkID0gbnVsbDtcbiAgICB9XG4gICAgdGhpcy5wZW5kaW5nRXZlbnQgPSBudWxsO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5sZW5zLCAnb3BhY2l0eScsICcwJyk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmxlbnMsICd2aXNpYmlsaXR5JywgJ2hpZGRlbicpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgaWYgKHRoaXMucmFmSWQgIT09IG51bGwpIHtcbiAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMucmFmSWQpO1xuICAgICAgdGhpcy5yYWZJZCA9IG51bGw7XG4gICAgfVxuICAgIGlmICh0aGlzLmxlbnMgJiYgdGhpcy5sZW5zLnBhcmVudE5vZGUpIHtcbiAgICAgIHRoaXMucmVuZGVyZXIucmVtb3ZlQ2hpbGQoZG9jdW1lbnQuYm9keSwgdGhpcy5sZW5zKTtcbiAgICAgIHRoaXMubGVucyA9IG51bGw7XG4gICAgICB0aGlzLnpvb21JbWcgPSBudWxsO1xuICAgIH1cbiAgfVxufVxuIl19
@@ -790,10 +790,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
790
790
  class MagnifierDirective {
791
791
  el;
792
792
  renderer;
793
- lens;
793
+ lens = null;
794
+ zoomImg = null;
794
795
  zoom = 2; // Zoom factor
795
796
  lensWidth = 300; // lens width
796
797
  lensHeight = 700; // lens height (taller)
798
+ // Geometry is cached on enter / scroll / resize so each move does no layout
799
+ // read and no repaint — only two compositor transforms.
800
+ rect = null;
801
+ currentSrc = '';
802
+ rafId = null;
803
+ pendingEvent = null;
797
804
  constructor(el, renderer) {
798
805
  this.el = el;
799
806
  this.renderer = renderer;
@@ -803,58 +810,134 @@ class MagnifierDirective {
803
810
  }
804
811
  createLens() {
805
812
  this.lens = this.renderer.createElement('div');
806
- this.renderer.setStyle(this.lens, 'position', 'absolute');
813
+ this.renderer.setStyle(this.lens, 'position', 'fixed');
814
+ this.renderer.setStyle(this.lens, 'left', '0');
815
+ this.renderer.setStyle(this.lens, 'top', '0');
807
816
  this.renderer.setStyle(this.lens, 'width', `${this.lensWidth}px`);
808
817
  this.renderer.setStyle(this.lens, 'height', `${this.lensHeight}px`);
809
818
  this.renderer.setStyle(this.lens, 'border', '2px solid red');
810
- this.renderer.setStyle(this.lens, 'background-color', 'rgba(255,255,255,0.5)');
819
+ this.renderer.setStyle(this.lens, 'overflow', 'hidden');
820
+ this.renderer.setStyle(this.lens, 'background-color', '#fff');
811
821
  this.renderer.setStyle(this.lens, 'pointer-events', 'none');
812
822
  this.renderer.setStyle(this.lens, 'z-index', '9999');
813
- this.renderer.setStyle(this.lens, 'transition', 'all 0.1s ease');
823
+ this.renderer.setStyle(this.lens, 'transition', 'opacity 0.12s ease');
824
+ this.renderer.setStyle(this.lens, 'opacity', '0');
814
825
  this.renderer.setStyle(this.lens, 'visibility', 'hidden');
826
+ this.renderer.setStyle(this.lens, 'will-change', 'transform');
827
+ // The zoomed image lives inside the lens. Moving IT with transform is a
828
+ // compositor-only operation — no background-position repaint per frame.
829
+ this.zoomImg = this.renderer.createElement('img');
830
+ this.renderer.setStyle(this.zoomImg, 'position', 'absolute');
831
+ this.renderer.setStyle(this.zoomImg, 'left', '0');
832
+ this.renderer.setStyle(this.zoomImg, 'top', '0');
833
+ this.renderer.setStyle(this.zoomImg, 'max-width', 'none');
834
+ this.renderer.setStyle(this.zoomImg, 'pointer-events', 'none');
835
+ this.renderer.setStyle(this.zoomImg, 'will-change', 'transform');
836
+ this.renderer.appendChild(this.lens, this.zoomImg);
815
837
  this.renderer.appendChild(document.body, this.lens);
816
838
  }
839
+ onMouseEnter(e) {
840
+ this.refreshGeometry();
841
+ this.renderer.setStyle(this.lens, 'visibility', 'visible');
842
+ this.renderer.setStyle(this.lens, 'opacity', '1');
843
+ this.scheduleUpdate(e);
844
+ }
817
845
  onMouseMove(e) {
846
+ this.scheduleUpdate(e);
847
+ }
848
+ // Coalesce rapid mouse moves into one update per animation frame.
849
+ scheduleUpdate(e) {
850
+ this.pendingEvent = e;
851
+ if (this.rafId !== null) {
852
+ return;
853
+ }
854
+ this.rafId = requestAnimationFrame(() => {
855
+ this.rafId = null;
856
+ if (this.pendingEvent) {
857
+ this.update(this.pendingEvent);
858
+ }
859
+ });
860
+ }
861
+ // The only place that reads layout / sets the heavy image source + size.
862
+ refreshGeometry() {
863
+ if (!this.lens || !this.zoomImg) {
864
+ return;
865
+ }
818
866
  const image = this.el.nativeElement;
819
- const rect = image.getBoundingClientRect();
820
- const scrollX = window.pageXOffset;
821
- const scrollY = window.pageYOffset;
822
- const x = e.pageX - rect.left - scrollX;
823
- const y = e.pageY - rect.top - scrollY;
824
- const lensX = e.pageX - this.lensWidth / 2;
825
- const lensY = e.pageY - this.lensHeight / 2;
826
- // Move lens
827
- this.renderer.setStyle(this.lens, 'left', `${lensX}px`);
828
- this.renderer.setStyle(this.lens, 'top', `${lensY}px`);
829
- // Set background image and zoom
830
- this.renderer.setStyle(this.lens, 'backgroundImage', `url(${image.src})`);
831
- this.renderer.setStyle(this.lens, 'backgroundSize', `${image.width * this.zoom}px ${image.height * this.zoom}px`);
832
- const bgX = x * this.zoom - this.lensWidth / 2;
833
- const bgY = y * this.zoom - this.lensHeight / 2;
834
- this.renderer.setStyle(this.lens, 'backgroundPosition', `-${bgX}px -${bgY}px`);
835
- this.renderer.setStyle(this.lens, 'visibility', 'visible');
867
+ this.rect = image.getBoundingClientRect();
868
+ if (image.src !== this.currentSrc) {
869
+ this.currentSrc = image.src;
870
+ this.renderer.setAttribute(this.zoomImg, 'src', image.src);
871
+ }
872
+ this.renderer.setStyle(this.zoomImg, 'width', `${this.rect.width * this.zoom}px`);
873
+ this.renderer.setStyle(this.zoomImg, 'height', `${this.rect.height * this.zoom}px`);
874
+ }
875
+ update(e) {
876
+ if (!this.lens || !this.zoomImg || !this.rect) {
877
+ return;
878
+ }
879
+ const rect = this.rect;
880
+ // Cursor position within the displayed image (clientX/Y pair with fixed lens).
881
+ let x = e.clientX - rect.left;
882
+ let y = e.clientY - rect.top;
883
+ // Clamp to the image so the lens never shows empty space at the edges.
884
+ x = Math.max(0, Math.min(x, rect.width));
885
+ y = Math.max(0, Math.min(y, rect.height));
886
+ // Lens follows the cursor (compositor transform — no layout).
887
+ const lensX = e.clientX - this.lensWidth / 2;
888
+ const lensY = e.clientY - this.lensHeight / 2;
889
+ this.renderer.setStyle(this.lens, 'transform', `translate(${lensX}px, ${lensY}px)`);
890
+ // Zoomed image pans within the lens (compositor transform — no repaint).
891
+ const imgX = this.lensWidth / 2 - x * this.zoom;
892
+ const imgY = this.lensHeight / 2 - y * this.zoom;
893
+ this.renderer.setStyle(this.zoomImg, 'transform', `translate(${imgX}px, ${imgY}px)`);
894
+ }
895
+ // Geometry can change while hovering; refresh without touching positions.
896
+ onViewportChange() {
897
+ if (this.lens && this.lens.style.visibility === 'visible') {
898
+ this.refreshGeometry();
899
+ }
836
900
  }
837
901
  onMouseLeave() {
902
+ if (this.rafId !== null) {
903
+ cancelAnimationFrame(this.rafId);
904
+ this.rafId = null;
905
+ }
906
+ this.pendingEvent = null;
907
+ this.renderer.setStyle(this.lens, 'opacity', '0');
838
908
  this.renderer.setStyle(this.lens, 'visibility', 'hidden');
839
909
  }
840
910
  ngOnDestroy() {
841
- // Cleanup lens element
911
+ if (this.rafId !== null) {
912
+ cancelAnimationFrame(this.rafId);
913
+ this.rafId = null;
914
+ }
842
915
  if (this.lens && this.lens.parentNode) {
843
916
  this.renderer.removeChild(document.body, this.lens);
844
917
  this.lens = null;
918
+ this.zoomImg = null;
845
919
  }
846
920
  }
847
921
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MagnifierDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
848
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: MagnifierDirective, selector: "[appMagnifier]", host: { listeners: { "mousemove": "onMouseMove($event)", "mouseleave": "onMouseLeave()" } }, ngImport: i0 });
922
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: MagnifierDirective, selector: "[appMagnifier]", host: { listeners: { "mouseenter": "onMouseEnter($event)", "mousemove": "onMouseMove($event)", "window:scroll": "onViewportChange()", "window:resize": "onViewportChange()", "mouseleave": "onMouseLeave()" } }, ngImport: i0 });
849
923
  }
850
924
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MagnifierDirective, decorators: [{
851
925
  type: Directive,
852
926
  args: [{
853
927
  selector: '[appMagnifier]',
854
928
  }]
855
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { onMouseMove: [{
929
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { onMouseEnter: [{
930
+ type: HostListener,
931
+ args: ['mouseenter', ['$event']]
932
+ }], onMouseMove: [{
856
933
  type: HostListener,
857
934
  args: ['mousemove', ['$event']]
935
+ }], onViewportChange: [{
936
+ type: HostListener,
937
+ args: ['window:scroll']
938
+ }, {
939
+ type: HostListener,
940
+ args: ['window:resize']
858
941
  }], onMouseLeave: [{
859
942
  type: HostListener,
860
943
  args: ['mouseleave']