@flogeez/angular-tiptap-editor 2.3.0 → 2.4.1

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +132 -93
  3. package/esm2022/flogeez-angular-tiptap-editor.mjs +5 -0
  4. package/esm2022/lib/ate-editor.provider.mjs +38 -0
  5. package/esm2022/lib/components/bubble-menus/base/ate-base-bubble-menu.mjs +207 -0
  6. package/esm2022/lib/components/bubble-menus/base/ate-base-sub-bubble-menu.mjs +133 -0
  7. package/esm2022/lib/components/bubble-menus/color/ate-color-bubble-menu.component.mjs +365 -0
  8. package/esm2022/lib/components/bubble-menus/image/ate-image-bubble-menu.component.mjs +226 -0
  9. package/esm2022/lib/components/bubble-menus/link/ate-link-bubble-menu.component.mjs +247 -0
  10. package/esm2022/lib/components/bubble-menus/table/ate-cell-bubble-menu.component.mjs +121 -0
  11. package/esm2022/lib/components/bubble-menus/table/ate-table-bubble-menu.component.mjs +266 -0
  12. package/esm2022/lib/components/bubble-menus/text/ate-bubble-menu.component.mjs +294 -0
  13. package/esm2022/lib/components/color-picker/ate-color-picker.component.mjs +122 -0
  14. package/esm2022/lib/components/edit-toggle/ate-edit-toggle.component.mjs +42 -0
  15. package/esm2022/lib/components/editor/angular-tiptap-editor.component.mjs +953 -0
  16. package/esm2022/lib/components/slash-commands/ate-slash-commands.component.mjs +329 -0
  17. package/esm2022/lib/components/toolbar/ate-toolbar.component.mjs +531 -0
  18. package/esm2022/lib/components/ui/ate-button.component.mjs +90 -0
  19. package/esm2022/lib/components/ui/ate-separator.component.mjs +31 -0
  20. package/esm2022/lib/config/ate-editor.config.mjs +76 -0
  21. package/esm2022/lib/config/ate-global-config.token.mjs +6 -0
  22. package/esm2022/lib/config/ate-slash-commands.config.mjs +130 -0
  23. package/esm2022/lib/directives/ate-noop-value-accessor.directive.mjs +40 -0
  24. package/esm2022/lib/extensions/ate-link-click-behavior.extension.mjs +32 -0
  25. package/esm2022/lib/extensions/ate-resizable-image.extension.mjs +270 -0
  26. package/esm2022/lib/extensions/ate-table.extension.mjs +29 -0
  27. package/esm2022/lib/extensions/ate-tiptap-state.extension.mjs +85 -0
  28. package/esm2022/lib/extensions/ate-upload-progress.extension.mjs +195 -0
  29. package/esm2022/lib/extensions/calculators/ate-discovery.calculator.mjs +62 -0
  30. package/esm2022/lib/extensions/calculators/ate-image.calculator.mjs +8 -0
  31. package/esm2022/lib/extensions/calculators/ate-marks.calculator.mjs +75 -0
  32. package/esm2022/lib/extensions/calculators/ate-selection.calculator.mjs +35 -0
  33. package/esm2022/lib/extensions/calculators/ate-structure.calculator.mjs +34 -0
  34. package/esm2022/lib/extensions/calculators/ate-table.calculator.mjs +38 -0
  35. package/esm2022/lib/models/ate-bubble-menu.model.mjs +49 -0
  36. package/esm2022/lib/models/ate-editor-config.model.mjs +2 -0
  37. package/esm2022/lib/models/ate-editor-state.model.mjs +90 -0
  38. package/esm2022/lib/models/ate-image.model.mjs +2 -0
  39. package/esm2022/lib/models/ate-toolbar.model.mjs +35 -0
  40. package/esm2022/lib/node-view/ate-angular-node-view.mjs +97 -0
  41. package/esm2022/lib/node-view/ate-injector.registry.mjs +16 -0
  42. package/esm2022/lib/node-view/ate-node-view.factory.mjs +125 -0
  43. package/esm2022/lib/node-view/ate-node-view.models.mjs +2 -0
  44. package/esm2022/lib/node-view/ate-node-view.renderer.mjs +154 -0
  45. package/esm2022/lib/node-view/ate-register-angular-component.mjs +49 -0
  46. package/esm2022/lib/services/ate-color-picker.service.mjs +193 -0
  47. package/esm2022/lib/services/ate-editor-commands.service.mjs +465 -0
  48. package/esm2022/lib/services/ate-i18n.service.mjs +410 -0
  49. package/esm2022/lib/services/ate-image.service.mjs +368 -0
  50. package/esm2022/lib/services/ate-link.service.mjs +97 -0
  51. package/esm2022/lib/utils/ate-color.utils.mjs +60 -0
  52. package/esm2022/public-api.mjs +93 -0
  53. package/fesm2022/flogeez-angular-tiptap-editor.mjs +1178 -1100
  54. package/fesm2022/flogeez-angular-tiptap-editor.mjs.map +1 -1
  55. package/index.d.ts +3 -1562
  56. package/lib/ate-editor.provider.d.ts +21 -0
  57. package/lib/components/bubble-menus/base/ate-base-bubble-menu.d.ts +71 -0
  58. package/lib/components/bubble-menus/base/ate-base-sub-bubble-menu.d.ts +60 -0
  59. package/lib/components/bubble-menus/color/ate-color-bubble-menu.component.d.ts +77 -0
  60. package/lib/components/bubble-menus/image/ate-image-bubble-menu.component.d.ts +52 -0
  61. package/lib/components/bubble-menus/link/ate-link-bubble-menu.component.d.ts +55 -0
  62. package/lib/components/bubble-menus/table/ate-cell-bubble-menu.component.d.ts +12 -0
  63. package/lib/components/bubble-menus/table/ate-table-bubble-menu.component.d.ts +24 -0
  64. package/lib/components/bubble-menus/text/ate-bubble-menu.component.d.ts +53 -0
  65. package/lib/components/color-picker/ate-color-picker.component.d.ts +54 -0
  66. package/lib/components/edit-toggle/ate-edit-toggle.component.d.ts +13 -0
  67. package/lib/components/editor/angular-tiptap-editor.component.d.ts +235 -0
  68. package/lib/components/slash-commands/ate-slash-commands.component.d.ts +43 -0
  69. package/lib/components/toolbar/ate-toolbar.component.d.ts +47 -0
  70. package/lib/components/ui/ate-button.component.d.ts +25 -0
  71. package/lib/components/ui/ate-separator.component.d.ts +7 -0
  72. package/lib/config/ate-editor.config.d.ts +7 -0
  73. package/lib/config/ate-global-config.token.d.ts +6 -0
  74. package/lib/config/ate-slash-commands.config.d.ts +41 -0
  75. package/lib/directives/ate-noop-value-accessor.directive.d.ts +13 -0
  76. package/lib/extensions/ate-link-click-behavior.extension.d.ts +2 -0
  77. package/lib/extensions/ate-resizable-image.extension.d.ts +27 -0
  78. package/lib/extensions/ate-table.extension.d.ts +2 -0
  79. package/lib/extensions/ate-tiptap-state.extension.d.ts +7 -0
  80. package/lib/extensions/ate-upload-progress.extension.d.ts +7 -0
  81. package/lib/extensions/calculators/ate-discovery.calculator.d.ts +7 -0
  82. package/lib/extensions/calculators/ate-image.calculator.d.ts +2 -0
  83. package/lib/extensions/calculators/ate-marks.calculator.d.ts +2 -0
  84. package/lib/extensions/calculators/ate-selection.calculator.d.ts +2 -0
  85. package/lib/extensions/calculators/ate-structure.calculator.d.ts +2 -0
  86. package/lib/extensions/calculators/ate-table.calculator.d.ts +2 -0
  87. package/lib/models/ate-bubble-menu.model.d.ts +33 -0
  88. package/lib/models/ate-editor-config.model.d.ts +100 -0
  89. package/lib/models/ate-editor-state.model.d.ts +109 -0
  90. package/lib/models/ate-image.model.d.ts +104 -0
  91. package/lib/models/ate-toolbar.model.d.ts +18 -0
  92. package/lib/node-view/ate-angular-node-view.d.ts +115 -0
  93. package/lib/node-view/ate-injector.registry.d.ts +3 -0
  94. package/lib/node-view/ate-node-view.factory.d.ts +10 -0
  95. package/lib/node-view/ate-node-view.models.d.ts +138 -0
  96. package/lib/node-view/ate-node-view.renderer.d.ts +23 -0
  97. package/lib/node-view/ate-register-angular-component.d.ts +35 -0
  98. package/lib/services/ate-color-picker.service.d.ts +65 -0
  99. package/lib/services/ate-editor-commands.service.d.ts +72 -0
  100. package/lib/services/ate-i18n.service.d.ts +394 -0
  101. package/lib/services/ate-image.service.d.ts +71 -0
  102. package/lib/services/ate-link.service.d.ts +42 -0
  103. package/lib/utils/ate-color.utils.d.ts +12 -0
  104. package/package.json +3 -1
  105. package/public-api.d.ts +112 -0
