@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.
- package/CHANGELOG.md +23 -0
- package/README.md +132 -93
- package/esm2022/flogeez-angular-tiptap-editor.mjs +5 -0
- package/esm2022/lib/ate-editor.provider.mjs +38 -0
- package/esm2022/lib/components/bubble-menus/base/ate-base-bubble-menu.mjs +207 -0
- package/esm2022/lib/components/bubble-menus/base/ate-base-sub-bubble-menu.mjs +133 -0
- package/esm2022/lib/components/bubble-menus/color/ate-color-bubble-menu.component.mjs +365 -0
- package/esm2022/lib/components/bubble-menus/image/ate-image-bubble-menu.component.mjs +226 -0
- package/esm2022/lib/components/bubble-menus/link/ate-link-bubble-menu.component.mjs +247 -0
- package/esm2022/lib/components/bubble-menus/table/ate-cell-bubble-menu.component.mjs +121 -0
- package/esm2022/lib/components/bubble-menus/table/ate-table-bubble-menu.component.mjs +266 -0
- package/esm2022/lib/components/bubble-menus/text/ate-bubble-menu.component.mjs +294 -0
- package/esm2022/lib/components/color-picker/ate-color-picker.component.mjs +122 -0
- package/esm2022/lib/components/edit-toggle/ate-edit-toggle.component.mjs +42 -0
- package/esm2022/lib/components/editor/angular-tiptap-editor.component.mjs +953 -0
- package/esm2022/lib/components/slash-commands/ate-slash-commands.component.mjs +329 -0
- package/esm2022/lib/components/toolbar/ate-toolbar.component.mjs +531 -0
- package/esm2022/lib/components/ui/ate-button.component.mjs +90 -0
- package/esm2022/lib/components/ui/ate-separator.component.mjs +31 -0
- package/esm2022/lib/config/ate-editor.config.mjs +76 -0
- package/esm2022/lib/config/ate-global-config.token.mjs +6 -0
- package/esm2022/lib/config/ate-slash-commands.config.mjs +130 -0
- package/esm2022/lib/directives/ate-noop-value-accessor.directive.mjs +40 -0
- package/esm2022/lib/extensions/ate-link-click-behavior.extension.mjs +32 -0
- package/esm2022/lib/extensions/ate-resizable-image.extension.mjs +270 -0
- package/esm2022/lib/extensions/ate-table.extension.mjs +29 -0
- package/esm2022/lib/extensions/ate-tiptap-state.extension.mjs +85 -0
- package/esm2022/lib/extensions/ate-upload-progress.extension.mjs +195 -0
- package/esm2022/lib/extensions/calculators/ate-discovery.calculator.mjs +62 -0
- package/esm2022/lib/extensions/calculators/ate-image.calculator.mjs +8 -0
- package/esm2022/lib/extensions/calculators/ate-marks.calculator.mjs +75 -0
- package/esm2022/lib/extensions/calculators/ate-selection.calculator.mjs +35 -0
- package/esm2022/lib/extensions/calculators/ate-structure.calculator.mjs +34 -0
- package/esm2022/lib/extensions/calculators/ate-table.calculator.mjs +38 -0
- package/esm2022/lib/models/ate-bubble-menu.model.mjs +49 -0
- package/esm2022/lib/models/ate-editor-config.model.mjs +2 -0
- package/esm2022/lib/models/ate-editor-state.model.mjs +90 -0
- package/esm2022/lib/models/ate-image.model.mjs +2 -0
- package/esm2022/lib/models/ate-toolbar.model.mjs +35 -0
- package/esm2022/lib/node-view/ate-angular-node-view.mjs +97 -0
- package/esm2022/lib/node-view/ate-injector.registry.mjs +16 -0
- package/esm2022/lib/node-view/ate-node-view.factory.mjs +125 -0
- package/esm2022/lib/node-view/ate-node-view.models.mjs +2 -0
- package/esm2022/lib/node-view/ate-node-view.renderer.mjs +154 -0
- package/esm2022/lib/node-view/ate-register-angular-component.mjs +49 -0
- package/esm2022/lib/services/ate-color-picker.service.mjs +193 -0
- package/esm2022/lib/services/ate-editor-commands.service.mjs +465 -0
- package/esm2022/lib/services/ate-i18n.service.mjs +410 -0
- package/esm2022/lib/services/ate-image.service.mjs +368 -0
- package/esm2022/lib/services/ate-link.service.mjs +97 -0
- package/esm2022/lib/utils/ate-color.utils.mjs +60 -0
- package/esm2022/public-api.mjs +93 -0
- package/fesm2022/flogeez-angular-tiptap-editor.mjs +1178 -1100
- package/fesm2022/flogeez-angular-tiptap-editor.mjs.map +1 -1
- package/index.d.ts +3 -1562
- package/lib/ate-editor.provider.d.ts +21 -0
- package/lib/components/bubble-menus/base/ate-base-bubble-menu.d.ts +71 -0
- package/lib/components/bubble-menus/base/ate-base-sub-bubble-menu.d.ts +60 -0
- package/lib/components/bubble-menus/color/ate-color-bubble-menu.component.d.ts +77 -0
- package/lib/components/bubble-menus/image/ate-image-bubble-menu.component.d.ts +52 -0
- package/lib/components/bubble-menus/link/ate-link-bubble-menu.component.d.ts +55 -0
- package/lib/components/bubble-menus/table/ate-cell-bubble-menu.component.d.ts +12 -0
- package/lib/components/bubble-menus/table/ate-table-bubble-menu.component.d.ts +24 -0
- package/lib/components/bubble-menus/text/ate-bubble-menu.component.d.ts +53 -0
- package/lib/components/color-picker/ate-color-picker.component.d.ts +54 -0
- package/lib/components/edit-toggle/ate-edit-toggle.component.d.ts +13 -0
- package/lib/components/editor/angular-tiptap-editor.component.d.ts +235 -0
- package/lib/components/slash-commands/ate-slash-commands.component.d.ts +43 -0
- package/lib/components/toolbar/ate-toolbar.component.d.ts +47 -0
- package/lib/components/ui/ate-button.component.d.ts +25 -0
- package/lib/components/ui/ate-separator.component.d.ts +7 -0
- package/lib/config/ate-editor.config.d.ts +7 -0
- package/lib/config/ate-global-config.token.d.ts +6 -0
- package/lib/config/ate-slash-commands.config.d.ts +41 -0
- package/lib/directives/ate-noop-value-accessor.directive.d.ts +13 -0
- package/lib/extensions/ate-link-click-behavior.extension.d.ts +2 -0
- package/lib/extensions/ate-resizable-image.extension.d.ts +27 -0
- package/lib/extensions/ate-table.extension.d.ts +2 -0
- package/lib/extensions/ate-tiptap-state.extension.d.ts +7 -0
- package/lib/extensions/ate-upload-progress.extension.d.ts +7 -0
- package/lib/extensions/calculators/ate-discovery.calculator.d.ts +7 -0
- package/lib/extensions/calculators/ate-image.calculator.d.ts +2 -0
- package/lib/extensions/calculators/ate-marks.calculator.d.ts +2 -0
- package/lib/extensions/calculators/ate-selection.calculator.d.ts +2 -0
- package/lib/extensions/calculators/ate-structure.calculator.d.ts +2 -0
- package/lib/extensions/calculators/ate-table.calculator.d.ts +2 -0
- package/lib/models/ate-bubble-menu.model.d.ts +33 -0
- package/lib/models/ate-editor-config.model.d.ts +100 -0
- package/lib/models/ate-editor-state.model.d.ts +109 -0
- package/lib/models/ate-image.model.d.ts +104 -0
- package/lib/models/ate-toolbar.model.d.ts +18 -0
- package/lib/node-view/ate-angular-node-view.d.ts +115 -0
- package/lib/node-view/ate-injector.registry.d.ts +3 -0
- package/lib/node-view/ate-node-view.factory.d.ts +10 -0
- package/lib/node-view/ate-node-view.models.d.ts +138 -0
- package/lib/node-view/ate-node-view.renderer.d.ts +23 -0
- package/lib/node-view/ate-register-angular-component.d.ts +35 -0
- package/lib/services/ate-color-picker.service.d.ts +65 -0
- package/lib/services/ate-editor-commands.service.d.ts +72 -0
- package/lib/services/ate-i18n.service.d.ts +394 -0
- package/lib/services/ate-image.service.d.ts +71 -0
- package/lib/services/ate-link.service.d.ts +42 -0
- package/lib/utils/ate-color.utils.d.ts +12 -0
- package/package.json +3 -1
- 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
|