fable-editor 1.0.2 → 1.2.0
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/README.md +55 -6
- package/dist/angular/README.md +55 -6
- package/dist/angular/angular/fable-editor.component.d.ts +4 -1
- package/dist/angular/core/config.d.ts +15 -0
- package/dist/angular/core/editor.d.ts +107 -0
- package/dist/angular/core/i18n.d.ts +49 -0
- package/dist/angular/core/types.d.ts +21 -0
- package/dist/angular/esm2022/angular/fable-editor.component.mjs +14 -2
- package/dist/angular/esm2022/core/config.mjs +215 -24
- package/dist/angular/esm2022/core/editor.mjs +1255 -74
- package/dist/angular/esm2022/core/i18n.mjs +101 -3
- package/dist/angular/esm2022/core/icons.mjs +12 -2
- package/dist/angular/esm2022/core/types.mjs +1 -1
- package/dist/angular/fesm2022/fable-editor.mjs +1591 -99
- package/dist/angular/fesm2022/fable-editor.mjs.map +1 -1
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/editor.css +452 -57
- package/dist/core/editor.d.ts +107 -0
- package/dist/core/editor.d.ts.map +1 -1
- package/dist/core/i18n.d.ts +49 -0
- package/dist/core/i18n.d.ts.map +1 -1
- package/dist/core/icons.d.ts.map +1 -1
- package/dist/core/index.cjs +4 -4
- package/dist/core/index.mjs +2580 -703
- package/dist/core/types.d.ts +21 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/react/FableEditor.d.ts.map +1 -1
- package/dist/react/index.cjs +13 -13
- package/dist/react/index.mjs +2982 -1136
- package/package.json +1 -1
- package/style.css +452 -57
package/README.md
CHANGED
|
@@ -7,14 +7,18 @@ A rich text editor packaged as an npm library with first-class React and Angular
|
|
|
7
7
|
| Feature | Details |
|
|
8
8
|
|---|---|
|
|
9
9
|
| Toolbar & menubar | Configurable via `toolbar`/`menubar` strings (`\|`-separated groups), or use the built-in defaults. Menus: File, Edit, View, Insert, Format, Tools, Table, Help |
|
|
10
|
-
|
|
|
10
|
+
| Responsive toolbar | On narrow screens (mobile/tablet) — or in a narrow host container — the toolbar collapses to a single row with a trailing `…` button that expands/collapses the full set. Automatic, width-driven, no configuration needed |
|
|
11
|
+
| Tables | Resize handles, row/column insert & delete, row/column reordering (move a row up/down or a column left/right from the context toolbar or Table menu), cell background color picker (build header rows in one click), cell/row/column/table properties, context toolbar |
|
|
11
12
|
| Paste handling | PowerPaste-style clean paste from Word / Google Docs / Excel |
|
|
12
13
|
| Internationalization | English / Arabic, automatic RTL/LTR switching |
|
|
13
|
-
| Fonts & formatting | Configurable font list (`fontFamilyFormats`), sizes, line-height, word-spacing, text/background color, change case |
|
|
14
|
+
| Fonts & formatting | Configurable font list (`fontFamilyFormats`), sizes, line-height, word-spacing, letter-spacing, text/background color, change case |
|
|
15
|
+
| Theming | `primaryColor` (accent, default `#df3c2b`), `toolbarGroupBackground` and `uiFontFamily` (default Noto Sans stack) options restyle the editor chrome via CSS variables |
|
|
14
16
|
| Custom content styling | `contentStyle` injects scoped CSS into the editable area (e.g. default font/size) |
|
|
15
17
|
| Images | Placeholder upload UI, drag-and-drop, configurable accepted file types (`imageFileTypes`), pluggable async upload handler |
|
|
18
|
+
| Video | Insert/edit dialog with **General** (source URL / upload / width / height), **Embed** (paste embed code) and **Advanced** (alternative source, poster image) tabs. YouTube / Vimeo / Dailymotion page URLs become embedded players — including when pasted directly into the editor. Template media slots can host a video instead of an image. |
|
|
19
|
+
| Code | Inline `code` (toolbar button, or type `` `snippet` `` — converts on the closing backtick) and code sample blocks: a dialog with language dropdown + paste-in textarea inserts a styled block with a language header; click a block to edit, copy or delete it. No dependencies, no highlighting engine |
|
|
16
20
|
| Documents | Import `.docx` files, source-code view, print preview |
|
|
17
|
-
| Productivity | Undo/redo, revision history, autosave draft restore, word count, special characters, page breaks |
|
|
21
|
+
| Productivity | Undo/redo, revision history, autosave draft restore, word count, special characters & emoji pickers (category tabs + glyph grid), page breaks |
|
|
18
22
|
| Fullscreen | Toggle fullscreen editing |
|
|
19
23
|
| Help | Built-in shortcuts/help dialog |
|
|
20
24
|
|
|
@@ -68,9 +72,46 @@ editor.destroy();
|
|
|
68
72
|
| `imageFileTypes` | `string[]` | common image MIME types | `accept` list for the native image file picker. |
|
|
69
73
|
| `imageUploadHandler` | `(file: File) => Promise<string>` | — | Resolve with a URL after uploading; omit to inline images as base64. |
|
|
70
74
|
| `onImageUploadError` | `(error, file) => void` | — | Called when `imageUploadHandler` rejects. |
|
|
75
|
+
| `videoFileTypes` | `string[]` | `['video/mp4','video/webm','video/ogg']` | `accept` list for the native video file picker. |
|
|
76
|
+
| `videoUploadHandler` | `(file: File) => Promise<string>` | — | Resolve with a URL after uploading; omit to inline videos as base64. |
|
|
77
|
+
| `onVideoUploadError` | `(error, file) => void` | — | Called when `videoUploadHandler` rejects. |
|
|
78
|
+
| `primaryColor` | `string` | `'#df3c2b'` | Accent color for active states, primary buttons and selection outlines. Any CSS color. Applied as the `--fable-primary` CSS variable. |
|
|
79
|
+
| `toolbarGroupBackground` | `string` | `'#f1f3f6'` | Background of each toolbar button group (the rounded pills). Applied as `--fable-tgrp-bg`. |
|
|
80
|
+
| `uiFontFamily` | `string` | Noto Sans stack | Font family for the editor UI (menubar, toolbar, dialogs). Load the Noto Sans webfont in your app to use the default, or pass your own family. Applied as `--fable-ui-font`. |
|
|
71
81
|
| `draftKey` | `string` | current page path | Storage key suffix for autosaved drafts. |
|
|
72
82
|
| `onChange` / `onReady` | functions | — | Content-change and ready callbacks (usually set by the React/Angular wrapper instead). |
|
|
73
83
|
|
|
84
|
+
### Theming
|
|
85
|
+
|
|
86
|
+
The editor's accent color, toolbar group background and UI font are driven by CSS variables, so they can be set per instance:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
new FableEditor({
|
|
90
|
+
target: el,
|
|
91
|
+
primaryColor: '#df3c2b', // accent (default)
|
|
92
|
+
toolbarGroupBackground: '#f1f3f6', // toolbar pill background (default)
|
|
93
|
+
uiFontFamily: "'Noto Sans', sans-serif" // UI font (default stack)
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The same values are available as React props / Angular inputs. The UI font defaults to a **Noto Sans** stack — load the webfont in your app (e.g. from Google Fonts) or pass any family you've already loaded. Alternatively, set the variables yourself in CSS: `--fable-primary`, `--fable-tgrp-bg`, `--fable-ui-font`.
|
|
98
|
+
|
|
99
|
+
### Toolbar & menubar configuration
|
|
100
|
+
|
|
101
|
+
The `toolbar`/`menubar` options accept the same style of layout string as TinyMCE — `|` separates visual groups, spaces separate items:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
new FableEditor({
|
|
105
|
+
target: document.querySelector('#default-editor')!,
|
|
106
|
+
toolbar:
|
|
107
|
+
'undo redo | styles | bold italic underline strikethrough | ' +
|
|
108
|
+
'alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
|
|
109
|
+
menubar: 'file edit insert format'
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Available toolbar items: `undo redo preview print importword revhistory fontfamily fontsize fontsizeincrease fontsizedecrease bold italic underline strikethrough forecolor backcolor alignleft aligncenter alignright alignjustify bullist numlist outdent indent link blockquote code changecase lineheight wordspacing letterspacing removeformat blocks ltr rtl quickimage quickvideo quicktable codesample template charmap emoji fullscreen sourcecode`. The TinyMCE names `styles` (→ `blocks`), `image` (→ `quickimage`), `media` (→ `quickvideo`) and `table` (→ `quicktable`) are accepted as aliases, so a typical TinyMCE toolbar string works unchanged. Unknown tokens are skipped with a console warning. Menubar keys: `file edit view insert format tools table help`.
|
|
114
|
+
|
|
74
115
|
## React
|
|
75
116
|
|
|
76
117
|
```tsx
|
|
@@ -86,12 +127,15 @@ function App() {
|
|
|
86
127
|
onChange={setValue}
|
|
87
128
|
language="en"
|
|
88
129
|
height={400}
|
|
130
|
+
toolbar="undo redo | styles | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
|
|
89
131
|
/>
|
|
90
132
|
);
|
|
91
133
|
}
|
|
92
134
|
```
|
|
93
135
|
|
|
94
|
-
|
|
136
|
+
`toolbar`/`menubar` accept the same layout strings as the core option (see [Toolbar & menubar configuration](#toolbar--menubar-configuration)), so each app can show exactly the controls it needs.
|
|
137
|
+
|
|
138
|
+
Props: `value`, `defaultValue`, `onChange`, `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `primaryColor`, `toolbarGroupBackground`, `uiFontFamily`, `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, `onImageUploadError`, `videoFileTypes`, `videoUploadHandler`, `onVideoUploadError`, `init`, `className`, `style` (see the [`init` options](#init-options) table above — every option is also a top-level prop). A ref exposes `getContent`, `setContent`, `insertContent`, `setLanguage`, `focus`, `destroy`.
|
|
95
139
|
|
|
96
140
|
## Angular
|
|
97
141
|
|
|
@@ -118,10 +162,15 @@ export class AppModule {}
|
|
|
118
162
|
Use in a template:
|
|
119
163
|
|
|
120
164
|
```html
|
|
121
|
-
<fable-editor
|
|
165
|
+
<fable-editor
|
|
166
|
+
[(ngModel)]="content"
|
|
167
|
+
language="en"
|
|
168
|
+
[height]="400"
|
|
169
|
+
toolbar="undo redo | styles | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image">
|
|
170
|
+
</fable-editor>
|
|
122
171
|
```
|
|
123
172
|
|
|
124
|
-
Inputs: `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `init`. `menubar`/`toolbar` accept a string directly (e.g. `[toolbar]="'undo redo | bold italic'"`); `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, and `
|
|
173
|
+
Inputs: `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `primaryColor`, `toolbarGroupBackground`, `uiFontFamily`, `init`. `menubar`/`toolbar` accept a string directly (e.g. `[toolbar]="'undo redo | bold italic'"`); `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, `onImageUploadError`, `videoFileTypes`, `videoUploadHandler`, and `onVideoUploadError` aren't top-level inputs — pass them via `[init]="{ contentStyle: '...' }"` (see the [`init` options](#init-options) table above). Outputs: `editorChange`, `editorReady`. Works with `ngModel` and `formControlName`.
|
|
125
174
|
|
|
126
175
|
## Development & testing
|
|
127
176
|
|
package/dist/angular/README.md
CHANGED
|
@@ -7,14 +7,18 @@ A rich text editor packaged as an npm library with first-class React and Angular
|
|
|
7
7
|
| Feature | Details |
|
|
8
8
|
|---|---|
|
|
9
9
|
| Toolbar & menubar | Configurable via `toolbar`/`menubar` strings (`\|`-separated groups), or use the built-in defaults. Menus: File, Edit, View, Insert, Format, Tools, Table, Help |
|
|
10
|
-
|
|
|
10
|
+
| Responsive toolbar | On narrow screens (mobile/tablet) — or in a narrow host container — the toolbar collapses to a single row with a trailing `…` button that expands/collapses the full set. Automatic, width-driven, no configuration needed |
|
|
11
|
+
| Tables | Resize handles, row/column insert & delete, row/column reordering (move a row up/down or a column left/right from the context toolbar or Table menu), cell background color picker (build header rows in one click), cell/row/column/table properties, context toolbar |
|
|
11
12
|
| Paste handling | PowerPaste-style clean paste from Word / Google Docs / Excel |
|
|
12
13
|
| Internationalization | English / Arabic, automatic RTL/LTR switching |
|
|
13
|
-
| Fonts & formatting | Configurable font list (`fontFamilyFormats`), sizes, line-height, word-spacing, text/background color, change case |
|
|
14
|
+
| Fonts & formatting | Configurable font list (`fontFamilyFormats`), sizes, line-height, word-spacing, letter-spacing, text/background color, change case |
|
|
15
|
+
| Theming | `primaryColor` (accent, default `#df3c2b`), `toolbarGroupBackground` and `uiFontFamily` (default Noto Sans stack) options restyle the editor chrome via CSS variables |
|
|
14
16
|
| Custom content styling | `contentStyle` injects scoped CSS into the editable area (e.g. default font/size) |
|
|
15
17
|
| Images | Placeholder upload UI, drag-and-drop, configurable accepted file types (`imageFileTypes`), pluggable async upload handler |
|
|
18
|
+
| Video | Insert/edit dialog with **General** (source URL / upload / width / height), **Embed** (paste embed code) and **Advanced** (alternative source, poster image) tabs. YouTube / Vimeo / Dailymotion page URLs become embedded players — including when pasted directly into the editor. Template media slots can host a video instead of an image. |
|
|
19
|
+
| Code | Inline `code` (toolbar button, or type `` `snippet` `` — converts on the closing backtick) and code sample blocks: a dialog with language dropdown + paste-in textarea inserts a styled block with a language header; click a block to edit, copy or delete it. No dependencies, no highlighting engine |
|
|
16
20
|
| Documents | Import `.docx` files, source-code view, print preview |
|
|
17
|
-
| Productivity | Undo/redo, revision history, autosave draft restore, word count, special characters, page breaks |
|
|
21
|
+
| Productivity | Undo/redo, revision history, autosave draft restore, word count, special characters & emoji pickers (category tabs + glyph grid), page breaks |
|
|
18
22
|
| Fullscreen | Toggle fullscreen editing |
|
|
19
23
|
| Help | Built-in shortcuts/help dialog |
|
|
20
24
|
|
|
@@ -68,9 +72,46 @@ editor.destroy();
|
|
|
68
72
|
| `imageFileTypes` | `string[]` | common image MIME types | `accept` list for the native image file picker. |
|
|
69
73
|
| `imageUploadHandler` | `(file: File) => Promise<string>` | — | Resolve with a URL after uploading; omit to inline images as base64. |
|
|
70
74
|
| `onImageUploadError` | `(error, file) => void` | — | Called when `imageUploadHandler` rejects. |
|
|
75
|
+
| `videoFileTypes` | `string[]` | `['video/mp4','video/webm','video/ogg']` | `accept` list for the native video file picker. |
|
|
76
|
+
| `videoUploadHandler` | `(file: File) => Promise<string>` | — | Resolve with a URL after uploading; omit to inline videos as base64. |
|
|
77
|
+
| `onVideoUploadError` | `(error, file) => void` | — | Called when `videoUploadHandler` rejects. |
|
|
78
|
+
| `primaryColor` | `string` | `'#df3c2b'` | Accent color for active states, primary buttons and selection outlines. Any CSS color. Applied as the `--fable-primary` CSS variable. |
|
|
79
|
+
| `toolbarGroupBackground` | `string` | `'#f1f3f6'` | Background of each toolbar button group (the rounded pills). Applied as `--fable-tgrp-bg`. |
|
|
80
|
+
| `uiFontFamily` | `string` | Noto Sans stack | Font family for the editor UI (menubar, toolbar, dialogs). Load the Noto Sans webfont in your app to use the default, or pass your own family. Applied as `--fable-ui-font`. |
|
|
71
81
|
| `draftKey` | `string` | current page path | Storage key suffix for autosaved drafts. |
|
|
72
82
|
| `onChange` / `onReady` | functions | — | Content-change and ready callbacks (usually set by the React/Angular wrapper instead). |
|
|
73
83
|
|
|
84
|
+
### Theming
|
|
85
|
+
|
|
86
|
+
The editor's accent color, toolbar group background and UI font are driven by CSS variables, so they can be set per instance:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
new FableEditor({
|
|
90
|
+
target: el,
|
|
91
|
+
primaryColor: '#df3c2b', // accent (default)
|
|
92
|
+
toolbarGroupBackground: '#f1f3f6', // toolbar pill background (default)
|
|
93
|
+
uiFontFamily: "'Noto Sans', sans-serif" // UI font (default stack)
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The same values are available as React props / Angular inputs. The UI font defaults to a **Noto Sans** stack — load the webfont in your app (e.g. from Google Fonts) or pass any family you've already loaded. Alternatively, set the variables yourself in CSS: `--fable-primary`, `--fable-tgrp-bg`, `--fable-ui-font`.
|
|
98
|
+
|
|
99
|
+
### Toolbar & menubar configuration
|
|
100
|
+
|
|
101
|
+
The `toolbar`/`menubar` options accept the same style of layout string as TinyMCE — `|` separates visual groups, spaces separate items:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
new FableEditor({
|
|
105
|
+
target: document.querySelector('#default-editor')!,
|
|
106
|
+
toolbar:
|
|
107
|
+
'undo redo | styles | bold italic underline strikethrough | ' +
|
|
108
|
+
'alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
|
|
109
|
+
menubar: 'file edit insert format'
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Available toolbar items: `undo redo preview print importword revhistory fontfamily fontsize fontsizeincrease fontsizedecrease bold italic underline strikethrough forecolor backcolor alignleft aligncenter alignright alignjustify bullist numlist outdent indent link blockquote code changecase lineheight wordspacing letterspacing removeformat blocks ltr rtl quickimage quickvideo quicktable codesample template charmap emoji fullscreen sourcecode`. The TinyMCE names `styles` (→ `blocks`), `image` (→ `quickimage`), `media` (→ `quickvideo`) and `table` (→ `quicktable`) are accepted as aliases, so a typical TinyMCE toolbar string works unchanged. Unknown tokens are skipped with a console warning. Menubar keys: `file edit view insert format tools table help`.
|
|
114
|
+
|
|
74
115
|
## React
|
|
75
116
|
|
|
76
117
|
```tsx
|
|
@@ -86,12 +127,15 @@ function App() {
|
|
|
86
127
|
onChange={setValue}
|
|
87
128
|
language="en"
|
|
88
129
|
height={400}
|
|
130
|
+
toolbar="undo redo | styles | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
|
|
89
131
|
/>
|
|
90
132
|
);
|
|
91
133
|
}
|
|
92
134
|
```
|
|
93
135
|
|
|
94
|
-
|
|
136
|
+
`toolbar`/`menubar` accept the same layout strings as the core option (see [Toolbar & menubar configuration](#toolbar--menubar-configuration)), so each app can show exactly the controls it needs.
|
|
137
|
+
|
|
138
|
+
Props: `value`, `defaultValue`, `onChange`, `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `primaryColor`, `toolbarGroupBackground`, `uiFontFamily`, `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, `onImageUploadError`, `videoFileTypes`, `videoUploadHandler`, `onVideoUploadError`, `init`, `className`, `style` (see the [`init` options](#init-options) table above — every option is also a top-level prop). A ref exposes `getContent`, `setContent`, `insertContent`, `setLanguage`, `focus`, `destroy`.
|
|
95
139
|
|
|
96
140
|
## Angular
|
|
97
141
|
|
|
@@ -118,10 +162,15 @@ export class AppModule {}
|
|
|
118
162
|
Use in a template:
|
|
119
163
|
|
|
120
164
|
```html
|
|
121
|
-
<fable-editor
|
|
165
|
+
<fable-editor
|
|
166
|
+
[(ngModel)]="content"
|
|
167
|
+
language="en"
|
|
168
|
+
[height]="400"
|
|
169
|
+
toolbar="undo redo | styles | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image">
|
|
170
|
+
</fable-editor>
|
|
122
171
|
```
|
|
123
172
|
|
|
124
|
-
Inputs: `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `init`. `menubar`/`toolbar` accept a string directly (e.g. `[toolbar]="'undo redo | bold italic'"`); `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, and `
|
|
173
|
+
Inputs: `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `primaryColor`, `toolbarGroupBackground`, `uiFontFamily`, `init`. `menubar`/`toolbar` accept a string directly (e.g. `[toolbar]="'undo redo | bold italic'"`); `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, `onImageUploadError`, `videoFileTypes`, `videoUploadHandler`, and `onVideoUploadError` aren't top-level inputs — pass them via `[init]="{ contentStyle: '...' }"` (see the [`init` options](#init-options) table above). Outputs: `editorChange`, `editorReady`. Works with `ngModel` and `formControlName`.
|
|
125
174
|
|
|
126
175
|
## Development & testing
|
|
127
176
|
|
|
@@ -9,6 +9,9 @@ export declare class FableEditorComponent implements OnInit, OnChanges, OnDestro
|
|
|
9
9
|
toolbar: boolean | string;
|
|
10
10
|
statusbar: boolean;
|
|
11
11
|
readonly: boolean;
|
|
12
|
+
primaryColor?: string;
|
|
13
|
+
toolbarGroupBackground?: string;
|
|
14
|
+
uiFontFamily?: string;
|
|
12
15
|
init: Partial<Omit<EditorInitOptions, 'target'>>;
|
|
13
16
|
value: string;
|
|
14
17
|
editorChange: EventEmitter<string>;
|
|
@@ -25,5 +28,5 @@ export declare class FableEditorComponent implements OnInit, OnChanges, OnDestro
|
|
|
25
28
|
focus(): void;
|
|
26
29
|
writeValue(value: string): void;
|
|
27
30
|
static ɵfac: i0.ɵɵFactoryDeclaration<FableEditorComponent, never>;
|
|
28
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<FableEditorComponent, "fable-editor", never, { "language": { "alias": "language"; "required": false; }; "height": { "alias": "height"; "required": false; }; "menubar": { "alias": "menubar"; "required": false; }; "toolbar": { "alias": "toolbar"; "required": false; }; "statusbar": { "alias": "statusbar"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "init": { "alias": "init"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "editorChange": "editorChange"; "editorReady": "editorReady"; }, never, never, true, never>;
|
|
31
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FableEditorComponent, "fable-editor", never, { "language": { "alias": "language"; "required": false; }; "height": { "alias": "height"; "required": false; }; "menubar": { "alias": "menubar"; "required": false; }; "toolbar": { "alias": "toolbar"; "required": false; }; "statusbar": { "alias": "statusbar"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "primaryColor": { "alias": "primaryColor"; "required": false; }; "toolbarGroupBackground": { "alias": "toolbarGroupBackground"; "required": false; }; "uiFontFamily": { "alias": "uiFontFamily"; "required": false; }; "init": { "alias": "init"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "editorChange": "editorChange"; "editorReady": "editorReady"; }, never, never, true, never>;
|
|
29
32
|
}
|
|
@@ -10,8 +10,23 @@ export declare const MIN_FONT_PX = 1;
|
|
|
10
10
|
export declare const MAX_FONT_PX = 100;
|
|
11
11
|
export declare const LINE_HEIGHTS: string[];
|
|
12
12
|
export declare const WORD_SPACINGS: string[];
|
|
13
|
+
export declare const LETTER_SPACINGS: string[];
|
|
14
|
+
/** Languages offered by the code-sample dialog: [value stored in data-lang, label]. */
|
|
15
|
+
export declare const CODE_LANGS: [string, string][];
|
|
13
16
|
export declare const BLOCKS: [string, string][];
|
|
14
17
|
/** Quick-access text color chips shown directly in the toolbar. */
|
|
15
18
|
export declare const QUICK_COLORS: string[];
|
|
19
|
+
/** 25 swatches — a 5×5 grid in the picker (pastels, brights, darks, grays,
|
|
20
|
+
* then pink/brown/cyan + black/white). Shared by fore/back color and cell fill. */
|
|
16
21
|
export declare const COLORS: string[];
|
|
22
|
+
/** Category key = i18n string key for the tab label in the picker dialogs. */
|
|
23
|
+
export interface GlyphCategory {
|
|
24
|
+
key: string;
|
|
25
|
+
chars: string[];
|
|
26
|
+
}
|
|
27
|
+
/** Special-character map, grouped for the two-pane picker (tabs left, glyphs right).
|
|
28
|
+
* `CHARS` below is the legacy flat list and is kept exported for back-compat. */
|
|
29
|
+
export declare const CHAR_CATEGORIES: GlyphCategory[];
|
|
30
|
+
/** Emoji map for the emoji picker — same two-pane dialog as the character map. */
|
|
31
|
+
export declare const EMOJI_CATEGORIES: GlyphCategory[];
|
|
17
32
|
export declare const CHARS: string[];
|
|
@@ -37,7 +37,20 @@ export declare class FableEditor implements FableEditorApi {
|
|
|
37
37
|
private imageUploadHandler?;
|
|
38
38
|
private onImageUploadError?;
|
|
39
39
|
private contentStyle?;
|
|
40
|
+
private vidInput;
|
|
41
|
+
private vidActive;
|
|
42
|
+
private vidCtx;
|
|
43
|
+
private vidHideTimer;
|
|
44
|
+
private vphActive;
|
|
45
|
+
private vphCtx;
|
|
46
|
+
private vphUploadTarget;
|
|
47
|
+
private videoUploadHandler?;
|
|
48
|
+
private onVideoUploadError?;
|
|
40
49
|
private selCtx;
|
|
50
|
+
private tbrMore;
|
|
51
|
+
private tbrExpanded;
|
|
52
|
+
private codeActive;
|
|
53
|
+
private codeCtx;
|
|
41
54
|
private tplActive;
|
|
42
55
|
private tplCtx;
|
|
43
56
|
private docInput;
|
|
@@ -52,6 +65,10 @@ export declare class FableEditor implements FableEditorApi {
|
|
|
52
65
|
private tArr;
|
|
53
66
|
private dir;
|
|
54
67
|
private initDOM;
|
|
68
|
+
/** Theme options land as CSS variables on the document root rather than the editor
|
|
69
|
+
* shell, because dialogs, popups and floating context toolbars are appended to
|
|
70
|
+
* document.body and would not inherit variables scoped to the shell. */
|
|
71
|
+
private applyTheme;
|
|
55
72
|
/** Rewrites the literal word `body` in a user-supplied `contentStyle` string to a
|
|
56
73
|
* selector scoped to this instance's content div. Everything else passes through
|
|
57
74
|
* verbatim, since `.ed` is a plain div in the host page (not an iframe) and an
|
|
@@ -100,6 +117,10 @@ export declare class FableEditor implements FableEditorApi {
|
|
|
100
117
|
private tsel;
|
|
101
118
|
private setListStyle;
|
|
102
119
|
private listMenu;
|
|
120
|
+
/** Shared color-picker popup body — swatch grid plus the current-color /
|
|
121
|
+
* no-color / custom-picker footer — so the fore/back toolbar menus and the
|
|
122
|
+
* table cell-background menu all show the identical popup. */
|
|
123
|
+
private buildPaletteInto;
|
|
103
124
|
private colorMenu;
|
|
104
125
|
private applyColor;
|
|
105
126
|
private syncColorSwatches;
|
|
@@ -117,6 +138,7 @@ export declare class FableEditor implements FableEditorApi {
|
|
|
117
138
|
private currentBlockStyle;
|
|
118
139
|
private lineHeightMenu;
|
|
119
140
|
private wordSpacingMenu;
|
|
141
|
+
private letterSpacingMenu;
|
|
120
142
|
private closestAnchor;
|
|
121
143
|
private linkDlg;
|
|
122
144
|
private blocksMenu;
|
|
@@ -135,9 +157,31 @@ export declare class FableEditor implements FableEditorApi {
|
|
|
135
157
|
* (the quick color chips); everything else yields one. */
|
|
136
158
|
private buildToolbarRegistry;
|
|
137
159
|
private buildToolbar;
|
|
160
|
+
/** xs/sm/md editors (< 992px wide) collapse an overflowing toolbar to one row +
|
|
161
|
+
* "…" expander; at lg widths and up the toolbar wraps to multiple rows as before. */
|
|
162
|
+
private static readonly TBR_COLLAPSE_MAX;
|
|
163
|
+
/** Responsive one-line toolbar: on mobile/tablet widths (or a narrow host
|
|
164
|
+
* container) a toolbar that can't fit on a single row collapses to one
|
|
165
|
+
* clipped row plus a trailing "…" button that expands/collapses the full
|
|
166
|
+
* set. Driven by the editor's own width, so it also applies to narrow
|
|
167
|
+
* panels on desktop. */
|
|
168
|
+
private updateToolbarOverflow;
|
|
138
169
|
private mItem;
|
|
139
170
|
private buildMenubar;
|
|
140
171
|
private tableOp;
|
|
172
|
+
/** Reorders the current row/column: 'rowup'/'rowdown' swap the caret's row with
|
|
173
|
+
* its neighbour, 'colleft'/'colright' move the caret's column one position in
|
|
174
|
+
* DOM order across every row. The caret travels with the moved cell. */
|
|
175
|
+
private tableMove;
|
|
176
|
+
/** Align the whole table via auto margins — same semantics as the align
|
|
177
|
+
* field in the table-properties dialog. */
|
|
178
|
+
private alignTable;
|
|
179
|
+
/** Color grid applying a background to the caret's cell — the direct way to
|
|
180
|
+
* build header rows (the toolbar's backcolor highlights text, not cells). */
|
|
181
|
+
private buildCellFillInto;
|
|
182
|
+
/** style.backgroundColor reads back as rgb(...) — normalise to #rrggbb for the picker. */
|
|
183
|
+
private cssToHex;
|
|
184
|
+
private cellFillMenu;
|
|
141
185
|
private applyTableProps;
|
|
142
186
|
private tablePropsDlg;
|
|
143
187
|
private currentCell;
|
|
@@ -156,7 +200,11 @@ export declare class FableEditor implements FableEditorApi {
|
|
|
156
200
|
private helpDlg;
|
|
157
201
|
private wordCountDlg;
|
|
158
202
|
private previewDlg;
|
|
203
|
+
/** Two-pane picker shared by the special-character map and the emoji map:
|
|
204
|
+
* fixed category tabs on the left, the active category's glyphs on the right. */
|
|
205
|
+
private glyphPickerDlg;
|
|
159
206
|
private charMap;
|
|
207
|
+
private emojiMap;
|
|
160
208
|
private printDoc;
|
|
161
209
|
private pickImage;
|
|
162
210
|
private imgPhHTML;
|
|
@@ -165,6 +213,9 @@ export declare class FableEditor implements FableEditorApi {
|
|
|
165
213
|
private removeImgPlaceholder;
|
|
166
214
|
private selectImgPlaceholder;
|
|
167
215
|
private renderPhCtxButtons;
|
|
216
|
+
/** Swaps a template slot's placeholder between image and video kinds, so the
|
|
217
|
+
* same media slot can be filled with either. */
|
|
218
|
+
private swapPlaceholderKind;
|
|
168
219
|
private renderPhCtxUrlInput;
|
|
169
220
|
private readImageFileInto;
|
|
170
221
|
/** Runs the host-supplied imageUploadHandler instead of inlining base64,
|
|
@@ -172,6 +223,62 @@ export declare class FableEditor implements FableEditorApi {
|
|
|
172
223
|
private uploadImageFile;
|
|
173
224
|
private replacePlaceholderWithImage;
|
|
174
225
|
private positionImgPhCtx;
|
|
226
|
+
private pickVideo;
|
|
227
|
+
private vidPhHTML;
|
|
228
|
+
private insertVideoPlaceholder;
|
|
229
|
+
private clearVphPlaceholderSel;
|
|
230
|
+
private removeVphPlaceholder;
|
|
231
|
+
private selectVphPlaceholder;
|
|
232
|
+
private renderVphCtxButtons;
|
|
233
|
+
private renderVphCtxUrlInput;
|
|
234
|
+
private readVideoFileInto;
|
|
235
|
+
/** Runs the host-supplied videoUploadHandler instead of inlining base64,
|
|
236
|
+
* so apps can route the file to S3 / Azure Blob / their own server. */
|
|
237
|
+
private uploadVideoFile;
|
|
238
|
+
private replacePlaceholderWithVideo;
|
|
239
|
+
/** Swaps a video placeholder for a hosted player iframe (YouTube & co.). */
|
|
240
|
+
private replacePlaceholderWithEmbed;
|
|
241
|
+
private positionVphCtx;
|
|
242
|
+
private cancelVidHide;
|
|
243
|
+
private clearVidHandles;
|
|
244
|
+
private selectVideo;
|
|
245
|
+
private buildVideoCtxToolbar;
|
|
246
|
+
private setVideoAlign;
|
|
247
|
+
private positionVidCtx;
|
|
248
|
+
/** Maps a page URL from a known video host to its iframe player URL. Returns
|
|
249
|
+
* null for anything else (e.g. direct .mp4 file URLs). */
|
|
250
|
+
private videoEmbedUrl;
|
|
251
|
+
private videoEmbedHTML;
|
|
252
|
+
/** Insert/edit-video dialog with General (source/width/height), Embed (embed
|
|
253
|
+
* code) and Advanced (alternative source, poster image) tabs. Hosted page
|
|
254
|
+
* URLs (YouTube & co.) become iframe players; anything else becomes a
|
|
255
|
+
* <video> tag. The General tab also offers the same upload path as before
|
|
256
|
+
* (videoUploadHandler when configured, else inline base64). */
|
|
257
|
+
private videoDlg;
|
|
258
|
+
private escapeAttr;
|
|
259
|
+
private escHtml;
|
|
260
|
+
/** The inline <code> run the caret sits in, if any (code blocks don't count). */
|
|
261
|
+
private closestInlineCode;
|
|
262
|
+
/** Wraps the selection in an inline <code> run, or unwraps the run the caret is
|
|
263
|
+
* in. Selections spanning multiple blocks are left alone (a <code> can't wrap
|
|
264
|
+
* across paragraphs without producing invalid markup). */
|
|
265
|
+
private toggleInlineCode;
|
|
266
|
+
/** Typing `snippet` converts to an inline <code> run the moment the closing
|
|
267
|
+
* backtick is typed, so code can be marked up without reaching for the toolbar. */
|
|
268
|
+
private tryCodeTyping;
|
|
269
|
+
/** Custom dropdown for dialog fields (a native <select> popup can't be styled):
|
|
270
|
+
* a field-styled button showing the current choice that opens a floating,
|
|
271
|
+
* scrollable option list. Read the current choice via `.value()`. */
|
|
272
|
+
private dlgSelect;
|
|
273
|
+
/** Insert/edit-code-sample dialog: language dropdown + monospace textarea to
|
|
274
|
+
* paste or type code into. Inserts a non-editable <pre class="code-block">
|
|
275
|
+
* whose header shows the language; click the block to edit / copy / delete. */
|
|
276
|
+
private codeDlg;
|
|
277
|
+
private clearCodeSel;
|
|
278
|
+
private selectCodeEl;
|
|
279
|
+
private buildCodeCtxToolbar;
|
|
280
|
+
private copyCodeBlock;
|
|
281
|
+
private positionCodeCtx;
|
|
175
282
|
private templateMenu;
|
|
176
283
|
/** Miniature div-based mockup of a layout, mirroring what the block looks
|
|
177
284
|
like in the editor: grey box = image slot, bars = heading/paragraph. */
|
|
@@ -38,9 +38,14 @@ export interface I18nStrings {
|
|
|
38
38
|
rowabove: string;
|
|
39
39
|
rowbelow: string;
|
|
40
40
|
delrow: string;
|
|
41
|
+
moverowup: string;
|
|
42
|
+
moverowdown: string;
|
|
41
43
|
colbefore: string;
|
|
42
44
|
colafter: string;
|
|
43
45
|
delcol: string;
|
|
46
|
+
movecolleft: string;
|
|
47
|
+
movecolright: string;
|
|
48
|
+
cellbg: string;
|
|
44
49
|
deltable: string;
|
|
45
50
|
ltr: string;
|
|
46
51
|
rtl: string;
|
|
@@ -87,6 +92,9 @@ export interface I18nStrings {
|
|
|
87
92
|
tblcellpadding: string;
|
|
88
93
|
tblborder: string;
|
|
89
94
|
tblalign: string;
|
|
95
|
+
tblalignleft: string;
|
|
96
|
+
tblaligncenter: string;
|
|
97
|
+
tblalignright: string;
|
|
90
98
|
alignnone: string;
|
|
91
99
|
quote: string;
|
|
92
100
|
link: string;
|
|
@@ -95,6 +103,7 @@ export interface I18nStrings {
|
|
|
95
103
|
unlink: string;
|
|
96
104
|
lineheight: string;
|
|
97
105
|
wordspacing: string;
|
|
106
|
+
letterspacing: string;
|
|
98
107
|
changecase: string;
|
|
99
108
|
lowercase: string;
|
|
100
109
|
uppercase: string;
|
|
@@ -140,6 +149,46 @@ export interface I18nStrings {
|
|
|
140
149
|
tplimgtop: string;
|
|
141
150
|
tplimgcenter: string;
|
|
142
151
|
deltemplate: string;
|
|
152
|
+
video: string;
|
|
153
|
+
quickvideo: string;
|
|
154
|
+
deletevideo: string;
|
|
155
|
+
uploadvideo: string;
|
|
156
|
+
videolink: string;
|
|
157
|
+
videourlph: string;
|
|
158
|
+
insertvideo: string;
|
|
159
|
+
dropvideo: string;
|
|
160
|
+
videodlgttl: string;
|
|
161
|
+
vidgeneral: string;
|
|
162
|
+
vidembed: string;
|
|
163
|
+
vidadvanced: string;
|
|
164
|
+
vidsource: string;
|
|
165
|
+
vidembedhint: string;
|
|
166
|
+
vidaltsource: string;
|
|
167
|
+
vidposter: string;
|
|
168
|
+
emoji: string;
|
|
169
|
+
code: string;
|
|
170
|
+
codesample: string;
|
|
171
|
+
codedlgttl: string;
|
|
172
|
+
codelang: string;
|
|
173
|
+
editcode: string;
|
|
174
|
+
deletecode: string;
|
|
175
|
+
copycode: string;
|
|
176
|
+
chcat_currency: string;
|
|
177
|
+
chcat_text: string;
|
|
178
|
+
chcat_math: string;
|
|
179
|
+
chcat_arrows: string;
|
|
180
|
+
chcat_symbols: string;
|
|
181
|
+
chcat_latin: string;
|
|
182
|
+
chcat_arabic: string;
|
|
183
|
+
emcat_smileys: string;
|
|
184
|
+
emcat_people: string;
|
|
185
|
+
emcat_animals: string;
|
|
186
|
+
emcat_food: string;
|
|
187
|
+
emcat_travel: string;
|
|
188
|
+
emcat_activities: string;
|
|
189
|
+
emcat_objects: string;
|
|
190
|
+
emcat_symbols: string;
|
|
191
|
+
emcat_flags: string;
|
|
143
192
|
}
|
|
144
193
|
export declare const I18N: Record<EditorLanguage, I18nStrings>;
|
|
145
194
|
export declare function getStrings(lang: EditorLanguage): I18nStrings;
|
|
@@ -50,6 +50,27 @@ export interface EditorInitOptions {
|
|
|
50
50
|
imageUploadHandler?: (file: File) => Promise<string>;
|
|
51
51
|
/** Called when imageUploadHandler rejects, so the host app can surface it. */
|
|
52
52
|
onImageUploadError?: (error: unknown, file: File) => void;
|
|
53
|
+
/** MIME types accepted by the native video file picker (the `accept` attribute).
|
|
54
|
+
* Defaults to `['video/mp4','video/webm','video/ogg']`. */
|
|
55
|
+
videoFileTypes?: string[];
|
|
56
|
+
/** Called when a user picks or drops a video. Resolve with the URL to
|
|
57
|
+
* insert (e.g. after uploading to S3/Azure Blob/your own server). If
|
|
58
|
+
* omitted, videos are inlined as base64 data URIs instead. Mirrors
|
|
59
|
+
* `imageUploadHandler` — the engine owns all placeholder/progress/insert UI. */
|
|
60
|
+
videoUploadHandler?: (file: File) => Promise<string>;
|
|
61
|
+
/** Called when videoUploadHandler rejects, so the host app can surface it. */
|
|
62
|
+
onVideoUploadError?: (error: unknown, file: File) => void;
|
|
63
|
+
/** Accent color used for active states, primary buttons and selection outlines.
|
|
64
|
+
* Any CSS color. Defaults to `#df3c2b`. Applied as the `--fable-primary` CSS
|
|
65
|
+
* variable on the document root (dialogs/popups render outside the editor shell). */
|
|
66
|
+
primaryColor?: string;
|
|
67
|
+
/** Background of each toolbar button group (the rounded pills). Any CSS
|
|
68
|
+
* color/gradient. Defaults to `#f1f3f6`. Applied as `--fable-tgrp-bg`. */
|
|
69
|
+
toolbarGroupBackground?: string;
|
|
70
|
+
/** Font family for the editor UI (menubar, toolbar, dialogs, popups). Defaults to
|
|
71
|
+
* a 'Noto Sans' stack — load the Noto Sans webfont in your app to use it, or pass
|
|
72
|
+
* your own family here. Applied as `--fable-ui-font`. */
|
|
73
|
+
uiFontFamily?: string;
|
|
53
74
|
}
|
|
54
75
|
export interface FableEditorApi {
|
|
55
76
|
getContent(): string;
|
|
@@ -9,6 +9,9 @@ export class FableEditorComponent {
|
|
|
9
9
|
toolbar = true;
|
|
10
10
|
statusbar = true;
|
|
11
11
|
readonly = false;
|
|
12
|
+
primaryColor;
|
|
13
|
+
toolbarGroupBackground;
|
|
14
|
+
uiFontFamily;
|
|
12
15
|
init = {};
|
|
13
16
|
value = '';
|
|
14
17
|
editorChange = new EventEmitter();
|
|
@@ -25,6 +28,9 @@ export class FableEditorComponent {
|
|
|
25
28
|
toolbar: this.toolbar,
|
|
26
29
|
statusbar: this.statusbar,
|
|
27
30
|
readonly: this.readonly,
|
|
31
|
+
primaryColor: this.primaryColor,
|
|
32
|
+
toolbarGroupBackground: this.toolbarGroupBackground,
|
|
33
|
+
uiFontFamily: this.uiFontFamily,
|
|
28
34
|
initialContent: this.value,
|
|
29
35
|
...this.init,
|
|
30
36
|
onChange: (content) => {
|
|
@@ -69,7 +75,7 @@ export class FableEditorComponent {
|
|
|
69
75
|
this.editor?.setContent(value ?? '');
|
|
70
76
|
}
|
|
71
77
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FableEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
72
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FableEditorComponent, isStandalone: true, selector: "fable-editor", inputs: { language: "language", height: "height", menubar: "menubar", toolbar: "toolbar", statusbar: "statusbar", readonly: "readonly", init: "init", value: "value" }, outputs: { editorChange: "editorChange", editorReady: "editorReady" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `<div #container></div>`, isInline: true, styles: [":host{display:block}\n"] });
|
|
78
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FableEditorComponent, isStandalone: true, selector: "fable-editor", inputs: { language: "language", height: "height", menubar: "menubar", toolbar: "toolbar", statusbar: "statusbar", readonly: "readonly", primaryColor: "primaryColor", toolbarGroupBackground: "toolbarGroupBackground", uiFontFamily: "uiFontFamily", init: "init", value: "value" }, outputs: { editorChange: "editorChange", editorReady: "editorReady" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `<div #container></div>`, isInline: true, styles: [":host{display:block}\n"] });
|
|
73
79
|
}
|
|
74
80
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FableEditorComponent, decorators: [{
|
|
75
81
|
type: Component,
|
|
@@ -89,6 +95,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
89
95
|
type: Input
|
|
90
96
|
}], readonly: [{
|
|
91
97
|
type: Input
|
|
98
|
+
}], primaryColor: [{
|
|
99
|
+
type: Input
|
|
100
|
+
}], toolbarGroupBackground: [{
|
|
101
|
+
type: Input
|
|
102
|
+
}], uiFontFamily: [{
|
|
103
|
+
type: Input
|
|
92
104
|
}], init: [{
|
|
93
105
|
type: Input
|
|
94
106
|
}], value: [{
|
|
@@ -98,4 +110,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
98
110
|
}], editorReady: [{
|
|
99
111
|
type: Output
|
|
100
112
|
}] } });
|
|
101
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
113
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFibGUtZWRpdG9yLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9hbmd1bGFyL2ZhYmxlLWVkaXRvci5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLFNBQVMsRUFFVCxZQUFZLEVBQ1osS0FBSyxFQUlMLE1BQU0sRUFFTixTQUFTLEVBQ1YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFdBQVcsRUFBcUQsTUFBTSxTQUFTLENBQUM7O0FBUXpGLE1BQU0sT0FBTyxvQkFBb0I7SUFDVyxTQUFTLENBQThCO0lBRXhFLFFBQVEsR0FBbUIsSUFBSSxDQUFDO0lBQ2hDLE1BQU0sR0FBRyxHQUFHLENBQUM7SUFDYixPQUFPLEdBQXFCLElBQUksQ0FBQztJQUNqQyxPQUFPLEdBQXFCLElBQUksQ0FBQztJQUNqQyxTQUFTLEdBQUcsSUFBSSxDQUFDO0lBQ2pCLFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDakIsWUFBWSxDQUFVO0lBQ3RCLHNCQUFzQixDQUFVO0lBQ2hDLFlBQVksQ0FBVTtJQUN0QixJQUFJLEdBQStDLEVBQUUsQ0FBQztJQUN0RCxLQUFLLEdBQUcsRUFBRSxDQUFDO0lBRVYsWUFBWSxHQUFHLElBQUksWUFBWSxFQUFVLENBQUM7SUFDMUMsV0FBVyxHQUFHLElBQUksWUFBWSxFQUFrQixDQUFDO0lBRW5ELE1BQU0sQ0FBZTtJQUNyQixXQUFXLEdBQUcsRUFBRSxDQUFDO0lBRXpCLFFBQVE7UUFDTixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7UUFDOUIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLFdBQVcsQ0FBQztZQUM1QixNQUFNLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhO1lBQ3BDLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUTtZQUN2QixNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07WUFDbkIsT0FBTyxF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O3NCQUFkLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUNHLE9BQU87c0JBQWYsS0FBSztnQkFDRyxTQUFTO3NCQUFqQixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csWUFBWTtzQkFBcEIsS0FBSztnQkFDRyxzQkFBc0I7c0JBQTlCLEtBQUs7Z0JBQ0csWUFBWTtzQkFBcEIsS0FBSztnQkFDRyxJQUFJO3NCQUFaLEtBQUs7Z0JBQ0csS0FBSztzQkFBYixLQUFLO2dCQUVJLFlBQVk7c0JBQXJCLE1BQU07Z0JBQ0csV0FBVztzQkFBcEIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENvbXBvbmVudCxcbiAgRWxlbWVudFJlZixcbiAgRXZlbnRFbWl0dGVyLFxuICBJbnB1dCxcbiAgT25DaGFuZ2VzLFxuICBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgT3V0cHV0LFxuICBTaW1wbGVDaGFuZ2VzLFxuICBWaWV3Q2hpbGRcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBGYWJsZUVkaXRvciwgRWRpdG9ySW5pdE9wdGlvbnMsIEVkaXRvckxhbmd1YWdlLCBGYWJsZUVkaXRvckFwaSB9IGZyb20gJy4uL2NvcmUnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdmYWJsZS1lZGl0b3InLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICB0ZW1wbGF0ZTogYDxkaXYgI2NvbnRhaW5lcj48L2Rpdj5gLFxuICBzdHlsZXM6IFsnOmhvc3QgeyBkaXNwbGF5OiBibG9jazsgfSddXG59KVxuZXhwb3J0IGNsYXNzIEZhYmxlRWRpdG9yQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSB7XG4gIEBWaWV3Q2hpbGQoJ2NvbnRhaW5lcicsIHsgc3RhdGljOiB0cnVlIH0pIGNvbnRhaW5lciE6IEVsZW1lbnRSZWY8SFRNTERpdkVsZW1lbnQ+O1xuXG4gIEBJbnB1dCgpIGxhbmd1YWdlOiBFZGl0b3JMYW5ndWFnZSA9ICdlbic7XG4gIEBJbnB1dCgpIGhlaWdodCA9IDMwMjtcbiAgQElucHV0KCkgbWVudWJhcjogYm9vbGVhbiB8IHN0cmluZyA9IHRydWU7XG4gIEBJbnB1dCgpIHRvb2xiYXI6IGJvb2xlYW4gfCBzdHJpbmcgPSB0cnVlO1xuICBASW5wdXQoKSBzdGF0dXNiYXIgPSB0cnVlO1xuICBASW5wdXQoKSByZWFkb25seSA9IGZhbHNlO1xuICBASW5wdXQoKSBwcmltYXJ5Q29sb3I/OiBzdHJpbmc7XG4gIEBJbnB1dCgpIHRvb2xiYXJHcm91cEJhY2tncm91bmQ/OiBzdHJpbmc7XG4gIEBJbnB1dCgpIHVpRm9udEZhbWlseT86IHN0cmluZztcbiAgQElucHV0KCkgaW5pdDogUGFydGlhbDxPbWl0PEVkaXRvckluaXRPcHRpb25zLCAndGFyZ2V0Jz4+ID0ge307XG4gIEBJbnB1dCgpIHZhbHVlID0gJyc7XG5cbiAgQE91dHB1dCgpIGVkaXRvckNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuICBAT3V0cHV0KCkgZWRpdG9yUmVhZHkgPSBuZXcgRXZlbnRFbWl0dGVyPEZhYmxlRWRpdG9yQXBpPigpO1xuXG4gIHByaXZhdGUgZWRpdG9yPzogRmFibGVFZGl0b3I7XG4gIHByaXZhdGUgbGFzdENvbnRlbnQgPSAnJztcblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLmxhc3RDb250ZW50ID0gdGhpcy52YWx1ZTtcbiAgICB0aGlzLmVkaXRvciA9IG5ldyBGYWJsZUVkaXRvcih7XG4gICAgICB0YXJnZXQ6IHRoaXMuY29udGFpbmVyLm5hdGl2ZUVsZW1lbnQsXG4gICAgICBsYW5ndWFnZTogdGhpcy5sYW5ndWFnZSxcbiAgICAgIGhlaWdodDogdGhpcy5oZWlnaHQsXG4gICAgICBtZW51YmFyOiB0aGlzLm1lbnViYXIsXG4gICAgICB0b29sYmFyOiB0aGlzLnRvb2xiYXIsXG4gICAgICBzdGF0dXNiYXI6IHRoaXMuc3RhdHVzYmFyLFxuICAgICAgcmVhZG9ubHk6IHRoaXMucmVhZG9ubHksXG4gICAgICBwcmltYXJ5Q29sb3I6IHRoaXMucHJpbWFyeUNvbG9yLFxuICAgICAgdG9vbGJhckdyb3VwQmFja2dyb3VuZDogdGhpcy50b29sYmFyR3JvdXBCYWNrZ3JvdW5kLFxuICAgICAgdWlGb250RmFtaWx5OiB0aGlzLnVpRm9udEZhbWlseSxcbiAgICAgIGluaXRpYWxDb250ZW50OiB0aGlzLnZhbHVlLFxuICAgICAgLi4udGhpcy5pbml0LFxuICAgICAgb25DaGFuZ2U6IChjb250ZW50KSA9PiB7XG4gICAgICAgIHRoaXMubGFzdENvbnRlbnQgPSBjb250ZW50O1xuICAgICAgICB0aGlzLmVkaXRvckNoYW5nZS5lbWl0KGNvbnRlbnQpO1xuICAgICAgfSxcbiAgICAgIG9uUmVhZHk6IChhcGkpID0+IHRoaXMuZWRpdG9yUmVhZHkuZW1pdChhcGkpXG4gICAgfSk7XG4gIH1cblxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmVkaXRvcikgcmV0dXJuO1xuICAgIGlmIChjaGFuZ2VzWydsYW5ndWFnZSddICYmICFjaGFuZ2VzWydsYW5ndWFnZSddLmZpcnN0Q2hhbmdlKSB7XG4gICAgICB0aGlzLmVkaXRvci5zZXRMYW5ndWFnZSh0aGlzLmxhbmd1YWdlKTtcbiAgICB9XG4gICAgaWYgKGNoYW5nZXNbJ3ZhbHVlJ10gJiYgIWNoYW5nZXNbJ3ZhbHVlJ10uZmlyc3RDaGFuZ2UgJiYgdGhpcy52YWx1ZSAhPT0gdGhpcy5sYXN0Q29udGVudCkge1xuICAgICAgdGhpcy5sYXN0Q29udGVudCA9IHRoaXMudmFsdWU7XG4gICAgICB0aGlzLmVkaXRvci5zZXRDb250ZW50KHRoaXMudmFsdWUgPz8gJycpO1xuICAgIH1cbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuZWRpdG9yPy5kZXN0cm95KCk7XG4gICAgdGhpcy5lZGl0b3IgPSB1bmRlZmluZWQ7XG4gIH1cblxuICBnZXRDb250ZW50KCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuZWRpdG9yPy5nZXRDb250ZW50KCkgPz8gJyc7XG4gIH1cblxuICBzZXRDb250ZW50KGh0bWw6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMubGFzdENvbnRlbnQgPSBodG1sO1xuICAgIHRoaXMuZWRpdG9yPy5zZXRDb250ZW50KGh0bWwgPz8gJycpO1xuICB9XG5cbiAgaW5zZXJ0Q29udGVudChodG1sOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmVkaXRvcj8uaW5zZXJ0Q29udGVudChodG1sKTtcbiAgfVxuXG4gIHNldExhbmd1YWdlKGxhbmc6IEVkaXRvckxhbmd1YWdlKTogdm9pZCB7XG4gICAgdGhpcy5lZGl0b3I/LnNldExhbmd1YWdlKGxhbmcpO1xuICB9XG5cbiAgZm9jdXMoKTogdm9pZCB7XG4gICAgdGhpcy5lZGl0b3I/LmZvY3VzKCk7XG4gIH1cblxuICB3cml0ZVZhbHVlKHZhbHVlOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmVkaXRvcj8uc2V0Q29udGVudCh2YWx1ZSA/PyAnJyk7XG4gIH1cbn1cbiJdfQ==
|