@@ -0,0 +1,226 @@
1
+ import { Component, input, ChangeDetectionStrategy, computed, inject } from "@angular/core";
2
+ import { AteButtonComponent } from "../../ui/ate-button.component";
3
+ import { AteSeparatorComponent } from "../../ui/ate-separator.component";
4
+ import { AteImageService } from "../../../services/ate-image.service";
5
+ import { AteBaseBubbleMenu } from "../base/ate-base-bubble-menu";
6
+ import * as i0 from "@angular/core";
7
+ export class AteImageBubbleMenuComponent extends AteBaseBubbleMenu {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.t = this.i18nService.imageUpload;
11
+ this.imageService = inject(AteImageService);
12
+ this.config = input({
13
+ changeImage: true,
14
+ resizeSmall: true,
15
+ resizeMedium: true,
16
+ resizeLarge: true,
17
+ resizeOriginal: true,
18
+ deleteImage: true,
19
+ separator: true,
20
+ });
21
+ this.imageUpload = input({});
22
+ this.imageBubbleMenuConfig = computed(() => ({
23
+ changeImage: this.config().changeImage ?? true,
24
+ resizeSmall: this.config().resizeSmall ?? true,
25
+ resizeMedium: this.config().resizeMedium ?? true,
26
+ resizeLarge: this.config().resizeLarge ?? true,
27
+ resizeOriginal: this.config().resizeOriginal ?? true,
28
+ deleteImage: this.config().deleteImage ?? true,
29
+ separator: this.config().separator ?? true,
30
+ }));
31
+ this.hasResizeButtons = computed(() => {
32
+ const c = this.imageBubbleMenuConfig();
33
+ return c.resizeSmall || c.resizeMedium || c.resizeLarge || c.resizeOriginal;
34
+ });
35
+ }
36
+ shouldShow() {
37
+ const { nodes, isEditable, isFocused } = this.state();
38
+ if (this.editorCommands.linkEditMode() || this.editorCommands.colorEditMode()) {
39
+ return false;
40
+ }
41
+ return nodes.isImage && isEditable && isFocused;
42
+ }
43
+ getSelectionRect() {
44
+ const ed = this.editor();
45
+ if (!ed) {
46
+ return new DOMRect(0, 0, 0, 0);
47
+ }
48
+ const { from } = ed.state.selection;
49
+ try {
50
+ // 1. Direct ProseMirror approach: get DOM node at position
51
+ const dom = ed.view.nodeDOM(from);
52
+ if (dom instanceof HTMLElement) {
53
+ // If it's a resizable container, look for the image inside
54
+ if (dom.classList.contains("resizable-image-container")) {
55
+ const img = dom.querySelector("img");
56
+ if (img) {
57
+ return img.getBoundingClientRect();
58
+ }
59
+ }
60
+ return dom.getBoundingClientRect();
61
+ }
62
+ }
63
+ catch (_e) {
64
+ // Fallback if nodeDOM fails
65
+ }
66
+ // 2. Ultimate fallback: find selected image in DOM
67
+ const selectedImg = ed.view.dom.querySelector("img.selected, .resizable-image-container.selected img");
68
+ if (selectedImg) {
69
+ return selectedImg.getBoundingClientRect();
70
+ }
71
+ // If nothing found at all
72
+ return new DOMRect(-9999, -9999, 0, 0);
73
+ }
74
+ executeCommand(editor, command, ..._args) {
75
+ switch (command) {
76
+ case "changeImage":
77
+ this.changeImage();
78
+ break;
79
+ case "resizeSmall":
80
+ this.imageService.resizeImageToSmall(editor);
81
+ break;
82
+ case "resizeMedium":
83
+ this.imageService.resizeImageToMedium(editor);
84
+ break;
85
+ case "resizeLarge":
86
+ this.imageService.resizeImageToLarge(editor);
87
+ break;
88
+ case "resizeOriginal":
89
+ this.imageService.resizeImageToOriginal(editor);
90
+ break;
91
+ case "deleteImage":
92
+ this.deleteImage();
93
+ break;
94
+ }
95
+ }
96
+ async changeImage() {
97
+ const ed = this.editor();
98
+ if (!ed) {
99
+ return;
100
+ }
101
+ try {
102
+ // Use dedicated method to replace an existing image
103
+ await this.imageService.selectAndReplaceImage(ed, this.imageUpload());
104
+ }
105
+ catch (error) {
106
+ console.error(this.i18nService.imageUpload().uploadError, error);
107
+ }
108
+ }
109
+ deleteImage() {
110
+ const ed = this.editor();
111
+ if (ed) {
112
+ ed.chain().focus().deleteSelection().run();
113
+ }
114
+ }
115
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AteImageBubbleMenuComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
116
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AteImageBubbleMenuComponent, isStandalone: true, selector: "ate-image-bubble-menu", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: `
117
+ <div #menuRef class="bubble-menu" (mousedown)="$event.preventDefault()">
118
+ @if (imageBubbleMenuConfig().changeImage) {
119
+ <ate-button
120
+ icon="drive_file_rename_outline"
121
+ [title]="t().changeImage"
122
+ (buttonClick)="onCommand('changeImage', $event)"></ate-button>
123
+ }
124
+ @if (imageBubbleMenuConfig().separator && hasResizeButtons()) {
125
+ <ate-separator />
126
+ }
127
+ @if (imageBubbleMenuConfig().resizeSmall) {
128
+ <ate-button
129
+ icon="crop_square"
130
+ iconSize="small"
131
+ [title]="t().resizeSmall"
132
+ (buttonClick)="onCommand('resizeSmall', $event)"></ate-button>
133
+ }
134
+ @if (imageBubbleMenuConfig().resizeMedium) {
135
+ <ate-button
136
+ icon="crop_square"
137
+ iconSize="medium"
138
+ [title]="t().resizeMedium"
139
+ (buttonClick)="onCommand('resizeMedium', $event)"></ate-button>
140
+ }
141
+ @if (imageBubbleMenuConfig().resizeLarge) {
142
+ <ate-button
143
+ icon="crop_square"
144
+ iconSize="large"
145
+ [title]="t().resizeLarge"
146
+ (buttonClick)="onCommand('resizeLarge', $event)"></ate-button>
147
+ }
148
+ @if (imageBubbleMenuConfig().resizeOriginal) {
149
+ <ate-button
150
+ icon="photo_size_select_actual"
151
+ [title]="t().resizeOriginal"
152
+ (buttonClick)="onCommand('resizeOriginal', $event)"></ate-button>
153
+ }
154
+ @if (imageBubbleMenuConfig().separator && imageBubbleMenuConfig().deleteImage) {
155
+ <ate-separator />
156
+ }
157
+ @if (imageBubbleMenuConfig().deleteImage) {
158
+ <ate-button
159
+ icon="delete"
160
+ [title]="t().deleteImage"
161
+ variant="danger"
162
+ (buttonClick)="onCommand('deleteImage', $event)"></ate-button>
163
+ }
164
+ </div>
165
+ `, isInline: true, dependencies: [{ kind: "component", type: AteButtonComponent, selector: "ate-button", inputs: ["icon", "title", "active", "disabled", "color", "backgroundColor", "variant", "size", "iconSize"], outputs: ["buttonClick"] }, { kind: "component", type: AteSeparatorComponent, selector: "ate-separator", inputs: ["orientation", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
166
+ }
167
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AteImageBubbleMenuComponent, decorators: [{
168
+ type: Component,
169
+ args: [{
170
+ selector: "ate-image-bubble-menu",
171
+ standalone: true,
172
+ changeDetection: ChangeDetectionStrategy.OnPush,
173
+ imports: [AteButtonComponent, AteSeparatorComponent],
174
+ template: `
175
+ <div #menuRef class="bubble-menu" (mousedown)="$event.preventDefault()">
176
+ @if (imageBubbleMenuConfig().changeImage) {
177
+ <ate-button
178
+ icon="drive_file_rename_outline"
179
+ [title]="t().changeImage"
180
+ (buttonClick)="onCommand('changeImage', $event)"></ate-button>
181
+ }
182
+ @if (imageBubbleMenuConfig().separator && hasResizeButtons()) {
183
+ <ate-separator />
184
+ }
185
+ @if (imageBubbleMenuConfig().resizeSmall) {
186
+ <ate-button
187
+ icon="crop_square"
188
+ iconSize="small"
189
+ [title]="t().resizeSmall"
190
+ (buttonClick)="onCommand('resizeSmall', $event)"></ate-button>
191
+ }
192
+ @if (imageBubbleMenuConfig().resizeMedium) {
193
+ <ate-button
194
+ icon="crop_square"
195
+ iconSize="medium"
196
+ [title]="t().resizeMedium"
197
+ (buttonClick)="onCommand('resizeMedium', $event)"></ate-button>
198
+ }
199
+ @if (imageBubbleMenuConfig().resizeLarge) {
200
+ <ate-button
201
+ icon="crop_square"
202
+ iconSize="large"
203
+ [title]="t().resizeLarge"
204
+ (buttonClick)="onCommand('resizeLarge', $event)"></ate-button>
205
+ }
206
+ @if (imageBubbleMenuConfig().resizeOriginal) {
207
+ <ate-button
208
+ icon="photo_size_select_actual"
209
+ [title]="t().resizeOriginal"
210
+ (buttonClick)="onCommand('resizeOriginal', $event)"></ate-button>
211
+ }
212
+ @if (imageBubbleMenuConfig().separator && imageBubbleMenuConfig().deleteImage) {
213
+ <ate-separator />
214
+ }
215
+ @if (imageBubbleMenuConfig().deleteImage) {
216
+ <ate-button
217
+ icon="delete"
218
+ [title]="t().deleteImage"
219
+ variant="danger"
220
+ (buttonClick)="onCommand('deleteImage', $event)"></ate-button>
221
+ }
222
+ </div>
223
+ `,
224
+ }]
225
+ }] });
226
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXRlLWltYWdlLWJ1YmJsZS1tZW51LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2FuZ3VsYXItdGlwdGFwLWVkaXRvci9zcmMvbGliL2NvbXBvbmVudHMvYnViYmxlLW1lbnVzL2ltYWdlL2F0ZS1pbWFnZS1idWJibGUtbWVudS5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsdUJBQXVCLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUU1RixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUNuRSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUN6RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFFdEUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sOEJBQThCLENBQUM7O0FBMkRqRSxNQUFNLE9BQU8sMkJBQTRCLFNBQVEsaUJBQWlCO0lBeERsRTs7UUF5RFcsTUFBQyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDO1FBQ3pCLGlCQUFZLEdBQUcsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBRXhELFdBQU0sR0FBRyxLQUFLLENBQTJCO1lBQ3ZDLFdBQVcsRUFBRSxJQUFJO1lBQ2pCLFdBQVcsRUFBRSxJQUFJO1lBQ2pCLFlBQVksRUFBRSxJQUFJO1lBQ2xCLFdBQVcsRUFBRSxJQUFJO1lBQ2pCLGNBQWMsRUFBRSxJQUFJO1lBQ3BCLFdBQVcsRUFBRSxJQUFJO1lBQ2pCLFNBQVMsRUFBRSxJQUFJO1NBQ2hCLENBQUMsQ0FBQztRQUVILGdCQUFXLEdBQUcsS0FBSyxDQUF3QixFQUFFLENBQUMsQ0FBQztRQUUvQywwQkFBcUIsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENB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RUFBRSxDQUFDLFdBQVcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNuRSxDQUFDO0lBQ0gsQ0FBQztJQUVPLFdBQVc7UUFDakIsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ3pCLElBQUksRUFBRSxFQUFFLENBQUM7WUFDUCxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUMsZUFBZSxFQUFFLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDN0MsQ0FBQztJQUNILENBQUM7K0dBeEhVLDJCQUEyQjttR0FBM0IsMkJBQTJCLHlYQW5ENUI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpRFQsNERBbERTLGtCQUFrQiw2TEFBRSxxQkFBcUI7OzRGQW9EeEMsMkJBQTJCO2tCQXhEdkMsU0FBUzttQkFBQztvQkFDVCxRQUFRLEVBQUUsdUJBQXVCO29CQUNqQyxVQUFVLEVBQUUsSUFBSTtvQkFDaEIsZUFBZSxFQUFFLHVCQUF1QixDQUFDLE1BQU07b0JBQy9DLE9BQU8sRUFBRSxDQUFDLGtCQUFrQixFQUFFLHFCQUFxQixDQUFDO29CQUNwRCxRQUFRLEVBQUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpRFQ7aUJBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIGlucHV0LCBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgY29tcHV0ZWQsIGluamVjdCB9IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5pbXBvcnQgeyB0eXBlIEVkaXRvciB9IGZyb20gXCJAdGlwdGFwL2NvcmVcIjtcbmltcG9ydCB7IEF0ZUJ1dHRvbkNvbXBvbmVudCB9IGZyb20gXCIuLi8uLi91aS9hdGUtYnV0dG9uLmNvbXBvbmVudFwiO1xuaW1wb3J0IHsgQXRlU2VwYXJhdG9yQ29tcG9uZW50IH0gZnJvbSBcIi4uLy4uL3VpL2F0ZS1zZXBhcmF0b3IuY29tcG9uZW50XCI7XG5pbXBvcnQgeyBBdGVJbWFnZVNlcnZpY2UgfSBmcm9tIFwiLi4vLi4vLi4vc2VydmljZXMvYXRlLWltYWdlLnNlcnZpY2VcIjtcbmltcG9ydCB7IEF0ZUltYWdlQnViYmxlTWVudUNvbmZpZyB9IGZyb20gXCIuLi8uLi8uLi9tb2RlbHMvYXRlLWJ1YmJsZS1tZW51Lm1vZGVsXCI7XG5pbXBvcnQgeyBBdGVCYXNlQnViYmxlTWVudSB9IGZyb20gXCIuLi9iYXNlL2F0ZS1iYXNlLWJ1YmJsZS1tZW51XCI7XG5pbXBvcnQgeyBBdGVJbWFnZVVwbG9hZE9wdGlvbnMgfSBmcm9tIFwiLi4vLi4vLi4vbW9kZWxzL2F0ZS1pbWFnZS5tb2RlbFwiO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6IFwiYXRlLWltYWdlLWJ1YmJsZS1tZW51XCIsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBpbXBvcnRzOiBbQXRlQnV0dG9uQ29tcG9uZW50LCBBdGVTZXBhcmF0b3JDb21wb25lbnRdLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXYgI21lbnVSZWYgY2xhc3M9XCJidWJibGUtbWVudVwiIChtb3VzZWRvd24pPVwiJGV2ZW50LnByZXZlbnREZWZhdWx0KClcIj5cbiAgICAgIEBpZiAoaW1hZ2VCdWJibGVNZW51Q29uZmlnKCkuY2hhbmdlSW1hZ2UpIHtcbiAgICAgICAgPGF0ZS1idXR0b25cbiAgICAgICAgICBpY29uPVwiZHJpdmVfZmlsZV9yZW5hbWVfb3V0bGluZVwiXG4gICAgICAgICAgW3RpdGxlXT1cInQoKS5jaGFuZ2VJbWFnZVwiXG4gICAgICAgICAgKGJ1dHRvbkNsaWNrKT1cIm9uQ29tbWFuZCgnY2hhbmdlSW1hZ2UnLCAkZXZlbnQpXCI+PC9hdGUtYnV0dG9uPlxuICAgICAgfVxuICAgICAgQGlmIChpbWFnZUJ1YmJsZU1lbnVDb25maWcoKS5zZXBhcmF0b3IgJiYgaGFzUmVzaXplQnV0dG9ucygpKSB7XG4gICAgICAgIDxhdGUtc2VwYXJhdG9yIC8+XG4gICAgICB9XG4gICAgICBAaWYgKGltYWdlQnViYmxlTWVudUNvbmZpZygpLnJlc2l6ZVNtYWxsKSB7XG4gICAgICAgIDxhdGUtYnV0dG9uXG4gICAgICAgICAgaWNvbj1cImNyb3Bfc3F1YXJlXCJcbiAgICAgICAgICBpY29uU2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICBbdGl0bGVdPVwidCgpLnJlc2l6ZVNtYWxsXCJcbiAgICAgICAgICAoYnV0dG9uQ2xpY2spPVwib25Db21tYW5kKCdyZXNpemVTbWFsbCcsICRldmVudClcIj48L2F0ZS1idXR0b24+XG4gICAgICB9XG4gICAgICBAaWYgKGltYWdlQnViYmxlTWVudUNvbmZpZygpLnJlc2l6ZU1lZGl1bSkge1xuICAgICAgICA8YXRlLWJ1dHRvblxuICAgICAgICAgIGljb249XCJjcm9wX3NxdWFyZVwiXG4gICAgICAgICAgaWNvblNpemU9XCJtZWRpdW1cIlxuICAgICAgICAgIFt0aXRsZV09XCJ0KCkucmVzaXplTWVkaXVtXCJcbiAgICAgICAgICAoYnV0dG9uQ2xpY2spPVwib25Db21tYW5kKCdyZXNpemVNZWRpdW0nLCAkZXZlbnQpXCI+PC9hdGUtYnV0dG9uPlxuICAgICAgfVxuICAgICAgQGlmIChpbWFnZUJ1YmJsZU1lbnVDb25maWcoKS5yZXNpemVMYXJnZSkge1xuICAgICAgICA8YXRlLWJ1dHRvblxuICAgICAgICAgIGljb249XCJjcm9wX3NxdWFyZVwiXG4gICAgICAgICAgaWNvblNpemU9XCJsYXJnZVwiXG4gICAgICAgICAgW3RpdGxlXT1cInQoKS5yZXNpemVMYXJnZVwiXG4gICAgICAgICAgKGJ1dHRvbkNsaWNrKT1cIm9uQ29tbWFuZCgncmVzaXplTGFyZ2UnLCAkZXZlbnQpXCI+PC9hdGUtYnV0dG9uPlxuICAgICAgfVxuICAgICAgQGlmIChpbWFnZUJ1YmJsZU1lbnVDb25maWcoKS5yZXNpemVPcmlnaW5hbCkge1xuICAgICAgICA8YXRlLWJ1dHRvblxuICAgICAgICAgIGljb249XCJwaG90b19zaXplX3NlbGVjdF9hY3R1YWxcIlxuICAgICAgICAgIFt0aXRsZV09XCJ0KCkucmVzaXplT3JpZ2luYWxcIlxuICAgICAgICAgIChidXR0b25DbGljayk9XCJvbkNvbW1hbmQoJ3Jlc2l6ZU9yaWdpbmFsJywgJGV2ZW50KVwiPjwvYXRlLWJ1dHRvbj5cbiAgICAgIH1cbiAgICAgIEBpZiAoaW1hZ2VCdWJibGVNZW51Q29uZmlnKCkuc2VwYXJhdG9yICYmIGltYWdlQnViYmxlTWVudUNvbmZpZygpLmRlbGV0ZUltYWdlKSB7XG4gICAgICAgIDxhdGUtc2VwYXJhdG9yIC8+XG4gICAgICB9XG4gICAgICBAaWYgKGltYWdlQnViYmxlTWVudUNvbmZpZygpLmRlbGV0ZUltYWdlKSB7XG4gICAgICAgIDxhdGUtYnV0dG9uXG4gICAgICAgICAgaWNvbj1cImRlbGV0ZVwiXG4gICAgICAgICAgW3RpdGxlXT1cInQoKS5kZWxldGVJbWFnZVwiXG4gICAgICAgICAgdmFyaWFudD1cImRhbmdlclwiXG4gICAgICAgICAgKGJ1dHRvbkNsaWNrKT1cIm9uQ29tbWFuZCgnZGVsZXRlSW1hZ2UnLCAkZXZlbnQpXCI+PC9hdGUtYnV0dG9uPlxuICAgICAgfVxuICAgIDwvZGl2PlxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBBdGVJbWFnZUJ1YmJsZU1lbnVDb21wb25lbnQgZXh0ZW5kcyBBdGVCYXNlQnViYmxlTWVudSB7XG4gIHJlYWRvbmx5IHQgPSB0aGlzLmkxOG5TZXJ2aWNlLmltYWdlVXBsb2FkO1xuICBwcml2YXRlIHJlYWRvbmx5IGltYWdlU2VydmljZSA9IGluamVjdChBdGVJbWFnZVNlcnZpY2UpO1xuXG4gIGNvbmZpZyA9IGlucHV0PEF0ZUltYWdlQnViYmxlTWVudUNvbmZpZz4oe1xuICAgIGNoYW5nZUltYWdlOiB0cnVlLFxuICAgIHJlc2l6ZVNtYWxsOiB0cnVlLFxuICAgIHJlc2l6ZU1lZGl1bTogdHJ1ZSxcbiAgICByZXNpemVMYXJnZTogdHJ1ZSxcbiAgICByZXNpemVPcmlnaW5hbDogdHJ1ZSxcbiAgICBkZWxldGVJbWFnZTogdHJ1ZSxcbiAgICBzZXBhcmF0b3I6IHRydWUsXG4gIH0pO1xuXG4gIGltYWdlVXBsb2FkID0gaW5wdXQ8QXRlSW1hZ2VVcGxvYWRPcHRpb25zPih7fSk7XG5cbiAgaW1hZ2VCdWJibGVNZW51Q29uZmlnID0gY29tcHV0ZWQoKCkgPT4gKHtcbiAgICBjaGFuZ2VJbWFnZTogdGhpcy5jb25maWcoKS5jaGFuZ2VJbWFnZSA/PyB0cnVlLFxuICAgIHJlc2l6ZVNtYWxsOiB0aGlzLmNvbmZpZygpLnJlc2l6ZVNtYWxsID8/IHRydWUsXG4gICAgcmVzaXplTWVkaXVtOiB0aGlzLmNvbmZpZygpLnJlc2l6ZU1lZGl1bSA/PyB0cnVlLFxuICAgIHJlc2l6ZUxhcmdlOiB0aGlzLmNvbmZpZygpLnJlc2l6ZUxhcmdlID8/IHRydWUsXG4gICAgcmVzaXplT3JpZ2luYWw6IHRoaXMuY29uZmlnKCkucmVzaXplT3JpZ2luYWwgPz8gdHJ1ZSxcbiAgICBkZWxldGVJbWFnZTogdGhpcy5jb25maWcoKS5kZWxldGVJbWFnZSA/PyB0cnVlLFxuICAgIHNlcGFyYXRvcjogdGhpcy5jb25maWcoKS5zZXBhcmF0b3IgPz8gdHJ1ZSxcbiAgfSkpO1xuXG4gIGhhc1Jlc2l6ZUJ1dHRvbnMgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgY29uc3QgYyA9IHRoaXMuaW1hZ2VCdWJibGVNZW51Q29uZmlnKCk7XG4gICAgcmV0dXJuIGMucmVzaXplU21hbGwgfHwgYy5yZXNpemVNZWRpdW0gfHwgYy5yZXNpemVMYXJnZSB8fCBjLnJlc2l6ZU9yaWdpbmFsO1xuICB9KTtcblxuICBvdmVycmlkZSBzaG91bGRTaG93KCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IHsgbm9kZXMsIGlzRWRpdGFibGUsIGlzRm9jdXNlZCB9ID0gdGhpcy5zdGF0ZSgpO1xuXG4gICAgaWYgKHRoaXMuZWRpdG9yQ29tbWFuZHMubGlua0VkaXRNb2RlKCkgfHwgdGhpcy5lZGl0b3JDb21tYW5kcy5jb2xvckVkaXRNb2RlKCkpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICByZXR1cm4gbm9kZXMuaXNJbWFnZSAmJiBpc0VkaXRhYmxlICYmIGlzRm9jdXNlZDtcbiAgfVxuXG4gIG92ZXJyaWRlIGdldFNlbGVjdGlvblJlY3QoKTogRE9NUmVjdCB7XG4gICAgY29uc3QgZWQgPSB0aGlzLmVkaXRvcigpO1xuICAgIGlmICghZWQpIHtcbiAgICAgIHJldHVybiBuZXcgRE9NUmVjdCgwLCAwLCAwLCAwKTtcbiAgICB9XG5cbiAgICBjb25zdCB7IGZyb20gfSA9IGVkLnN0YXRlLnNlbGVjdGlvbjtcblxuICAgIHRyeSB7XG4gICAgICAvLyAxLiBEaXJlY3QgUHJvc2VNaXJyb3IgYXBwcm9hY2g6IGdldCBET00gbm9kZSBhdCBwb3NpdGlvblxuICAgICAgY29uc3QgZG9tID0gZWQudmlldy5ub2RlRE9NKGZyb20pO1xuICAgICAgaWYgKGRvbSBpbnN0YW5jZW9mIEhUTUxFbGVtZW50KSB7XG4gICAgICAgIC8vIElmIGl0J3MgYSByZXNpemFibGUgY29udGFpbmVyLCBsb29rIGZvciB0aGUgaW1hZ2UgaW5zaWRlXG4gICAgICAgIGlmIChkb20uY2xhc3NMaXN0LmNvbnRhaW5zKFwicmVzaXphYmxlLWltYWdlLWNvbnRhaW5lclwiKSkge1xuICAgICAgICAgIGNvbnN0IGltZyA9IGRvbS5xdWVyeVNlbGVjdG9yKFwiaW1nXCIpO1xuICAgICAgICAgIGlmIChpbWcpIHtcbiAgICAgICAgICAgIHJldHVybiBpbWcuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkb20uZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoX2UpIHtcbiAgICAgIC8vIEZhbGxiYWNrIGlmIG5vZGVET00gZmFpbHNcbiAgICB9XG5cbiAgICAvLyAyLiBVbHRpbWF0ZSBmYWxsYmFjazogZmluZCBzZWxlY3RlZCBpbWFnZSBpbiBET01cbiAgICBjb25zdCBzZWxlY3RlZEltZyA9IGVkLnZpZXcuZG9tLnF1ZXJ5U2VsZWN0b3IoXG4gICAgICBcImltZy5zZWxlY3RlZCwgLnJlc2l6YWJsZS1pbWFnZS1jb250YWluZXIuc2VsZWN0ZWQgaW1nXCJcbiAgICApO1xuICAgIGlmIChzZWxlY3RlZEltZykge1xuICAgICAgcmV0dXJuIHNlbGVjdGVkSW1nLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIH1cblxuICAgIC8vIElmIG5vdGhpbmcgZm91bmQgYXQgYWxsXG4gICAgcmV0dXJuIG5ldyBET01SZWN0KC05OTk5LCAtOTk5OSwgMCwgMCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgb3ZlcnJpZGUgZXhlY3V0ZUNvbW1hbmQoZWRpdG9yOiBFZGl0b3IsIGNvbW1hbmQ6IHN0cmluZywgLi4uX2FyZ3M6IHVua25vd25bXSk6IHZvaWQge1xuICAgIHN3aXRjaCAoY29tbWFuZCkge1xuICAgICAgY2FzZSBcImNoYW5nZUltYWdlXCI6XG4gICAgICAgIHRoaXMuY2hhbmdlSW1hZ2UoKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIFwicmVzaXplU21hbGxcIjpcbiAgICAgICAgdGhpcy5pbWFnZVNlcnZpY2UucmVzaXplSW1hZ2VUb1NtYWxsKGVkaXRvcik7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBcInJlc2l6ZU1lZGl1bVwiOlxuICAgICAgICB0aGlzLmltYWdlU2VydmljZS5yZXNpemVJbWFnZVRvTWVkaXVtKGVkaXRvcik7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBcInJlc2l6ZUxhcmdlXCI6XG4gICAgICAgIHRoaXMuaW1hZ2VTZXJ2aWNlLnJlc2l6ZUltYWdlVG9MYXJnZShlZGl0b3IpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgXCJyZXNpemVPcmlnaW5hbFwiOlxuICAgICAgICB0aGlzLmltYWdlU2VydmljZS5yZXNpemVJbWFnZVRvT3JpZ2luYWwoZWRpdG9yKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIFwiZGVsZXRlSW1hZ2VcIjpcbiAgICAgICAgdGhpcy5kZWxldGVJbWFnZSgpO1xuICAgICAgICBicmVhaztcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGNoYW5nZUltYWdlKCkge1xuICAgIGNvbnN0IGVkID0gdGhpcy5lZGl0b3IoKTtcbiAgICBpZiAoIWVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIC8vIFVzZSBkZWRpY2F0ZWQgbWV0aG9kIHRvIHJlcGxhY2UgYW4gZXhpc3RpbmcgaW1hZ2VcbiAgICAgIGF3YWl0IHRoaXMuaW1hZ2VTZXJ2aWNlLnNlbGVjdEFuZFJlcGxhY2VJbWFnZShlZCwgdGhpcy5pbWFnZVVwbG9hZCgpKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcih0aGlzLmkxOG5TZXJ2aWNlLmltYWdlVXBsb2FkKCkudXBsb2FkRXJyb3IsIGVycm9yKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGRlbGV0ZUltYWdlKCkge1xuICAgIGNvbnN0IGVkID0gdGhpcy5lZGl0b3IoKTtcbiAgICBpZiAoZWQpIHtcbiAgICAgIGVkLmNoYWluKCkuZm9jdXMoKS5kZWxldGVTZWxlY3Rpb24oKS5ydW4oKTtcbiAgICB9XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,247 @@
1
+ import { Component, ChangeDetectionStrategy, viewChild, signal, effect, inject, } from "@angular/core";
2
+ import { FormsModule } from "@angular/forms";
3
+ import { AteButtonComponent } from "../../ui/ate-button.component";
4
+ import { AteLinkService } from "../../../services/ate-link.service";
5
+ import { AteSeparatorComponent } from "../../ui/ate-separator.component";
6
+ import { AteBaseSubBubbleMenu } from "../base/ate-base-sub-bubble-menu";
7
+ import * as i0 from "@angular/core";
8
+ import * as i1 from "@angular/forms";
9
+ export class AteLinkBubbleMenuComponent extends AteBaseSubBubbleMenu {
10
+ constructor() {
11
+ super();
12
+ this.linkSvc = inject(AteLinkService);
13
+ this.t = this.i18nService.bubbleMenu;
14
+ this.common = this.i18nService.common;
15
+ this.linkInput = viewChild("linkInput");
16
+ this.editUrl = signal("");
17
+ // Reactive effect for URL sync
18
+ effect(() => {
19
+ const state = this.state();
20
+ const isInteracting = this.linkSvc.isInteracting();
21
+ const currentLinkHref = state.marks.linkHref || "";
22
+ // SYNC LOGIC:
23
+ // If we are NOT currently typing (interacting),
24
+ // always keep the input in sync with the current editor selection.
25
+ if (!isInteracting) {
26
+ this.editUrl.set(currentLinkHref);
27
+ }
28
+ }, { allowSignalWrites: true });
29
+ }
30
+ onStateChange() {
31
+ this.linkSvc.editMode();
32
+ this.linkSvc.menuTrigger();
33
+ this.linkSvc.isInteracting();
34
+ }
35
+ shouldShow() {
36
+ const { selection, marks, isEditable, isFocused } = this.state();
37
+ if (!isEditable) {
38
+ return false;
39
+ }
40
+ // Show if explicitly in edit mode (from toolbar/bubble menu) or interacting with input
41
+ if (this.linkSvc.editMode() || this.linkSvc.isInteracting()) {
42
+ return true;
43
+ }
44
+ // Show if cursor is on an existing link (read/preview mode)
45
+ return isFocused && marks.link && selection.empty;
46
+ }
47
+ getSelectionRect() {
48
+ const trigger = this.linkSvc.menuTrigger();
49
+ const ed = this.editor();
50
+ if (!ed) {
51
+ return new DOMRect(0, 0, 0, 0);
52
+ }
53
+ // 1. If we have a stable trigger from service (toolbar or parent menu), anchor to it
54
+ if (trigger) {
55
+ const rect = trigger.getBoundingClientRect();
56
+ // Only use if it's still visible/in DOM (width > 0)
57
+ if (rect.width > 0) {
58
+ return rect;
59
+ }
60
+ }
61
+ // 2. Otherwise (bubble menu / relay), anchor to text selection
62
+ const { from } = ed.state.selection;
63
+ try {
64
+ const { node } = ed.view.domAtPos(from);
65
+ const element = node instanceof Element ? node : node.parentElement;
66
+ const linkElement = element?.closest("a");
67
+ if (linkElement) {
68
+ return linkElement.getBoundingClientRect();
69
+ }
70
+ }
71
+ catch (_e) {
72
+ /* ignore */
73
+ }
74
+ // Use native selection for multi-line accuracy
75
+ const selection = window.getSelection();
76
+ if (selection && selection.rangeCount > 0) {
77
+ const range = selection.getRangeAt(0);
78
+ const rect = range.getBoundingClientRect();
79
+ if (rect.width > 0 && rect.height > 0) {
80
+ return rect;
81
+ }
82
+ }
83
+ // Final fallback to coordinates at cursor
84
+ const { top, bottom, left, right } = ed.view.coordsAtPos(from);
85
+ return new DOMRect(left, top, right - left, bottom - top);
86
+ }
87
+ onTippyHide() {
88
+ // Clear trigger only AFTER the menu is hidden to maintain anchor stability during animation
89
+ this.linkSvc.done();
90
+ this.linkSvc.close();
91
+ }
92
+ currentUrl() {
93
+ return this.state().marks.linkHref || "";
94
+ }
95
+ onMouseDown(event) {
96
+ event.stopPropagation();
97
+ const target = event.target;
98
+ if (target.tagName !== "INPUT") {
99
+ event.preventDefault();
100
+ }
101
+ }
102
+ onFocus() {
103
+ this.linkSvc.setInteracting(true);
104
+ }
105
+ onBlur() {
106
+ setTimeout(() => {
107
+ this.linkSvc.setInteracting(false);
108
+ this.updateMenu();
109
+ }, 150);
110
+ }
111
+ onOpenLink(event) {
112
+ event.preventDefault();
113
+ event.stopPropagation();
114
+ const url = this.currentUrl();
115
+ if (url) {
116
+ window.open(url, "_blank", "noopener,noreferrer");
117
+ }
118
+ }
119
+ onRemove(event) {
120
+ event.preventDefault();
121
+ event.stopPropagation();
122
+ this.linkSvc.unsetLink(this.editor());
123
+ }
124
+ onApply(event) {
125
+ event.preventDefault();
126
+ event.stopPropagation();
127
+ const url = this.editUrl().trim();
128
+ if (url) {
129
+ this.linkSvc.setLink(this.editor(), url);
130
+ this.editUrl.set("");
131
+ this.linkSvc.setInteracting(false);
132
+ this.hideTippy();
133
+ }
134
+ else {
135
+ this.onRemove(event);
136
+ }
137
+ }
138
+ onCancel(event) {
139
+ event.preventDefault();
140
+ event.stopPropagation();
141
+ this.linkSvc.close();
142
+ this.hideTippy();
143
+ }
144
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AteLinkBubbleMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
145
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "18.2.14", type: AteLinkBubbleMenuComponent, isStandalone: true, selector: "ate-link-bubble-menu", viewQueries: [{ propertyName: "linkInput", first: true, predicate: ["linkInput"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
146
+ <div
147
+ #menuRef
148
+ class="bubble-menu"
149
+ (mousedown)="onMouseDown($event)"
150
+ (click)="$event.stopPropagation()"
151
+ (keydown)="$event.stopPropagation()"
152
+ (keydown.escape)="onCancel($event)"
153
+ tabindex="-1"
154
+ role="dialog">
155
+ <div class="link-input-row">
156
+ <div class="url-input-container">
157
+ <span class="material-symbols-outlined icon-link">link</span>
158
+ <input
159
+ #linkInput
160
+ type="text"
161
+ class="url-field"
162
+ [placeholder]="t().linkUrl"
163
+ [ngModel]="editUrl()"
164
+ (ngModelChange)="editUrl.set($event)"
165
+ (focus)="onFocus()"
166
+ (blur)="onBlur()"
167
+ (keydown.enter)="onApply($event)"
168
+ (keydown.escape)="onCancel($event)" />
169
+ </div>
170
+
171
+ <div class="action-buttons">
172
+ <ate-button
173
+ icon="check"
174
+ [title]="common().apply"
175
+ color="var(--ate-primary)"
176
+ [disabled]="!editUrl().trim()"
177
+ (buttonClick)="onApply($event)"></ate-button>
178
+ <ate-button
179
+ icon="open_in_new"
180
+ [title]="t().openLink"
181
+ [disabled]="!currentUrl()"
182
+ (buttonClick)="onOpenLink($event)"></ate-button>
183
+ <ate-separator />
184
+ <ate-button
185
+ icon="link_off"
186
+ [title]="t().removeLink"
187
+ variant="danger"
188
+ [disabled]="!currentUrl()"
189
+ (buttonClick)="onRemove($event)"></ate-button>
190
+ </div>
191
+ </div>
192
+ </div>
193
+ `, isInline: true, styles: [".link-input-row{display:flex;align-items:center;gap:6px}.url-input-container{flex:1;display:flex;align-items:center;background:var(--ate-surface-secondary, #f8fafc);border:1px solid var(--ate-border, #e2e8f0);border-radius:8px;padding:0 10px;height:32px;transition:all .15s ease}.url-input-container:focus-within{border-color:var(--ate-primary, #3b82f6);background:var(--ate-surface, #ffffff);box-shadow:0 0 0 2px var(--ate-primary-light, rgba(59, 130, 246, .1))}.icon-link{font-size:16px;color:var(--ate-text-muted, #94a3b8);margin-right:6px}.url-field{background:transparent;border:none;outline:none;color:var(--ate-text, #1e293b);font-size:13px;width:100%;font-family:inherit}.action-buttons{display:flex;align-items:center;gap:2px}\n"], dependencies: [{ kind: "component", type: AteButtonComponent, selector: "ate-button", inputs: ["icon", "title", "active", "disabled", "color", "backgroundColor", "variant", "size", "iconSize"], outputs: ["buttonClick"] }, { kind: "component", type: AteSeparatorComponent, selector: "ate-separator", inputs: ["orientation", "size"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
194
+ }
195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AteLinkBubbleMenuComponent, decorators: [{
196
+ type: Component,
197
+ args: [{ selector: "ate-link-bubble-menu", standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AteButtonComponent, AteSeparatorComponent, FormsModule], template: `
198
+ <div
199
+ #menuRef
200
+ class="bubble-menu"
201
+ (mousedown)="onMouseDown($event)"
202
+ (click)="$event.stopPropagation()"
203
+ (keydown)="$event.stopPropagation()"
204
+ (keydown.escape)="onCancel($event)"
205
+ tabindex="-1"
206
+ role="dialog">
207
+ <div class="link-input-row">
208
+ <div class="url-input-container">
209
+ <span class="material-symbols-outlined icon-link">link</span>
210
+ <input
211
+ #linkInput
212
+ type="text"
213
+ class="url-field"
214
+ [placeholder]="t().linkUrl"
215
+ [ngModel]="editUrl()"
216
+ (ngModelChange)="editUrl.set($event)"
217
+ (focus)="onFocus()"
218
+ (blur)="onBlur()"
219
+ (keydown.enter)="onApply($event)"
220
+ (keydown.escape)="onCancel($event)" />
221
+ </div>
222
+
223
+ <div class="action-buttons">
224
+ <ate-button
225
+ icon="check"
226
+ [title]="common().apply"
227
+ color="var(--ate-primary)"
228
+ [disabled]="!editUrl().trim()"
229
+ (buttonClick)="onApply($event)"></ate-button>
230
+ <ate-button
231
+ icon="open_in_new"
232
+ [title]="t().openLink"
233
+ [disabled]="!currentUrl()"
234
+ (buttonClick)="onOpenLink($event)"></ate-button>
235
+ <ate-separator />
236
+ <ate-button
237
+ icon="link_off"
238
+ [title]="t().removeLink"
239
+ variant="danger"
240
+ [disabled]="!currentUrl()"
241
+ (buttonClick)="onRemove($event)"></ate-button>
242
+ </div>
243
+ </div>
244
+ </div>
245
+ `, styles: [".link-input-row{display:flex;align-items:center;gap:6px}.url-input-container{flex:1;display:flex;align-items:center;background:var(--ate-surface-secondary, #f8fafc);border:1px solid var(--ate-border, #e2e8f0);border-radius:8px;padding:0 10px;height:32px;transition:all .15s ease}.url-input-container:focus-within{border-color:var(--ate-primary, #3b82f6);background:var(--ate-surface, #ffffff);box-shadow:0 0 0 2px var(--ate-primary-light, rgba(59, 130, 246, .1))}.icon-link{font-size:16px;color:var(--ate-text-muted, #94a3b8);margin-right:6px}.url-field{background:transparent;border:none;outline:none;color:var(--ate-text, #1e293b);font-size:13px;width:100%;font-family:inherit}.action-buttons{display:flex;align-items:center;gap:2px}\n"] }]
246
+ }], ctorParameters: () => [] });
247
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXRlLWxpbmstYnViYmxlLW1lbnUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci10aXB0YXAtZWRpdG9yL3NyYy9saWIvY29tcG9uZW50cy9idWJibGUtbWVudXMvbGluay9hdGUtbGluay1idWJibGUtbWVudS5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLFNBQVMsRUFDVCx1QkFBdUIsRUFDdkIsU0FBUyxFQUVULE1BQU0sRUFDTixNQUFNLEVBQ04sTUFBTSxHQUNQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUM3QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUNuRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDcEUsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDekUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7OztBQTBHeEUsTUFBTSxPQUFPLDBCQUEyQixTQUFRLG9CQUFvQjtJQVVsRTtRQUNFLEtBQUssRUFBRSxDQUFDO1FBVk8sWUFBTyxHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUV6QyxNQUFDLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLENBQUM7UUFDaEMsV0FBTSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDO1FBRTFDLGNBQVMsR0FBRyxTQUFTLENBQStCLFdBQVcsQ0FBQyxDQUFDO1FBRWpFLFlBQU8sR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7UUFLbkIsK0JBQStCO1FBQy9CLE1BQU0sQ0FBQyxHQUFHLEVBQUU7WUFDVixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxF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Q0FBQyxDQUFDO1lBQ25DLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNuQixDQUFDO2FBQU0sQ0FBQztZQUNOLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7SUFFRCxRQUFRLENBQUMsS0FBWTtRQUNuQixLQUFLLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDdkIsS0FBSyxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ3hCLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO0lBQ25CLENBQUM7K0dBN0pVLDBCQUEwQjttR0FBMUIsMEJBQTBCLCtOQW5HM0I7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWdEVCwyeUJBakRTLGtCQUFrQiw2TEFBRSxxQkFBcUIsMEZBQUUsV0FBVzs7NEZBb0dyRCwwQkFBMEI7a0JBeEd0QyxTQUFTOytCQUNFLHNCQUFzQixjQUNwQixJQUFJLG1CQUNDLHVCQUF1QixDQUFDLE1BQU0sV0FDdEMsQ0FBQyxrQkFBa0IsRUFBRSxxQkFBcUIsRUFBRSxXQUFXLENBQUMsWUFDdkQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWdEVCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENvbXBvbmVudCxcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIHZpZXdDaGlsZCxcbiAgRWxlbWVudFJlZixcbiAgc2lnbmFsLFxuICBlZmZlY3QsXG4gIGluamVjdCxcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7IEZvcm1zTW9kdWxlIH0gZnJvbSBcIkBhbmd1bGFyL2Zvcm1zXCI7XG5pbXBvcnQgeyBBdGVCdXR0b25Db21wb25lbnQgfSBmcm9tIFwiLi4vLi4vdWkvYXRlLWJ1dHRvbi5jb21wb25lbnRcIjtcbmltcG9ydCB7IEF0ZUxpbmtTZXJ2aWNlIH0gZnJvbSBcIi4uLy4uLy4uL3NlcnZpY2VzL2F0ZS1saW5rLnNlcnZpY2VcIjtcbmltcG9ydCB7IEF0ZVNlcGFyYXRvckNvbXBvbmVudCB9IGZyb20gXCIuLi8uLi91aS9hdGUtc2VwYXJhdG9yLmNvbXBvbmVudFwiO1xuaW1wb3J0IHsgQXRlQmFzZVN1YkJ1YmJsZU1lbnUgfSBmcm9tIFwiLi4vYmFzZS9hdGUtYmFzZS1zdWItYnViYmxlLW1lbnVcIjtcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiBcImF0ZS1saW5rLWJ1YmJsZS1tZW51XCIsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBpbXBvcnRzOiBbQXRlQnV0dG9uQ29tcG9uZW50LCBBdGVTZXBhcmF0b3JDb21wb25lbnQsIEZvcm1zTW9kdWxlXSxcbiAgdGVtcGxhdGU6IGBcbiAgICA8ZGl2XG4gICAgICAjbWVudVJlZlxuICAgICAgY2xhc3M9XCJidWJibGUtbWVudVwiXG4gICAgICAobW91c2Vkb3duKT1cIm9uTW91c2VEb3duKCRldmVudClcIlxuICAgICAgKGNsaWNrKT1cIiRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiXG4gICAgICAoa2V5ZG93bik9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgKGtleWRvd24uZXNjYXBlKT1cIm9uQ2FuY2VsKCRldmVudClcIlxuICAgICAgdGFiaW5kZXg9XCItMVwiXG4gICAgICByb2xlPVwiZGlhbG9nXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwibGluay1pbnB1dC1yb3dcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cInVybC1pbnB1dC1jb250YWluZXJcIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm1hdGVyaWFsLXN5bWJvbHMtb3V0bGluZWQgaWNvbi1saW5rXCI+bGluazwvc3Bhbj5cbiAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICNsaW5rSW5wdXRcbiAgICAgICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgICAgIGNsYXNzPVwidXJsLWZpZWxkXCJcbiAgICAgICAgICAgIFtwbGFjZWhvbGRlcl09XCJ0KCkubGlua1VybFwiXG4gICAgICAgICAgICBbbmdNb2RlbF09XCJlZGl0VXJsKClcIlxuICAgICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwiZWRpdFVybC5zZXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAoZm9jdXMpPVwib25Gb2N1cygpXCJcbiAgICAgICAgICAgIChibHVyKT1cIm9uQmx1cigpXCJcbiAgICAgICAgICAgIChrZXlkb3duLmVudGVyKT1cIm9uQXBwbHkoJGV2ZW50KVwiXG4gICAgICAgICAgICAoa2V5ZG93bi5lc2NhcGUpPVwib25DYW5jZWwoJGV2ZW50KVwiIC8+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxkaXYgY2xhc3M9XCJhY3Rpb24tYnV0dG9uc1wiPlxuICAgICAgICAgIDxhdGUtYnV0dG9uXG4gICAgICAgICAgICBpY29uPVwiY2hlY2tcIlxuICAgICAgICAgICAgW3RpdGxlXT1cImNvbW1vbigpLmFwcGx5XCJcbiAgICAgICAgICAgIGNvbG9yPVwidmFyKC0tYXRlLXByaW1hcnkpXCJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhZWRpdFVybCgpLnRyaW0oKVwiXG4gICAgICAgICAgICAoYnV0dG9uQ2xpY2spPVwib25BcHBseSgkZXZlbnQpXCI+PC9hdGUtYnV0dG9uPlxuICAgICAgICAgIDxhdGUtYnV0dG9uXG4gICAgICAgICAgICBpY29uPVwib3Blbl9pbl9uZXdcIlxuICAgICAgICAgICAgW3RpdGxlXT1cInQoKS5vcGVuTGlua1wiXG4gICAgICAgICAgICBbZGlzYWJsZWRdPVwiIWN1cnJlbnRVcmwoKVwiXG4gICAgICAgICAgICAoYnV0dG9uQ2xpY2spPVwib25PcGVuTGluaygkZXZlbnQpXCI+PC9hdGUtYnV0dG9uPlxuICAgICAgICAgIDxhdGUtc2VwYXJhdG9yIC8+XG4gICAgICAgICAgPGF0ZS1idXR0b25cbiAgICAgICAgICAgIGljb249XCJsaW5rX29mZlwiXG4gICAgICAgICAgICBbdGl0bGVdPVwidCgpLnJlbW92ZUxpbmtcIlxuICAgICAgICAgICAgdmFyaWFudD1cImRhbmdlclwiXG4gICAgICAgICAgICBbZGlzYWJsZWRdPVwiIWN1cnJlbnRVcmwoKVwiXG4gICAgICAgICAgICAoYnV0dG9uQ2xpY2spPVwib25SZW1vdmUoJGV2ZW50KVwiPjwvYXRlLWJ1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgYCxcbiAgc3R5bGVzOiBbXG4gICAgYFxuICAgICAgLmxpbmstaW5wdXQtcm93IHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICAgICAgZ2FwOiA2cHg7XG4gICAgICB9XG5cbiAgICAgIC51cmwtaW5wdXQtY29udGFpbmVyIHtcbiAgICAgICAgZmxleDogMTtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICAgICAgYmFja2dyb3VuZDogdmFyKC0tYXRlLXN1cmZhY2Utc2Vjb25kYXJ5LCAjZjhmYWZjKTtcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgdmFyKC0tYXRlLWJvcmRlciwgI2UyZThmMCk7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDhweDtcbiAgICAgICAgcGFkZGluZzogMCAxMHB4O1xuICAgICAgICBoZWlnaHQ6IDMycHg7XG4gICAgICAgIHRyYW5zaXRpb246IGFsbCAxNTBtcyBlYXNlO1xuICAgICAgfVxuXG4gICAgICAudXJsLWlucHV0LWNvbnRhaW5lcjpmb2N1cy13aXRoaW4ge1xuICAgICAgICBib3JkZXItY29sb3I6IHZhcigtLWF0ZS1wcmltYXJ5LCAjM2I4MmY2KTtcbiAgICAgICAgYmFja2dyb3VuZDogdmFyKC0tYXRlLXN1cmZhY2UsICNmZmZmZmYpO1xuICAgICAgICBib3gtc2hhZG93OiAwIDAgMCAycHggdmFyKC0tYXRlLXByaW1hcnktbGlnaHQsIHJnYmEoNTksIDEzMCwgMjQ2LCAwLjEpKTtcbiAgICAgIH1cblxuICAgICAgLmljb24tbGluayB7XG4gICAgICAgIGZvbnQtc2l6ZTogMTZweDtcbiAgICAgICAgY29sb3I6IHZhcigtLWF0ZS10ZXh0LW11dGVkLCAjOTRhM2I4KTtcbiAgICAgICAgbWFyZ2luLXJpZ2h0OiA2cHg7XG4gICAgICB9XG5cbiAgICAgIC51cmwtZmllbGQge1xuICAgICAgICBiYWNrZ3JvdW5kOiB0cmFuc3BhcmVudDtcbiAgICAgICAgYm9yZGVyOiBub25lO1xuICAgICAgICBvdXRsaW5lOiBub25lO1xuICAgICAgICBjb2xvcjogdmFyKC0tYXRlLXRleHQsICMxZTI5M2IpO1xuICAgICAgICBmb250LXNpemU6IDEzcHg7XG4gICAgICAgIHdpZHRoOiAxMDAlO1xuICAgICAgICBmb250LWZhbWlseTogaW5oZXJpdDtcbiAgICAgIH1cblxuICAgICAgLmFjdGlvbi1idXR0b25zIHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICAgICAgZ2FwOiAycHg7XG4gICAgICB9XG4gICAgYCxcbiAgXSxcbn0pXG5leHBvcnQgY2xhc3MgQXRlTGlua0J1YmJsZU1lbnVDb21wb25lbnQgZXh0ZW5kcyBBdGVCYXNlU3ViQnViYmxlTWVudSB7XG4gIHByaXZhdGUgcmVhZG9ubHkgbGlua1N2YyA9IGluamVjdChBdGVMaW5rU2VydmljZSk7XG5cbiAgcmVhZG9ubHkgdCA9IHRoaXMuaTE4blNlcnZpY2UuYnViYmxlTWVudTtcbiAgcmVhZG9ubHkgY29tbW9uID0gdGhpcy5pMThuU2VydmljZS5jb21tb247XG5cbiAgbGlua0lucHV0ID0gdmlld0NoaWxkPEVsZW1lbnRSZWY8SFRNTElucHV0RWxlbWVudD4+KFwibGlua0lucHV0XCIpO1xuXG4gIGVkaXRVcmwgPSBzaWduYWwoXCJcIik7XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgc3VwZXIoKTtcblxuICAgIC8vIFJlYWN0aXZlIGVmZmVjdCBmb3IgVVJMIHN5bmNcbiAgICBlZmZlY3QoKCkgPT4ge1xuICAgICAgY29uc3Qgc3RhdGUgPSB0aGlzLnN0YXRlKCk7XG4gICAgICBjb25zdCBpc0ludGVyYWN0aW5nID0gdGhpcy5saW5rU3ZjLmlzSW50ZXJhY3RpbmcoKTtcbiAgICAgIGNvbnN0IGN1cnJlbnRMaW5rSHJlZiA9IHN0YXRlLm1hcmtzLmxpbmtIcmVmIHx8IFwiXCI7XG5cbiAgICAgIC8vIFNZTkMgTE9HSUM6XG4gICAgICAvLyBJZiB3ZSBhcmUgTk9UIGN1cnJlbnRseSB0eXBpbmcgKGludGVyYWN0aW5nKSxcbiAgICAgIC8vIGFsd2F5cyBrZWVwIHRoZSBpbnB1dCBpbiBzeW5jIHdpdGggdGhlIGN1cnJlbnQgZWRpdG9yIHNlbGVjdGlvbi5cbiAgICAgIGlmICghaXNJbnRlcmFjdGluZykge1xuICAgICAgICB0aGlzLmVkaXRVcmwuc2V0KGN1cnJlbnRMaW5rSHJlZik7XG4gICAgICB9XG4gICAgfSwgeyBhbGxvd1NpZ25hbFdyaXRlczogdHJ1ZSB9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvdmVycmlkZSBvblN0YXRlQ2hhbmdlKCkge1xuICAgIHRoaXMubGlua1N2Yy5lZGl0TW9kZSgpO1xuICAgIHRoaXMubGlua1N2Yy5tZW51VHJpZ2dlcigpO1xuICAgIHRoaXMubGlua1N2Yy5pc0ludGVyYWN0aW5nKCk7XG4gIH1cblxuICBvdmVycmlkZSBzaG91bGRTaG93KCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IHsgc2VsZWN0aW9uLCBtYXJrcywgaXNFZGl0YWJsZSwgaXNGb2N1c2VkIH0gPSB0aGlzLnN0YXRlKCk7XG4gICAgaWYgKCFpc0VkaXRhYmxlKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gU2hvdyBpZiBleHBsaWNpdGx5IGluIGVkaXQgbW9kZSAoZnJvbSB0b29sYmFyL2J1YmJsZSBtZW51KSBvciBpbnRlcmFjdGluZyB3aXRoIGlucHV0XG4gICAgaWYgKHRoaXMubGlua1N2Yy5lZGl0TW9kZSgpIHx8IHRoaXMubGlua1N2Yy5pc0ludGVyYWN0aW5nKCkpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIC8vIFNob3cgaWYgY3Vyc29yIGlzIG9uIGFuIGV4aXN0aW5nIGxpbmsgKHJlYWQvcHJldmlldyBtb2RlKVxuICAgIHJldHVybiBpc0ZvY3VzZWQgJiYgbWFya3MubGluayAmJiBzZWxlY3Rpb24uZW1wdHk7XG4gIH1cblxuICBvdmVycmlkZSBnZXRTZWxlY3Rpb25SZWN0KCk6IERPTVJlY3Qge1xuICAgIGNvbnN0IHRyaWdnZXIgPSB0aGlzLmxpbmtTdmMubWVudVRyaWdnZXIoKTtcbiAgICBjb25zdCBlZCA9IHRoaXMuZWRpdG9yKCk7XG4gICAgaWYgKCFlZCkge1xuICAgICAgcmV0dXJuIG5ldyBET01SZWN0KDAsIDAsIDAsIDApO1xuICAgIH1cblxuICAgIC8vIDEuIElmIHdlIGhhdmUgYSBzdGFibGUgdHJpZ2dlciBmcm9tIHNlcnZpY2UgKHRvb2xiYXIgb3IgcGFyZW50IG1lbnUpLCBhbmNob3IgdG8gaXRcbiAgICBpZiAodHJpZ2dlcikge1xuICAgICAgY29uc3QgcmVjdCA9IHRyaWdnZXIuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAvLyBPbmx5IHVzZSBpZiBpdCdzIHN0aWxsIHZpc2libGUvaW4gRE9NICh3aWR0aCA+IDApXG4gICAgICBpZiAocmVjdC53aWR0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIHJlY3Q7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gMi4gT3RoZXJ3aXNlIChidWJibGUgbWVudSAvIHJlbGF5KSwgYW5jaG9yIHRvIHRleHQgc2VsZWN0aW9uXG4gICAgY29uc3QgeyBmcm9tIH0gPSBlZC5zdGF0ZS5zZWxlY3Rpb247XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgeyBub2RlIH0gPSBlZC52aWV3LmRvbUF0UG9zKGZyb20pO1xuICAgICAgY29uc3QgZWxlbWVudCA9IG5vZGUgaW5zdGFuY2VvZiBFbGVtZW50ID8gbm9kZSA6IG5vZGUucGFyZW50RWxlbWVudDtcbiAgICAgIGNvbnN0IGxpbmtFbGVtZW50ID0gZWxlbWVudD8uY2xvc2VzdChcImFcIik7XG4gICAgICBpZiAobGlua0VsZW1lbnQpIHtcbiAgICAgICAgcmV0dXJuIGxpbmtFbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKF9lKSB7XG4gICAgICAvKiBpZ25vcmUgKi9cbiAgICB9XG5cbiAgICAvLyBVc2UgbmF0aXZlIHNlbGVjdGlvbiBmb3IgbXVsdGktbGluZSBhY2N1cmFjeVxuICAgIGNvbnN0IHNlbGVjdGlvbiA9IHdpbmRvdy5nZXRTZWxlY3Rpb24oKTtcbiAgICBpZiAoc2VsZWN0aW9uICYmIHNlbGVjdGlvbi5yYW5nZUNvdW50ID4gMCkge1xuICAgICAgY29uc3QgcmFuZ2UgPSBzZWxlY3Rpb24uZ2V0UmFuZ2VBdCgwKTtcbiAgICAgIGNvbnN0IHJlY3QgPSByYW5nZS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgIGlmIChyZWN0LndpZHRoID4gMCAmJiByZWN0LmhlaWdodCA+IDApIHtcbiAgICAgICAgcmV0dXJuIHJlY3Q7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRmluYWwgZmFsbGJhY2sgdG8gY29vcmRpbmF0ZXMgYXQgY3Vyc29yXG4gICAgY29uc3QgeyB0b3AsIGJvdHRvbSwgbGVmdCwgcmlnaHQgfSA9IGVkLnZpZXcuY29vcmRzQXRQb3MoZnJvbSk7XG4gICAgcmV0dXJuIG5ldyBET01SZWN0KGxlZnQsIHRvcCwgcmlnaHQgLSBsZWZ0LCBib3R0b20gLSB0b3ApO1xuICB9XG5cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIG9uVGlwcHlIaWRlKCkge1xuICAgIC8vIENsZWFyIHRyaWdnZXIgb25seSBBRlRFUiB0aGUgbWVudSBpcyBoaWRkZW4gdG8gbWFpbnRhaW4gYW5jaG9yIHN0YWJpbGl0eSBkdXJpbmcgYW5pbWF0aW9uXG4gICAgdGhpcy5saW5rU3ZjLmRvbmUoKTtcbiAgICB0aGlzLmxpbmtTdmMuY2xvc2UoKTtcbiAgfVxuXG4gIGN1cnJlbnRVcmwoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RhdGUoKS5tYXJrcy5saW5rSHJlZiB8fCBcIlwiO1xuICB9XG5cbiAgb25Nb3VzZURvd24oZXZlbnQ6IE1vdXNlRXZlbnQpIHtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBjb25zdCB0YXJnZXQgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7XG4gICAgaWYgKHRhcmdldC50YWdOYW1lICE9PSBcIklOUFVUXCIpIHtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgfVxuICB9XG5cbiAgb25Gb2N1cygpIHtcbiAgICB0aGlzLmxpbmtTdmMuc2V0SW50ZXJhY3RpbmcodHJ1ZSk7XG4gIH1cblxuICBvbkJsdXIoKSB7XG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLmxpbmtTdmMuc2V0SW50ZXJhY3RpbmcoZmFsc2UpO1xuICAgICAgdGhpcy51cGRhdGVNZW51KCk7XG4gICAgfSwgMTUwKTtcbiAgfVxuXG4gIG9uT3BlbkxpbmsoZXZlbnQ6IEV2ZW50KSB7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBjb25zdCB1cmwgPSB0aGlzLmN1cnJlbnRVcmwoKTtcbiAgICBpZiAodXJsKSB7XG4gICAgICB3aW5kb3cub3Blbih1cmwsIFwiX2JsYW5rXCIsIFwibm9vcGVuZXIsbm9yZWZlcnJlclwiKTtcbiAgICB9XG4gIH1cblxuICBvblJlbW92ZShldmVudDogRXZlbnQpIHtcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIHRoaXMubGlua1N2Yy51bnNldExpbmsodGhpcy5lZGl0b3IoKSk7XG4gIH1cblxuICBvbkFwcGx5KGV2ZW50OiBFdmVudCkge1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgY29uc3QgdXJsID0gdGhpcy5lZGl0VXJsKCkudHJpbSgpO1xuICAgIGlmICh1cmwpIHtcbiAgICAgIHRoaXMubGlua1N2Yy5zZXRMaW5rKHRoaXMuZWRpdG9yKCksIHVybCk7XG4gICAgICB0aGlzLmVkaXRVcmwuc2V0KFwiXCIpO1xuICAgICAgdGhpcy5saW5rU3ZjLnNldEludGVyYWN0aW5nKGZhbHNlKTtcbiAgICAgIHRoaXMuaGlkZVRpcHB5KCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMub25SZW1vdmUoZXZlbnQpO1xuICAgIH1cbiAgfVxuXG4gIG9uQ2FuY2VsKGV2ZW50OiBFdmVudCkge1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgdGhpcy5saW5rU3ZjLmNsb3NlKCk7XG4gICAgdGhpcy5oaWRlVGlwcHkoKTtcbiAgfVxufVxuIl19