@firestitch/html-editor 12.0.3 → 12.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/app/classes/plugin.d.ts +7 -7
  2. package/app/components/html-editor/html-editor.component.d.ts +58 -58
  3. package/app/components/html-renderer/html-renderer.component.d.ts +12 -12
  4. package/app/consts/paragraph-buttons.const.d.ts +1 -1
  5. package/app/consts/rich-buttons.const.d.ts +1 -1
  6. package/app/consts/text-buttons.const.d.ts +1 -1
  7. package/app/enums/default-plugin.enum.d.ts +15 -15
  8. package/app/fs-html-editor.module.d.ts +43 -42
  9. package/app/injects/config.inject.d.ts +3 -3
  10. package/app/interfaces/html-editor-config.d.ts +40 -40
  11. package/app/interfaces/plugin-button.d.ts +12 -12
  12. package/app/interfaces/plugin-config.d.ts +5 -5
  13. package/app/plugins/checklist.plugin.d.ts +7 -7
  14. package/app/plugins/code.plugin.d.ts +9 -9
  15. package/app/plugins/configs/mention-plugin.config.d.ts +13 -13
  16. package/app/plugins/configs/screen-record-plugin.config.d.ts +7 -7
  17. package/app/plugins/mention.plugin.d.ts +15 -15
  18. package/app/plugins/screen-record.plugin.d.ts +11 -11
  19. package/app/services/froala-loader.service.d.ts +20 -20
  20. package/app/utils/loader.d.ts +7 -7
  21. package/bundles/firestitch-html-editor.umd.js +1405 -1401
  22. package/bundles/firestitch-html-editor.umd.js.map +1 -1
  23. package/esm2015/app/classes/plugin.js +4 -4
  24. package/esm2015/app/components/html-editor/html-editor.component.js +490 -489
  25. package/esm2015/app/components/html-renderer/html-renderer.component.js +28 -28
  26. package/esm2015/app/consts/paragraph-buttons.const.js +8 -8
  27. package/esm2015/app/consts/rich-buttons.const.js +6 -6
  28. package/esm2015/app/consts/text-buttons.const.js +18 -18
  29. package/esm2015/app/enums/default-plugin.enum.js +1 -1
  30. package/esm2015/app/fs-html-editor.module.js +69 -65
  31. package/esm2015/app/injects/config.inject.js +3 -3
  32. package/esm2015/app/interfaces/html-editor-config.js +1 -1
  33. package/esm2015/app/interfaces/plugin-button.js +1 -1
  34. package/esm2015/app/interfaces/plugin-config.js +1 -1
  35. package/esm2015/app/plugins/checklist.plugin.js +34 -34
  36. package/esm2015/app/plugins/code.plugin.js +62 -62
  37. package/esm2015/app/plugins/configs/mention-plugin.config.js +1 -1
  38. package/esm2015/app/plugins/configs/screen-record-plugin.config.js +1 -1
  39. package/esm2015/app/plugins/mention.plugin.js +86 -86
  40. package/esm2015/app/plugins/screen-record.plugin.js +91 -91
  41. package/esm2015/app/services/froala-loader.service.js +79 -79
  42. package/esm2015/app/utils/loader.js +83 -83
  43. package/esm2015/firestitch-html-editor.js +4 -4
  44. package/esm2015/public_api.js +11 -11
  45. package/fesm2015/firestitch-html-editor.js +1004 -999
  46. package/fesm2015/firestitch-html-editor.js.map +1 -1
  47. package/firestitch-html-editor.d.ts +5 -5
  48. package/package.json +1 -1
  49. package/public_api.d.ts +16 -16
@@ -1,8 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, Injectable, Optional, Inject, Component, ChangeDetectionStrategy, Input, forwardRef, HostBinding, ViewChild, NgModule } from '@angular/core';
3
- import * as i3 from '@angular/common';
3
+ import * as i4 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
- import * as i4 from '@firestitch/skeleton';
5
+ import * as i2 from '@firestitch/label';
6
+ import { FsLabelModule } from '@firestitch/label';
7
+ import * as i5 from '@firestitch/skeleton';
6
8
  import { FsSkeletonModule } from '@firestitch/skeleton';
7
9
  import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
8
10
  import { FileProcessor, FsFile } from '@firestitch/file';
@@ -14,1033 +16,1036 @@ import Tribute from 'tributejs';
14
16
  import { guid } from '@firestitch/common';
15
17
  import { __awaiter } from 'tslib';
16
18
 
17
- const RichButtons = [
18
- 'insertTable',
19
- 'insertImage',
20
- 'insertLink',
21
- 'insertVideo',
19
+ const RichButtons = [
20
+ 'insertTable',
21
+ 'insertImage',
22
+ 'insertLink',
23
+ 'insertVideo',
22
24
  ];
23
25
 
24
- const TextButtons = [
25
- 'paragraphFormat',
26
- 'code',
27
- 'bold',
28
- 'italic',
29
- 'underline',
30
- 'strikeThrough',
31
- 'subscript',
32
- 'superscript',
33
- 'fontFamily',
34
- 'fontSize',
35
- 'textColor',
36
- 'backgroundColor',
37
- 'inlineClass',
38
- 'inlineStyle',
39
- 'clearFormatting',
40
- 'quote',
26
+ const TextButtons = [
27
+ 'paragraphFormat',
28
+ 'code',
29
+ 'bold',
30
+ 'italic',
31
+ 'underline',
32
+ 'strikeThrough',
33
+ 'subscript',
34
+ 'superscript',
35
+ 'fontFamily',
36
+ 'fontSize',
37
+ 'textColor',
38
+ 'backgroundColor',
39
+ 'inlineClass',
40
+ 'inlineStyle',
41
+ 'clearFormatting',
42
+ 'quote',
41
43
  ];
42
44
 
43
- const ParagraphButtons = [
44
- 'align',
45
- 'formatOL',
46
- 'formatUL',
47
- 'checklist',
48
- 'indent',
49
- 'outdent',
45
+ const ParagraphButtons = [
46
+ 'align',
47
+ 'formatOL',
48
+ 'formatUL',
49
+ 'checklist',
50
+ 'indent',
51
+ 'outdent',
50
52
  ];
51
53
 
52
- const FS_HTML_EDITOR_DEFAULT_CONFIG = new InjectionToken('fs-html-editor.default-config');
54
+ const FS_HTML_EDITOR_DEFAULT_CONFIG = new InjectionToken('fs-html-editor.default-config');
53
55
  const FS_HTML_EDITOR_CONFIG = new InjectionToken('fs-html-editor.config');
54
56
 
55
- function resourceLoaderFactory() {
56
- let resourceBase = '/assets/js/froala/';
57
- let stylesBase = '/assets/css/';
58
- const head = document.getElementsByTagName('head')[0];
59
- const files = new Map();
60
- const resourcePaths = {
61
- froala: 'froala_editor.min.js',
62
- align: 'plugins/align.min.js',
63
- colors: 'plugins/colors.min.js',
64
- image: 'plugins/image.min.js',
65
- link: 'plugins/link.min.js',
66
- lists: 'plugins/lists.min.js',
67
- paragraph_format: 'plugins/paragraph_format.min.js',
68
- table: 'plugins/table.min.js',
69
- url: 'plugins/url.min.js',
70
- video: 'plugins/video.min.js',
71
- quote: 'plugins/quote.min.js',
72
- draggable: 'plugins/draggable.min.js',
73
- font_size: 'plugins/font_size.min.js',
74
- quick_insert: 'plugins/quick_insert.min.js',
75
- };
76
- const stylesPaths = {
77
- froala: 'froala_editor.pkgd.min.css',
78
- };
79
- const loadResource = function (scriptPath) {
80
- if (!files.has(scriptPath)) {
81
- const obs$ = new Observable((obs) => {
82
- const script = document.createElement('script');
83
- script.src = scriptPath;
84
- head.appendChild(script);
85
- script.onload = () => {
86
- obs.next();
87
- obs.complete();
88
- };
89
- script.onerror = (err) => {
90
- obs.error(err);
91
- };
92
- })
93
- .pipe(shareReplay(1));
94
- files.set(scriptPath, obs$);
95
- }
96
- return files.get(scriptPath);
97
- };
98
- const loadStyles = function (stylePath) {
99
- if (!files.has(stylePath)) {
100
- const obs$ = new Observable((obs) => {
101
- const style = document.createElement('link');
102
- style.id = stylePath;
103
- style.rel = 'stylesheet';
104
- style.href = `${stylePath}`;
105
- head.appendChild(style);
106
- style.onload = () => {
107
- obs.next();
108
- obs.complete();
109
- };
110
- style.onerror = (err) => {
111
- obs.error(err);
112
- };
113
- });
114
- files.set(stylePath, obs$);
115
- }
116
- return files.get(stylePath);
117
- };
118
- return {
119
- setResourceBase: (path) => {
120
- resourceBase = path;
121
- },
122
- setStylesBase: (path) => {
123
- stylesBase = path;
124
- },
125
- loadResource: (resourceName) => {
126
- const path = resourcePaths[resourceName] || resourceName;
127
- return loadResource(resourceBase + path);
128
- },
129
- loadStyles: (resourceName) => {
130
- const path = stylesPaths[resourceName] || resourceName;
131
- return loadStyles(stylesBase + path);
132
- }
133
- };
134
- }
57
+ function resourceLoaderFactory() {
58
+ let resourceBase = '/assets/js/froala/';
59
+ let stylesBase = '/assets/css/';
60
+ const head = document.getElementsByTagName('head')[0];
61
+ const files = new Map();
62
+ const resourcePaths = {
63
+ froala: 'froala_editor.min.js',
64
+ align: 'plugins/align.min.js',
65
+ colors: 'plugins/colors.min.js',
66
+ image: 'plugins/image.min.js',
67
+ link: 'plugins/link.min.js',
68
+ lists: 'plugins/lists.min.js',
69
+ paragraph_format: 'plugins/paragraph_format.min.js',
70
+ table: 'plugins/table.min.js',
71
+ url: 'plugins/url.min.js',
72
+ video: 'plugins/video.min.js',
73
+ quote: 'plugins/quote.min.js',
74
+ draggable: 'plugins/draggable.min.js',
75
+ font_size: 'plugins/font_size.min.js',
76
+ quick_insert: 'plugins/quick_insert.min.js',
77
+ };
78
+ const stylesPaths = {
79
+ froala: 'froala_editor.pkgd.min.css',
80
+ };
81
+ const loadResource = function (scriptPath) {
82
+ if (!files.has(scriptPath)) {
83
+ const obs$ = new Observable((obs) => {
84
+ const script = document.createElement('script');
85
+ script.src = scriptPath;
86
+ head.appendChild(script);
87
+ script.onload = () => {
88
+ obs.next();
89
+ obs.complete();
90
+ };
91
+ script.onerror = (err) => {
92
+ obs.error(err);
93
+ };
94
+ })
95
+ .pipe(shareReplay(1));
96
+ files.set(scriptPath, obs$);
97
+ }
98
+ return files.get(scriptPath);
99
+ };
100
+ const loadStyles = function (stylePath) {
101
+ if (!files.has(stylePath)) {
102
+ const obs$ = new Observable((obs) => {
103
+ const style = document.createElement('link');
104
+ style.id = stylePath;
105
+ style.rel = 'stylesheet';
106
+ style.href = `${stylePath}`;
107
+ head.appendChild(style);
108
+ style.onload = () => {
109
+ obs.next();
110
+ obs.complete();
111
+ };
112
+ style.onerror = (err) => {
113
+ obs.error(err);
114
+ };
115
+ });
116
+ files.set(stylePath, obs$);
117
+ }
118
+ return files.get(stylePath);
119
+ };
120
+ return {
121
+ setResourceBase: (path) => {
122
+ resourceBase = path;
123
+ },
124
+ setStylesBase: (path) => {
125
+ stylesBase = path;
126
+ },
127
+ loadResource: (resourceName) => {
128
+ const path = resourcePaths[resourceName] || resourceName;
129
+ return loadResource(resourceBase + path);
130
+ },
131
+ loadStyles: (resourceName) => {
132
+ const path = stylesPaths[resourceName] || resourceName;
133
+ return loadStyles(stylesBase + path);
134
+ }
135
+ };
136
+ }
135
137
  const ResourceLoader = resourceLoaderFactory();
136
138
 
137
- class FsFroalaLoaderService {
138
- constructor(_defaultConfig) {
139
- this._defaultConfig = _defaultConfig;
140
- this._froalaLoaded = new BehaviorSubject(false);
141
- this._froalaPluginsLoaded = new BehaviorSubject(false);
142
- this._loaded$ = combineLatest([this._froalaLoaded, this._froalaPluginsLoaded])
143
- .pipe(map(([FroalaReady, pluginsRead]) => {
144
- return pluginsRead && FroalaReady;
145
- }), shareReplay(1));
146
- if (this._defaultConfig) {
147
- if (this._defaultConfig.assetsJSPath) {
148
- ResourceLoader.setResourceBase(this._defaultConfig.assetsJSPath);
149
- }
150
- if (this._defaultConfig.assetsCSSPath) {
151
- ResourceLoader.setStylesBase(this._defaultConfig.assetsCSSPath);
152
- }
153
- }
154
- this._load();
155
- }
156
- get loaded$() {
157
- return this._loaded$;
158
- }
159
- get ready() {
160
- return this._froalaLoaded.getValue() && this._froalaPluginsLoaded.getValue();
161
- }
162
- get FroalaEditor() {
163
- return this._FroalaEditor;
164
- }
165
- _load() {
166
- combineLatest([
167
- ResourceLoader.loadResource('froala'),
168
- ResourceLoader.loadStyles('froala')
169
- ])
170
- .pipe(tap(() => {
171
- this._FroalaEditor = window.FroalaEditor;
172
- this._froalaDone();
173
- }), concatMap(() => {
174
- return this._loadPlugins();
175
- }), tap(() => {
176
- this._froalaPluginsDone();
177
- }))
178
- .subscribe();
179
- }
180
- _loadPlugins() {
181
- const imports = this._defaultConfig
182
- .froalaPlugins
183
- .reduce((acc, pluginName) => {
184
- const import$ = ResourceLoader.loadResource(pluginName);
185
- acc.push(import$);
186
- return acc;
187
- }, []);
188
- return combineLatest(imports);
189
- }
190
- _froalaDone() {
191
- this._froalaLoaded.next(true);
192
- }
193
- _froalaPluginsDone() {
194
- this._froalaPluginsLoaded.next(true);
195
- }
196
- }
197
- FsFroalaLoaderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsFroalaLoaderService, deps: [{ token: FS_HTML_EDITOR_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
198
- FsFroalaLoaderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsFroalaLoaderService, providedIn: 'root' });
199
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsFroalaLoaderService, decorators: [{
200
- type: Injectable,
201
- args: [{
202
- providedIn: 'root',
203
- }]
204
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
205
- type: Optional
206
- }, {
207
- type: Inject,
208
- args: [FS_HTML_EDITOR_CONFIG]
139
+ class FsFroalaLoaderService {
140
+ constructor(_defaultConfig) {
141
+ this._defaultConfig = _defaultConfig;
142
+ this._froalaLoaded = new BehaviorSubject(false);
143
+ this._froalaPluginsLoaded = new BehaviorSubject(false);
144
+ this._loaded$ = combineLatest([this._froalaLoaded, this._froalaPluginsLoaded])
145
+ .pipe(map(([FroalaReady, pluginsRead]) => {
146
+ return pluginsRead && FroalaReady;
147
+ }), shareReplay(1));
148
+ if (this._defaultConfig) {
149
+ if (this._defaultConfig.assetsJSPath) {
150
+ ResourceLoader.setResourceBase(this._defaultConfig.assetsJSPath);
151
+ }
152
+ if (this._defaultConfig.assetsCSSPath) {
153
+ ResourceLoader.setStylesBase(this._defaultConfig.assetsCSSPath);
154
+ }
155
+ }
156
+ this._load();
157
+ }
158
+ get loaded$() {
159
+ return this._loaded$;
160
+ }
161
+ get ready() {
162
+ return this._froalaLoaded.getValue() && this._froalaPluginsLoaded.getValue();
163
+ }
164
+ get FroalaEditor() {
165
+ return this._FroalaEditor;
166
+ }
167
+ _load() {
168
+ combineLatest([
169
+ ResourceLoader.loadResource('froala'),
170
+ ResourceLoader.loadStyles('froala')
171
+ ])
172
+ .pipe(tap(() => {
173
+ this._FroalaEditor = window.FroalaEditor;
174
+ this._froalaDone();
175
+ }), concatMap(() => {
176
+ return this._loadPlugins();
177
+ }), tap(() => {
178
+ this._froalaPluginsDone();
179
+ }))
180
+ .subscribe();
181
+ }
182
+ _loadPlugins() {
183
+ const imports = this._defaultConfig
184
+ .froalaPlugins
185
+ .reduce((acc, pluginName) => {
186
+ const import$ = ResourceLoader.loadResource(pluginName);
187
+ acc.push(import$);
188
+ return acc;
189
+ }, []);
190
+ return combineLatest(imports);
191
+ }
192
+ _froalaDone() {
193
+ this._froalaLoaded.next(true);
194
+ }
195
+ _froalaPluginsDone() {
196
+ this._froalaPluginsLoaded.next(true);
197
+ }
198
+ }
199
+ FsFroalaLoaderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsFroalaLoaderService, deps: [{ token: FS_HTML_EDITOR_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
200
+ FsFroalaLoaderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsFroalaLoaderService, providedIn: 'root' });
201
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsFroalaLoaderService, decorators: [{
202
+ type: Injectable,
203
+ args: [{
204
+ providedIn: 'root',
205
+ }]
206
+ }], ctorParameters: function () { return [{ type: undefined, decorators: [{
207
+ type: Optional
208
+ }, {
209
+ type: Inject,
210
+ args: [FS_HTML_EDITOR_CONFIG]
209
211
  }] }]; } });
210
212
 
211
- class FsHtmlRendererComponent {
212
- constructor(sanitized, _cdRef) {
213
- this.sanitized = sanitized;
214
- this._cdRef = _cdRef;
215
- }
216
- set setHtml(html) {
217
- this.trustedHtml = this.sanitized.bypassSecurityTrustHtml(html || '');
218
- this._cdRef.markForCheck();
219
- }
220
- }
221
- FsHtmlRendererComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlRendererComponent, deps: [{ token: i1.DomSanitizer }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
222
- FsHtmlRendererComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: FsHtmlRendererComponent, selector: "fs-html-renderer", inputs: { setHtml: ["html", "setHtml"] }, ngImport: i0, template: "<div class=\"fr-view fs-html-editor-default\" [innerHtml]=\"trustedHtml\"></div>\n", styles: [""], changeDetection: i0.ChangeDetectionStrategy.OnPush });
223
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlRendererComponent, decorators: [{
224
- type: Component,
225
- args: [{
226
- selector: 'fs-html-renderer',
227
- templateUrl: 'html-renderer.component.html',
228
- styleUrls: ['html-renderer.component.scss'],
229
- changeDetection: ChangeDetectionStrategy.OnPush,
230
- }]
231
- }], ctorParameters: function () { return [{ type: i1.DomSanitizer }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { setHtml: [{
232
- type: Input,
233
- args: ['html']
213
+ class FsHtmlRendererComponent {
214
+ constructor(sanitized, _cdRef) {
215
+ this.sanitized = sanitized;
216
+ this._cdRef = _cdRef;
217
+ }
218
+ set setHtml(html) {
219
+ this.trustedHtml = this.sanitized.bypassSecurityTrustHtml(html || '');
220
+ this._cdRef.markForCheck();
221
+ }
222
+ }
223
+ FsHtmlRendererComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlRendererComponent, deps: [{ token: i1.DomSanitizer }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
224
+ FsHtmlRendererComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: FsHtmlRendererComponent, selector: "fs-html-renderer", inputs: { setHtml: ["html", "setHtml"] }, ngImport: i0, template: "<div class=\"fr-view fs-html-editor-default\" [innerHtml]=\"trustedHtml\"></div>\n", styles: [""], changeDetection: i0.ChangeDetectionStrategy.OnPush });
225
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlRendererComponent, decorators: [{
226
+ type: Component,
227
+ args: [{
228
+ selector: 'fs-html-renderer',
229
+ templateUrl: 'html-renderer.component.html',
230
+ styleUrls: ['html-renderer.component.scss'],
231
+ changeDetection: ChangeDetectionStrategy.OnPush,
232
+ }]
233
+ }], ctorParameters: function () { return [{ type: i1.DomSanitizer }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { setHtml: [{
234
+ type: Input,
235
+ args: ['html']
234
236
  }] } });
235
237
 
236
- class FsHtmlEditorComponent {
237
- constructor(_defaultConfig, _cdRef, _fr) {
238
- this._defaultConfig = _defaultConfig;
239
- this._cdRef = _cdRef;
240
- this._fr = _fr;
241
- this.classFocused = false;
242
- this.classDisabled = false;
243
- this.config = {};
244
- this.initialized = false;
245
- this._initialize$ = new ReplaySubject();
246
- this._froalaReady$ = new BehaviorSubject(null);
247
- this._destroy$ = new Subject();
248
- this.onChange = (data) => { };
249
- this.onTouched = () => { };
250
- }
251
- get el() {
252
- return this.elRef.nativeElement;
253
- }
254
- get html() {
255
- return this._html;
256
- }
257
- get editor() {
258
- return this._editor;
259
- }
260
- get froalaLoaded$() {
261
- return this._fr.loaded$;
262
- }
263
- ngOnInit() {
264
- this.config = this.config || {};
265
- this.config.autofocus = this.config.autofocus !== false && !this.config.disabled;
266
- this.initialized = !this.config.initOnClick;
267
- this._listenLazyInit();
268
- }
269
- ngAfterViewInit() {
270
- if (!this.config.initOnClick || !this.hasContent()) {
271
- this.initialized = true;
272
- this._initialize$.next();
273
- }
274
- }
275
- hasContent() {
276
- const el = document.createElement('div');
277
- if (el.querySelector('img')) {
278
- return true;
279
- }
280
- el.innerHTML = this._html;
281
- return !!el.innerText.length;
282
- }
283
- initialize(options = {}) {
284
- const config = this._createConfig();
285
- this._initPlugins(config);
286
- this._initButtons(config);
287
- this._editor = new this._fr.FroalaEditor(this.el, this._createOptions(), () => {
288
- this._froalaReady$.next({ config, options });
289
- });
290
- }
291
- _processImageUpload(blob) {
292
- const processor = new FileProcessor();
293
- const file = new FsFile(blob);
294
- return processor.process([file], this.config.image)
295
- .pipe(switchMap(([f]) => {
296
- return this.config.image.upload(f.file);
297
- }));
298
- }
299
- uninitializedClick(event) {
300
- if (!event.defaultPrevented) {
301
- const target = event.target;
302
- if (target && target.nodeName === 'A' && target.getAttribute('href')) {
303
- const targetTarget = target.getAttribute('target');
304
- target.setAttribute('target', '_blank');
305
- setTimeout(() => {
306
- if (targetTarget) {
307
- target.setAttribute('target', targetTarget);
308
- }
309
- else {
310
- target.removeAttribute('target');
311
- }
312
- });
313
- }
314
- else {
315
- if (this.config.initClick) {
316
- this.config.initClick(event);
317
- }
318
- this._initialize$.next();
319
- this.initialized = true;
320
- }
321
- }
322
- }
323
- updateSize() {
324
- // Hack: To trigger the toolbar button size modes
325
- window.dispatchEvent(new Event('resize'));
326
- }
327
- validate(control) {
328
- const err = {};
329
- if (this.config.maxLength && this._html) {
330
- const maxLength = this.config.maxLength;
331
- const length = this._html.length;
332
- if (length > maxLength) {
333
- err.maxLengthError = `Must be ${maxLength} characters or fewer. You entered ${length} characters.`;
334
- }
335
- }
336
- return Object.keys(err).length ? err : null;
337
- }
338
- focus(options) {
339
- options = options ? options : {};
340
- if (options.cursorPosition === 'end') {
341
- this.editor.selection.setAtEnd(this.editor.$el.get(0));
342
- }
343
- else {
344
- this.editor.selection.setAtStart(this.editor.$el.get(0));
345
- }
346
- this.editor.selection.restore();
347
- const elSelection = this.editor.selection.element();
348
- if (elSelection) {
349
- elSelection.scrollIntoView({ block: 'center' });
350
- }
351
- }
352
- clear() {
353
- this.writeValue('');
354
- }
355
- disable() {
356
- this.classDisabled = true;
357
- }
358
- setHtml(html) {
359
- this.writeValue(html);
360
- }
361
- writeValue(html) {
362
- this._html = html || '';
363
- if (this.editor && this.editor.html) {
364
- try {
365
- this.editor.html.set(this._html);
366
- }
367
- catch (e) {
368
- }
369
- }
370
- this._cdRef.markForCheck();
371
- }
372
- registerOnChange(fn) {
373
- this.onChange = fn;
374
- }
375
- registerOnTouched(fn) {
376
- this.onTouched = fn;
377
- }
378
- ngOnDestroy() {
379
- this._initialize$.complete();
380
- this._destroy$.next();
381
- this._destroy$.complete();
382
- this.destroy();
383
- }
384
- destroy() {
385
- if (this.editor) {
386
- this.editor.destroy();
387
- }
388
- this.initialized = false;
389
- this._cdRef.markForCheck();
390
- this.el.innerHTML = '';
391
- }
392
- _createConfig() {
393
- return merge({ froalaConfig: {} }, this._defaultConfig, this.config);
394
- }
395
- _change(html) {
396
- this._html = html;
397
- this.onChange(html);
398
- }
399
- _initButtons(config) {
400
- (config.buttons || [])
401
- .forEach((button) => {
402
- /*
403
- Svg Keys:
404
- ["add", "advancedImageEditor", "alignCenter", "alignJustify", "alignLeft", "alignRight",
405
- "anchors", "back", "backgroundColor", "blockquote", "bold", "cellBackground",
406
- "cellBorderColor", "cellOptions", "cellStyle", "clearFormatting", "close", "codeView",
407
- "cogs", "columns", "editLink", "exitFullscreen", "fontAwesome", "fontFamily",
408
- "fontSize", "fullscreen", "help", "horizontalLine", "imageAltText", "imageCaption",
409
- "imageClass", "imageDisplay", "imageManager", "imageSize", "indent", "inlineClass",
410
- "inlineStyle", "insertEmbed", "insertFile", "insertImage", "insertLink", "insertMore",
411
- "insertTable", "insertVideo", "upload", "italic", "search", "lineHeight", "linkStyles",
412
- "mention", "more", "openLink", "orderedList", "outdent", "pageBreaker",
413
- "paragraphFormat", "paragraphMore", "paragraphStyle", "pdfExport", "print",
414
- "redo", "removeTable", "remove", "replaceImage", "row", "selectAll", "smile",
415
- "spellcheck", "star", "strikeThrough", "subscript", "superscript", "symbols", "tags",
416
- "tableHeader", "tableStyle", "textColor", "textMore", "underline", "undo", "unlink",
417
- "unorderedList", "verticalAlignBottom", "verticalAlignMiddle", "verticalAlignTop"]
418
- */
419
- var _a, _b, _c;
420
- if (button.svgKey) {
421
- this._fr.FroalaEditor.DefineIcon(button.name, { SVG_KEY: button.svgKey });
422
- }
423
- this._fr.FroalaEditor.RegisterCommand(button.name, {
424
- title: button.title,
425
- focus: (_a = button.focus) !== null && _a !== void 0 ? _a : true,
426
- undo: (_b = button.undo) !== null && _b !== void 0 ? _b : true,
427
- refreshAfterCallback: (_c = button.refreshAfterCallback) !== null && _c !== void 0 ? _c : true,
428
- callback: function () {
429
- button.click(this);
430
- }
431
- });
432
- });
433
- }
434
- _initPlugins(config) {
435
- (config.plugins || []).forEach((plugin) => {
436
- this._fr.FroalaEditor.PLUGINS[plugin.config.name] = function (editor) {
437
- plugin.editor = editor;
438
- plugin.initialize();
439
- return plugin;
440
- };
441
- (plugin.config.buttons || []).forEach((button) => {
442
- this._fr.FroalaEditor.DefineIcon(button.name, {
443
- NAME: button.name,
444
- PATH: button.svgPath,
445
- });
446
- this._fr.FroalaEditor.RegisterCommand(button.name, {
447
- icon: button.name,
448
- title: button.tooltip,
449
- undo: button.undo,
450
- focus: button.focus,
451
- showOnMobile: button.showOnMobile,
452
- refreshAfterCallback: button.refreshAfterCallback,
453
- callback: function () {
454
- if (button.click) {
455
- button.click(this);
456
- }
457
- },
458
- refresh: function (button) {
459
- if (button.refresh) {
460
- button.refresh(this, button);
461
- }
462
- }
463
- });
464
- });
465
- });
466
- }
467
- _createOptions() {
468
- const config = this._createConfig();
469
- return merge(
470
- // For some reason editor store somewhere pointer on default config object and dublicate options each time on init
471
- // Extra merge level fixed this problem and allow to init config properly without legacy data
472
- {}, {
473
- shortcutsEnabled: ['show', 'bold', 'italic', 'underline', 'indent', 'outdent', 'undo', 'redo', 'insertImage', 'createLink'],
474
- key: config.activationKey,
475
- placeholderText: config.placeholder,
476
- linkAlwaysBlank: true,
477
- tabSpaces: 2,
478
- typingTimer: 250,
479
- tooltips: false,
480
- wordPasteModal: false,
481
- imageDefaultWidth: 0,
482
- imageDefaultAlign: 'left',
483
- quickInsertEnabled: false,
484
- paragraphDefaultSelection: 'Format',
485
- videoUpload: false,
486
- paragraphFormat: {
487
- N: 'Normal',
488
- H1: 'Heading 1',
489
- H2: 'Heading 2',
490
- H3: 'Heading 3',
491
- H4: 'Heading 4',
492
- BLOCKQUOTE: 'Quote',
493
- },
494
- paragraphFormatSelection: true,
495
- toolbarButtonsXS: {
496
- moreText: {
497
- buttons: TextButtons,
498
- buttonsVisible: 1,
499
- },
500
- moreParagraph: {
501
- buttons: ParagraphButtons,
502
- buttonsVisible: 2,
503
- },
504
- moreRich: {
505
- buttons: RichButtons,
506
- buttonsVisible: 0,
507
- },
508
- },
509
- toolbarButtons: {
510
- moreText: {
511
- buttons: TextButtons,
512
- buttonsVisible: 3,
513
- },
514
- moreParagraph: {
515
- buttons: ParagraphButtons,
516
- buttonsVisible: 5,
517
- },
518
- moreRich: {
519
- buttons: RichButtons,
520
- buttonsVisible: 3,
521
- },
522
- },
523
- }, config.froalaConfig);
524
- }
525
- _listenLazyInit() {
526
- this.froalaLoaded$
527
- .pipe(filter((ready) => ready), switchMap(() => this._initialize$), tap(() => {
528
- const selection = window.getSelection();
529
- const options = {};
530
- if (selection.baseNode) {
531
- options.selection = {
532
- node: selection.baseNode.nodeValue,
533
- offset: selection.baseOffset,
534
- };
535
- }
536
- this.initialize(options);
537
- }), switchMap(() => this._froalaReady$), filter((data) => !!data), tap(({ config, options }) => {
538
- this._setupFroala(config, options);
539
- }))
540
- .subscribe();
541
- }
542
- _contentChanged() {
543
- const editorHTML = this._editor.html.get();
544
- if (this.html !== editorHTML) {
545
- this._change(editorHTML);
546
- }
547
- }
548
- _setupFroala(config, options) {
549
- this._cdRef.markForCheck();
550
- this.setHtml(this._html);
551
- if (config.disabled) {
552
- this.disable();
553
- }
554
- if (config.froalaConfig.events) {
555
- if (config.froalaConfig.events.initialized) {
556
- config.froalaConfig.events.initialized();
557
- }
558
- }
559
- this._editor.events.on('focus', () => {
560
- this.classFocused = true;
561
- this._cdRef.markForCheck();
562
- });
563
- this._editor.events.on('blur', () => {
564
- this.classFocused = false;
565
- this._cdRef.markForCheck();
566
- });
567
- fromEventPattern((handler) => {
568
- this._editor.events.on('contentChanged', handler);
569
- }, () => { })
570
- .pipe(startWith(this._html), map(() => this.editor.html.get()), distinctUntilChanged(), skip(1))
571
- .subscribe(() => {
572
- this._contentChanged();
573
- });
574
- // this._editor.events.on('contentChanged', () => {
575
- // console.log('test');
576
- // if (!this._firstChange) {
577
- // this._contentChanged();
578
- // }
579
- // });
580
- this._editor.events.on('paste.afterCleanup', (html) => {
581
- var div = document.createElement('div');
582
- div.innerHTML = html;
583
- div.querySelectorAll('*')
584
- .forEach((el) => {
585
- for (var i = 0; i < el.attributes.length; i++) {
586
- const name = el.attributes[0].name;
587
- if (name !== 'href') {
588
- el.removeAttribute(name);
589
- }
590
- }
591
- });
592
- return div.innerHTML;
593
- });
594
- this._editor.events.on('keydown', () => {
595
- this.onTouched();
596
- });
597
- if (this.config.image && this.config.image.upload) {
598
- this._editor.events.on('image.beforePasteUpload', (image) => {
599
- fetch(image.getAttribute('src'))
600
- .then((res) => {
601
- res.blob()
602
- .then((blob) => {
603
- this._processImageUpload(blob)
604
- .pipe(takeUntil(this._destroy$))
605
- .subscribe((url) => {
606
- image.setAttribute('src', url);
607
- image.removeAttribute('data-fr-image-pasted');
608
- this._contentChanged();
609
- });
610
- });
611
- });
612
- return false;
613
- });
614
- this._editor.events.on('image.beforeUpload', (blobs) => {
615
- this._processImageUpload(blobs[0])
616
- .pipe(takeUntil(this._destroy$))
617
- .subscribe((url) => {
618
- this.editor.image.insert(url, null, null, this.editor.image.get());
619
- });
620
- return false;
621
- });
622
- }
623
- if (this.el.querySelector('.fr-second-toolbar')) {
624
- this.el.querySelector('.fr-second-toolbar').remove();
625
- }
626
- const selection = options.selection;
627
- if (selection) {
628
- const node = Array.from(this.el.querySelectorAll('.fr-element *')).find((node) => {
629
- return selection.node === node.textContent;
630
- });
631
- if (node) {
632
- const range = document.createRange();
633
- const sel = window.getSelection();
634
- range.setStart(this._getTextNode(node), selection.offset);
635
- range.collapse(true);
636
- sel.removeAllRanges();
637
- sel.addRange(range);
638
- }
639
- else if (this.config.autofocus) {
640
- this.focus();
641
- }
642
- }
643
- else if (this.config.autofocus) {
644
- this.focus();
645
- }
646
- }
647
- _getTextNode(node) {
648
- if (node) {
649
- if (node.nodeType === Node.TEXT_NODE) {
650
- return node;
651
- }
652
- if (node.childNodes) {
653
- return this._getTextNode(node.childNodes[0]);
654
- }
655
- }
656
- }
657
- }
658
- FsHtmlEditorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorComponent, deps: [{ token: FS_HTML_EDITOR_CONFIG, optional: true }, { token: i0.ChangeDetectorRef }, { token: FsFroalaLoaderService }], target: i0.ɵɵFactoryTarget.Component });
659
- FsHtmlEditorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: FsHtmlEditorComponent, selector: "fs-html-editor", inputs: { config: "config" }, host: { properties: { "class.focused": "this.classFocused", "class.disabled": "this.classDisabled" } }, providers: [
660
- {
661
- provide: NG_VALUE_ACCESSOR,
662
- useExisting: forwardRef(() => FsHtmlEditorComponent),
663
- multi: true
664
- },
665
- {
666
- provide: NG_VALIDATORS,
667
- useExisting: forwardRef(() => FsHtmlEditorComponent),
668
- multi: true
669
- },
670
- ], viewQueries: [{ propertyName: "elRef", first: true, predicate: ["elRef"], descendants: true }], ngImport: i0, template: "<div class=\"fs-html-editor-default\">\n <ng-container *ngIf=\"initialized\">\n <ng-container *fsSkeleton=\"(froalaLoaded$ | async) !== false\">\n </ng-container>\n </ng-container>\n\n <div #elRef></div>\n\n <div class=\"bottom-line\"></div>\n <div class=\"disabled-line\"></div>\n <fs-html-renderer\n *ngIf=\"!initialized\"\n [html]=\"html\"\n (click)=\"uninitializedClick($event)\">\n </fs-html-renderer>\n</div>\n", styles: [":host ::ng-deep .fr-box.fr-basic .fr-wrapper{border:0;border-bottom:1px solid #949494;margin-bottom:1px;background-color:transparent}:host ::ng-deep .fr-box.fr-basic .fr-element{padding:0;font-family:inherit;color:inherit;font-size:inherit;line-height:inherit;min-height:auto}:host ::ng-deep .fr-more-toolbar,:host ::ng-deep .fr-toolbar,:host ::ng-deep .fr-dropdown:after,:host ::ng-deep .fr-dropdown-menu .fr-dropdown-wrapper{transition:none!important}:host ::ng-deep .fr-toolbar.fr-top{position:sticky;z-index:2;border-radius:0;background-color:#fff}:host ::ng-deep .fr-toolbar.fr-top .fr-btn-grp{margin:0 20px 0 0}:host ::ng-deep .fr-toolbar.fr-top .fr-btn-grp:nth-child(1):after,:host ::ng-deep .fr-toolbar.fr-top .fr-btn-grp:nth-child(2):after{content:\"\";position:absolute;top:9px;height:30px;border-left:1px solid #e5e5e5;margin-left:10px}:host ::ng-deep .fr-toolbar.fr-top div:first-of-type:before{border:none}:host ::ng-deep .fr-toolbar.fr-top.fr-toolbar-open .fr-newline{height:auto}:host ::ng-deep .fr-toolbar.fr-top .fr-btn-grp:first-child{margin-left:0}:host ::ng-deep .fr-toolbar.fr-top.fr-top{border:0}:host ::ng-deep .fr-toolbar.fr-top .fr-newline{margin:0}:host ::ng-deep .fr-toolbar.fr-top .fr-more-toolbar{z-index:auto}:host ::ng-deep .fr-toolbar.fr-top .fr-command.fr-btn{z-index:auto}:host ::ng-deep .fr-toolbar.fr-top .fr-command.fr-btn.fr-dropdown:after{right:3px}:host ::ng-deep .fr-toolbar.fr-top .fr-command.fr-btn.fr-dropdown.fr-selection span{margin-right:24px}:host ::ng-deep .fr-toolbar.fr-top .fr-command.fr-btn.fr-open{border-radius:4px;margin:4px 2px}:host ::ng-deep .fr-view ol,:host ::ng-deep .fr-view ul{list-style-position:unset}:host.disabled ::ng-deep .fr-box.fr-basic{opacity:.5;pointer-events:none}:host.disabled ::ng-deep .fr-box.fr-basic .fr-wrapper{border:none}:host.disabled ::ng-deep .disabled-line{display:block}::ng-deep .fr-popup{z-index:1001!important}::ng-deep .fr-video-by-url-layer .fr-checkbox-line{display:none}.disabled-line{display:none;position:absolute;bottom:-1px;z-index:1;background-image:linear-gradient(90deg,rgba(0,0,0,.42) 0,rgba(0,0,0,.42) 33%,transparent 0);background-size:4px 100%;background-repeat:repeat-x;height:1px;width:100%}.bottom-line{opacity:0;transform-origin:50%;transform:scaleX(.5);transition:background-color .3s cubic-bezier(.55,0,.55,.2);width:100%;border-bottom:2px solid #949494;position:absolute;bottom:-1px;z-index:1}:host{position:relative;display:block}:host.focused .bottom-line{opacity:1;display:block;transform:scaleX(1);transition:transform .3s cubic-bezier(.25,.8,.25,1),opacity .1s cubic-bezier(.25,.8,.25,1),background-color .3s cubic-bezier(.25,.8,.25,1)}::ng-deep .fs-html-editor-mention-container{position:absolute;top:0;left:0;height:auto;max-height:300px;max-width:500px;overflow:auto;display:block;z-index:999999;box-shadow:0 2px 4px -1px #0003,0 4px 5px #00000024,0 1px 10px #0000001f;border-bottom-left-radius:4px;border-bottom-right-radius:4px;background:#fff}::ng-deep .fs-html-editor-mention-container ul{margin:0;padding:0;list-style:none}::ng-deep .fs-html-editor-mention-container li{padding:0 16px;cursor:pointer;line-height:48px}::ng-deep .fs-html-editor-mention-container li.highlight,::ng-deep .fs-html-editor-mention-container li:hover{background:#F5F5F5}::ng-deep .fs-html-editor-mention-container li.no-match{cursor:default}\n"], components: [{ type: FsHtmlRendererComponent, selector: "fs-html-renderer", inputs: ["html"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4.FsSkeletonContentDirective, selector: "[fsSkeleton]", inputs: ["fsSkeleton", "fsSkeletonPattern"] }], pipes: { "async": i3.AsyncPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
671
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorComponent, decorators: [{
672
- type: Component,
673
- args: [{
674
- selector: 'fs-html-editor',
675
- templateUrl: 'html-editor.component.html',
676
- styleUrls: ['html-editor.component.scss'],
677
- providers: [
678
- {
679
- provide: NG_VALUE_ACCESSOR,
680
- useExisting: forwardRef(() => FsHtmlEditorComponent),
681
- multi: true
682
- },
683
- {
684
- provide: NG_VALIDATORS,
685
- useExisting: forwardRef(() => FsHtmlEditorComponent),
686
- multi: true
687
- },
688
- ],
689
- changeDetection: ChangeDetectionStrategy.OnPush,
690
- }]
691
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
692
- type: Optional
693
- }, {
694
- type: Inject,
695
- args: [FS_HTML_EDITOR_CONFIG]
696
- }] }, { type: i0.ChangeDetectorRef }, { type: FsFroalaLoaderService }]; }, propDecorators: { classFocused: [{
697
- type: HostBinding,
698
- args: ['class.focused']
699
- }], classDisabled: [{
700
- type: HostBinding,
701
- args: ['class.disabled']
702
- }], elRef: [{
703
- type: ViewChild,
704
- args: ['elRef']
705
- }], config: [{
706
- type: Input
238
+ class FsHtmlEditorComponent {
239
+ constructor(_defaultConfig, _cdRef, _fr) {
240
+ this._defaultConfig = _defaultConfig;
241
+ this._cdRef = _cdRef;
242
+ this._fr = _fr;
243
+ this.classFocused = false;
244
+ this.classDisabled = false;
245
+ this.config = {};
246
+ this.initialized = false;
247
+ this._initialize$ = new ReplaySubject();
248
+ this._froalaReady$ = new BehaviorSubject(null);
249
+ this._destroy$ = new Subject();
250
+ this.onChange = (data) => { };
251
+ this.onTouched = () => { };
252
+ }
253
+ get el() {
254
+ return this.elRef.nativeElement;
255
+ }
256
+ get html() {
257
+ return this._html;
258
+ }
259
+ get editor() {
260
+ return this._editor;
261
+ }
262
+ get froalaLoaded$() {
263
+ return this._fr.loaded$;
264
+ }
265
+ ngOnInit() {
266
+ this.config = this.config || {};
267
+ this.config.autofocus = this.config.autofocus !== false && !this.config.disabled;
268
+ this.initialized = !this.config.initOnClick;
269
+ this._listenLazyInit();
270
+ }
271
+ ngAfterViewInit() {
272
+ if (!this.config.initOnClick || !this.hasContent()) {
273
+ this.initialized = true;
274
+ this._initialize$.next();
275
+ }
276
+ }
277
+ hasContent() {
278
+ const el = document.createElement('div');
279
+ if (el.querySelector('img')) {
280
+ return true;
281
+ }
282
+ el.innerHTML = this._html;
283
+ return !!el.innerText.length;
284
+ }
285
+ initialize(options = {}) {
286
+ const config = this._createConfig();
287
+ this._initPlugins(config);
288
+ this._initButtons(config);
289
+ this._editor = new this._fr.FroalaEditor(this.el, this._createOptions(), () => {
290
+ this._froalaReady$.next({ config, options });
291
+ });
292
+ }
293
+ _processImageUpload(blob) {
294
+ const processor = new FileProcessor();
295
+ const file = new FsFile(blob);
296
+ return processor.process([file], this.config.image)
297
+ .pipe(switchMap(([f]) => {
298
+ return this.config.image.upload(f.file);
299
+ }));
300
+ }
301
+ uninitializedClick(event) {
302
+ if (!event.defaultPrevented) {
303
+ const target = event.target;
304
+ if (target && target.nodeName === 'A' && target.getAttribute('href')) {
305
+ const targetTarget = target.getAttribute('target');
306
+ target.setAttribute('target', '_blank');
307
+ setTimeout(() => {
308
+ if (targetTarget) {
309
+ target.setAttribute('target', targetTarget);
310
+ }
311
+ else {
312
+ target.removeAttribute('target');
313
+ }
314
+ });
315
+ }
316
+ else {
317
+ if (this.config.initClick) {
318
+ this.config.initClick(event);
319
+ }
320
+ this._initialize$.next();
321
+ this.initialized = true;
322
+ }
323
+ }
324
+ }
325
+ updateSize() {
326
+ // Hack: To trigger the toolbar button size modes
327
+ window.dispatchEvent(new Event('resize'));
328
+ }
329
+ validate(control) {
330
+ const err = {};
331
+ if (this.config.maxLength && this._html) {
332
+ const maxLength = this.config.maxLength;
333
+ const length = this._html.length;
334
+ if (length > maxLength) {
335
+ err.maxLengthError = `Must be ${maxLength} characters or fewer. You entered ${length} characters.`;
336
+ }
337
+ }
338
+ return Object.keys(err).length ? err : null;
339
+ }
340
+ focus(options) {
341
+ options = options ? options : {};
342
+ if (options.cursorPosition === 'end') {
343
+ this.editor.selection.setAtEnd(this.editor.$el.get(0));
344
+ }
345
+ else {
346
+ this.editor.selection.setAtStart(this.editor.$el.get(0));
347
+ }
348
+ this.editor.selection.restore();
349
+ const elSelection = this.editor.selection.element();
350
+ if (elSelection) {
351
+ elSelection.scrollIntoView({ block: 'center' });
352
+ }
353
+ }
354
+ clear() {
355
+ this.writeValue('');
356
+ }
357
+ disable() {
358
+ this.classDisabled = true;
359
+ }
360
+ setHtml(html) {
361
+ this.writeValue(html);
362
+ }
363
+ writeValue(html) {
364
+ this._html = html || '';
365
+ if (this.editor && this.editor.html) {
366
+ try {
367
+ this.editor.html.set(this._html);
368
+ }
369
+ catch (e) {
370
+ }
371
+ }
372
+ this._cdRef.markForCheck();
373
+ }
374
+ registerOnChange(fn) {
375
+ this.onChange = fn;
376
+ }
377
+ registerOnTouched(fn) {
378
+ this.onTouched = fn;
379
+ }
380
+ ngOnDestroy() {
381
+ this._initialize$.complete();
382
+ this._destroy$.next();
383
+ this._destroy$.complete();
384
+ this.destroy();
385
+ }
386
+ destroy() {
387
+ if (this.editor) {
388
+ this.editor.destroy();
389
+ }
390
+ this.initialized = false;
391
+ this._cdRef.markForCheck();
392
+ this.el.innerHTML = '';
393
+ }
394
+ _createConfig() {
395
+ return merge({ froalaConfig: {} }, this._defaultConfig, this.config);
396
+ }
397
+ _change(html) {
398
+ this._html = html;
399
+ this.onChange(html);
400
+ }
401
+ _initButtons(config) {
402
+ (config.buttons || [])
403
+ .forEach((button) => {
404
+ /*
405
+ Svg Keys:
406
+ ["add", "advancedImageEditor", "alignCenter", "alignJustify", "alignLeft", "alignRight",
407
+ "anchors", "back", "backgroundColor", "blockquote", "bold", "cellBackground",
408
+ "cellBorderColor", "cellOptions", "cellStyle", "clearFormatting", "close", "codeView",
409
+ "cogs", "columns", "editLink", "exitFullscreen", "fontAwesome", "fontFamily",
410
+ "fontSize", "fullscreen", "help", "horizontalLine", "imageAltText", "imageCaption",
411
+ "imageClass", "imageDisplay", "imageManager", "imageSize", "indent", "inlineClass",
412
+ "inlineStyle", "insertEmbed", "insertFile", "insertImage", "insertLink", "insertMore",
413
+ "insertTable", "insertVideo", "upload", "italic", "search", "lineHeight", "linkStyles",
414
+ "mention", "more", "openLink", "orderedList", "outdent", "pageBreaker",
415
+ "paragraphFormat", "paragraphMore", "paragraphStyle", "pdfExport", "print",
416
+ "redo", "removeTable", "remove", "replaceImage", "row", "selectAll", "smile",
417
+ "spellcheck", "star", "strikeThrough", "subscript", "superscript", "symbols", "tags",
418
+ "tableHeader", "tableStyle", "textColor", "textMore", "underline", "undo", "unlink",
419
+ "unorderedList", "verticalAlignBottom", "verticalAlignMiddle", "verticalAlignTop"]
420
+ */
421
+ var _a, _b, _c;
422
+ if (button.svgKey) {
423
+ this._fr.FroalaEditor.DefineIcon(button.name, { SVG_KEY: button.svgKey });
424
+ }
425
+ this._fr.FroalaEditor.RegisterCommand(button.name, {
426
+ title: button.title,
427
+ focus: (_a = button.focus) !== null && _a !== void 0 ? _a : true,
428
+ undo: (_b = button.undo) !== null && _b !== void 0 ? _b : true,
429
+ refreshAfterCallback: (_c = button.refreshAfterCallback) !== null && _c !== void 0 ? _c : true,
430
+ callback: function () {
431
+ button.click(this);
432
+ }
433
+ });
434
+ });
435
+ }
436
+ _initPlugins(config) {
437
+ (config.plugins || []).forEach((plugin) => {
438
+ this._fr.FroalaEditor.PLUGINS[plugin.config.name] = function (editor) {
439
+ plugin.editor = editor;
440
+ plugin.initialize();
441
+ return plugin;
442
+ };
443
+ (plugin.config.buttons || []).forEach((button) => {
444
+ this._fr.FroalaEditor.DefineIcon(button.name, {
445
+ NAME: button.name,
446
+ PATH: button.svgPath,
447
+ });
448
+ this._fr.FroalaEditor.RegisterCommand(button.name, {
449
+ icon: button.name,
450
+ title: button.tooltip,
451
+ undo: button.undo,
452
+ focus: button.focus,
453
+ showOnMobile: button.showOnMobile,
454
+ refreshAfterCallback: button.refreshAfterCallback,
455
+ callback: function () {
456
+ if (button.click) {
457
+ button.click(this);
458
+ }
459
+ },
460
+ refresh: function (button) {
461
+ if (button.refresh) {
462
+ button.refresh(this, button);
463
+ }
464
+ }
465
+ });
466
+ });
467
+ });
468
+ }
469
+ _createOptions() {
470
+ const config = this._createConfig();
471
+ return merge(
472
+ // For some reason editor store somewhere pointer on default config object and dublicate options each time on init
473
+ // Extra merge level fixed this problem and allow to init config properly without legacy data
474
+ {}, {
475
+ shortcutsEnabled: ['show', 'bold', 'italic', 'underline', 'indent', 'outdent', 'undo', 'redo', 'insertImage', 'createLink'],
476
+ key: config.activationKey,
477
+ placeholderText: config.placeholder,
478
+ linkAlwaysBlank: true,
479
+ tabSpaces: 2,
480
+ typingTimer: 250,
481
+ tooltips: false,
482
+ wordPasteModal: false,
483
+ imageDefaultWidth: 0,
484
+ imageDefaultAlign: 'left',
485
+ quickInsertEnabled: false,
486
+ paragraphDefaultSelection: 'Format',
487
+ videoUpload: false,
488
+ paragraphFormat: {
489
+ N: 'Normal',
490
+ H1: 'Heading 1',
491
+ H2: 'Heading 2',
492
+ H3: 'Heading 3',
493
+ H4: 'Heading 4',
494
+ BLOCKQUOTE: 'Quote',
495
+ },
496
+ paragraphFormatSelection: true,
497
+ toolbarButtonsXS: {
498
+ moreText: {
499
+ buttons: TextButtons,
500
+ buttonsVisible: 1,
501
+ },
502
+ moreParagraph: {
503
+ buttons: ParagraphButtons,
504
+ buttonsVisible: 2,
505
+ },
506
+ moreRich: {
507
+ buttons: RichButtons,
508
+ buttonsVisible: 0,
509
+ },
510
+ },
511
+ toolbarButtons: {
512
+ moreText: {
513
+ buttons: TextButtons,
514
+ buttonsVisible: 3,
515
+ },
516
+ moreParagraph: {
517
+ buttons: ParagraphButtons,
518
+ buttonsVisible: 5,
519
+ },
520
+ moreRich: {
521
+ buttons: RichButtons,
522
+ buttonsVisible: 3,
523
+ },
524
+ },
525
+ }, config.froalaConfig);
526
+ }
527
+ _listenLazyInit() {
528
+ this.froalaLoaded$
529
+ .pipe(filter((ready) => ready), switchMap(() => this._initialize$), tap(() => {
530
+ const selection = window.getSelection();
531
+ const options = {};
532
+ if (selection.baseNode) {
533
+ options.selection = {
534
+ node: selection.baseNode.nodeValue,
535
+ offset: selection.baseOffset,
536
+ };
537
+ }
538
+ this.initialize(options);
539
+ }), switchMap(() => this._froalaReady$), filter((data) => !!data), tap(({ config, options }) => {
540
+ this._setupFroala(config, options);
541
+ }))
542
+ .subscribe();
543
+ }
544
+ _contentChanged() {
545
+ const editorHTML = this._editor.html.get();
546
+ if (this.html !== editorHTML) {
547
+ this._change(editorHTML);
548
+ }
549
+ }
550
+ _setupFroala(config, options) {
551
+ this._cdRef.markForCheck();
552
+ this.setHtml(this._html);
553
+ if (config.disabled) {
554
+ this.disable();
555
+ }
556
+ if (config.froalaConfig.events) {
557
+ if (config.froalaConfig.events.initialized) {
558
+ config.froalaConfig.events.initialized();
559
+ }
560
+ }
561
+ this._editor.events.on('focus', () => {
562
+ this.classFocused = true;
563
+ this._cdRef.markForCheck();
564
+ });
565
+ this._editor.events.on('blur', () => {
566
+ this.classFocused = false;
567
+ this._cdRef.markForCheck();
568
+ });
569
+ fromEventPattern((handler) => {
570
+ this._editor.events.on('contentChanged', handler);
571
+ }, () => { })
572
+ .pipe(startWith(this._html), map(() => this.editor.html.get()), distinctUntilChanged(), skip(1))
573
+ .subscribe(() => {
574
+ this._contentChanged();
575
+ });
576
+ // this._editor.events.on('contentChanged', () => {
577
+ // console.log('test');
578
+ // if (!this._firstChange) {
579
+ // this._contentChanged();
580
+ // }
581
+ // });
582
+ this._editor.events.on('paste.afterCleanup', (html) => {
583
+ var div = document.createElement('div');
584
+ div.innerHTML = html;
585
+ div.querySelectorAll('*')
586
+ .forEach((el) => {
587
+ for (var i = 0; i < el.attributes.length; i++) {
588
+ const name = el.attributes[0].name;
589
+ if (name !== 'href') {
590
+ el.removeAttribute(name);
591
+ }
592
+ }
593
+ });
594
+ return div.innerHTML;
595
+ });
596
+ this._editor.events.on('keydown', () => {
597
+ this.onTouched();
598
+ });
599
+ if (this.config.image && this.config.image.upload) {
600
+ this._editor.events.on('image.beforePasteUpload', (image) => {
601
+ fetch(image.getAttribute('src'))
602
+ .then((res) => {
603
+ res.blob()
604
+ .then((blob) => {
605
+ this._processImageUpload(blob)
606
+ .pipe(takeUntil(this._destroy$))
607
+ .subscribe((url) => {
608
+ image.setAttribute('src', url);
609
+ image.removeAttribute('data-fr-image-pasted');
610
+ this._contentChanged();
611
+ });
612
+ });
613
+ });
614
+ return false;
615
+ });
616
+ this._editor.events.on('image.beforeUpload', (blobs) => {
617
+ this._processImageUpload(blobs[0])
618
+ .pipe(takeUntil(this._destroy$))
619
+ .subscribe((url) => {
620
+ this.editor.image.insert(url, null, null, this.editor.image.get());
621
+ });
622
+ return false;
623
+ });
624
+ }
625
+ if (this.el.querySelector('.fr-second-toolbar')) {
626
+ this.el.querySelector('.fr-second-toolbar').remove();
627
+ }
628
+ const selection = options.selection;
629
+ if (selection) {
630
+ const node = Array.from(this.el.querySelectorAll('.fr-element *')).find((node) => {
631
+ return selection.node === node.textContent;
632
+ });
633
+ if (node) {
634
+ const range = document.createRange();
635
+ const sel = window.getSelection();
636
+ range.setStart(this._getTextNode(node), selection.offset);
637
+ range.collapse(true);
638
+ sel.removeAllRanges();
639
+ sel.addRange(range);
640
+ }
641
+ else if (this.config.autofocus) {
642
+ this.focus();
643
+ }
644
+ }
645
+ else if (this.config.autofocus) {
646
+ this.focus();
647
+ }
648
+ }
649
+ _getTextNode(node) {
650
+ if (node) {
651
+ if (node.nodeType === Node.TEXT_NODE) {
652
+ return node;
653
+ }
654
+ if (node.childNodes) {
655
+ return this._getTextNode(node.childNodes[0]);
656
+ }
657
+ }
658
+ }
659
+ }
660
+ FsHtmlEditorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorComponent, deps: [{ token: FS_HTML_EDITOR_CONFIG, optional: true }, { token: i0.ChangeDetectorRef }, { token: FsFroalaLoaderService }], target: i0.ɵɵFactoryTarget.Component });
661
+ FsHtmlEditorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: FsHtmlEditorComponent, selector: "fs-html-editor", inputs: { config: "config" }, host: { properties: { "class.focused": "this.classFocused", "class.disabled": "this.classDisabled" } }, providers: [
662
+ {
663
+ provide: NG_VALUE_ACCESSOR,
664
+ useExisting: forwardRef(() => FsHtmlEditorComponent),
665
+ multi: true
666
+ },
667
+ {
668
+ provide: NG_VALIDATORS,
669
+ useExisting: forwardRef(() => FsHtmlEditorComponent),
670
+ multi: true
671
+ },
672
+ ], viewQueries: [{ propertyName: "elRef", first: true, predicate: ["elRef"], descendants: true }], ngImport: i0, template: "<fs-label-field>\n <fs-label *ngIf=\"config.label\">{{config.label}}</fs-label>\n <div class=\"fs-html-editor-default\">\n <ng-container *ngIf=\"initialized\">\n <ng-container *fsSkeleton=\"(froalaLoaded$ | async) !== false\">\n </ng-container>\n </ng-container>\n \n <div #elRef></div>\n \n <div class=\"bottom-line\"></div>\n <div class=\"disabled-line\"></div>\n <fs-html-renderer\n *ngIf=\"!initialized\"\n [html]=\"html\"\n (click)=\"uninitializedClick($event)\">\n </fs-html-renderer>\n </div>\n \n <fs-label-message>\n <div class=\"fs-form-message\"></div>\n </fs-label-message>\n</fs-label-field>\n", styles: [":host ::ng-deep .fr-box.fr-basic .fr-wrapper{border:0;border-bottom:1px solid #949494;margin-bottom:1px;background-color:transparent}:host ::ng-deep .fr-box.fr-basic .fr-element{padding:0;font-family:inherit;color:inherit;font-size:inherit;line-height:inherit;min-height:auto}:host ::ng-deep .fr-more-toolbar,:host ::ng-deep .fr-toolbar,:host ::ng-deep .fr-dropdown:after,:host ::ng-deep .fr-dropdown-menu .fr-dropdown-wrapper{transition:none!important}:host ::ng-deep .fr-toolbar.fr-top{position:sticky;z-index:2;border-radius:0;background-color:#fff}:host ::ng-deep .fr-toolbar.fr-top .fr-btn-grp{margin:0 20px 0 0}:host ::ng-deep .fr-toolbar.fr-top .fr-btn-grp:nth-child(1):after,:host ::ng-deep .fr-toolbar.fr-top .fr-btn-grp:nth-child(2):after{content:\"\";position:absolute;top:9px;height:30px;border-left:1px solid #e5e5e5;margin-left:10px}:host ::ng-deep .fr-toolbar.fr-top div:first-of-type:before{border:none}:host ::ng-deep .fr-toolbar.fr-top.fr-toolbar-open .fr-newline{height:auto}:host ::ng-deep .fr-toolbar.fr-top .fr-btn-grp:first-child{margin-left:0}:host ::ng-deep .fr-toolbar.fr-top.fr-top{border:0}:host ::ng-deep .fr-toolbar.fr-top .fr-newline{margin:0}:host ::ng-deep .fr-toolbar.fr-top .fr-more-toolbar{z-index:auto}:host ::ng-deep .fr-toolbar.fr-top .fr-command.fr-btn{z-index:auto}:host ::ng-deep .fr-toolbar.fr-top .fr-command.fr-btn.fr-dropdown:after{right:3px}:host ::ng-deep .fr-toolbar.fr-top .fr-command.fr-btn.fr-dropdown.fr-selection span{margin-right:24px}:host ::ng-deep .fr-toolbar.fr-top .fr-command.fr-btn.fr-open{border-radius:4px;margin:4px 2px}:host ::ng-deep .fr-view ol,:host ::ng-deep .fr-view ul{list-style-position:unset}:host.disabled ::ng-deep .fr-box.fr-basic{opacity:.5;pointer-events:none}:host.disabled ::ng-deep .fr-box.fr-basic .fr-wrapper{border:none}:host.disabled ::ng-deep .disabled-line{display:block}::ng-deep .fr-popup{z-index:1001!important}::ng-deep .fr-video-by-url-layer .fr-checkbox-line{display:none}.disabled-line{display:none;position:absolute;bottom:-1px;z-index:1;background-image:linear-gradient(90deg,rgba(0,0,0,.42) 0,rgba(0,0,0,.42) 33%,transparent 0);background-size:4px 100%;background-repeat:repeat-x;height:1px;width:100%}.bottom-line{opacity:0;transform-origin:50%;transform:scaleX(.5);transition:background-color .3s cubic-bezier(.55,0,.55,.2);width:100%;border-bottom:2px solid #949494;position:absolute;bottom:-1px;z-index:1}:host{position:relative;display:block}:host.focused .bottom-line{opacity:1;display:block;transform:scaleX(1);transition:transform .3s cubic-bezier(.25,.8,.25,1),opacity .1s cubic-bezier(.25,.8,.25,1),background-color .3s cubic-bezier(.25,.8,.25,1)}::ng-deep .fs-html-editor-mention-container{position:absolute;top:0;left:0;height:auto;max-height:300px;max-width:500px;overflow:auto;display:block;z-index:999999;box-shadow:0 2px 4px -1px #0003,0 4px 5px #00000024,0 1px 10px #0000001f;border-bottom-left-radius:4px;border-bottom-right-radius:4px;background:#fff}::ng-deep .fs-html-editor-mention-container ul{margin:0;padding:0;list-style:none}::ng-deep .fs-html-editor-mention-container li{padding:0 16px;cursor:pointer;line-height:48px}::ng-deep .fs-html-editor-mention-container li.highlight,::ng-deep .fs-html-editor-mention-container li:hover{background:#F5F5F5}::ng-deep .fs-html-editor-mention-container li.no-match{cursor:default}\n"], components: [{ type: i2.FsLabelFieldComponent, selector: "fs-label-field", inputs: ["bottomMargin", "topMargin", "labelMargin"] }, { type: i2.FsLabelComponent, selector: "fs-label" }, { type: FsHtmlRendererComponent, selector: "fs-html-renderer", inputs: ["html"] }, { type: i2.FsLabelMessageComponent, selector: "fs-label-message" }], directives: [{ type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.FsSkeletonContentDirective, selector: "[fsSkeleton]", inputs: ["fsSkeleton", "fsSkeletonPattern"] }], pipes: { "async": i4.AsyncPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
673
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorComponent, decorators: [{
674
+ type: Component,
675
+ args: [{
676
+ selector: 'fs-html-editor',
677
+ templateUrl: 'html-editor.component.html',
678
+ styleUrls: ['html-editor.component.scss'],
679
+ providers: [
680
+ {
681
+ provide: NG_VALUE_ACCESSOR,
682
+ useExisting: forwardRef(() => FsHtmlEditorComponent),
683
+ multi: true
684
+ },
685
+ {
686
+ provide: NG_VALIDATORS,
687
+ useExisting: forwardRef(() => FsHtmlEditorComponent),
688
+ multi: true
689
+ },
690
+ ],
691
+ changeDetection: ChangeDetectionStrategy.OnPush,
692
+ }]
693
+ }], ctorParameters: function () { return [{ type: undefined, decorators: [{
694
+ type: Optional
695
+ }, {
696
+ type: Inject,
697
+ args: [FS_HTML_EDITOR_CONFIG]
698
+ }] }, { type: i0.ChangeDetectorRef }, { type: FsFroalaLoaderService }]; }, propDecorators: { classFocused: [{
699
+ type: HostBinding,
700
+ args: ['class.focused']
701
+ }], classDisabled: [{
702
+ type: HostBinding,
703
+ args: ['class.disabled']
704
+ }], elRef: [{
705
+ type: ViewChild,
706
+ args: ['elRef']
707
+ }], config: [{
708
+ type: Input
707
709
  }] } });
708
710
 
709
- class FsHtmlEditorModule {
710
- static forRoot(config = {}) {
711
- return {
712
- ngModule: FsHtmlEditorModule,
713
- providers: [
714
- { provide: FS_HTML_EDITOR_DEFAULT_CONFIG, useValue: config },
715
- {
716
- provide: FS_HTML_EDITOR_CONFIG,
717
- useFactory: FsHtmlEditorConfigFactory,
718
- deps: [FS_HTML_EDITOR_DEFAULT_CONFIG]
719
- }
720
- ]
721
- };
722
- }
723
- }
724
- FsHtmlEditorModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
725
- FsHtmlEditorModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorModule, declarations: [FsHtmlEditorComponent,
726
- FsHtmlRendererComponent], imports: [CommonModule,
727
- FsSkeletonModule], exports: [FsHtmlEditorComponent,
728
- FsHtmlRendererComponent] });
729
- FsHtmlEditorModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorModule, imports: [[
730
- CommonModule,
731
- FsSkeletonModule,
732
- ]] });
733
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorModule, decorators: [{
734
- type: NgModule,
735
- args: [{
736
- imports: [
737
- CommonModule,
738
- FsSkeletonModule,
739
- ],
740
- exports: [
741
- FsHtmlEditorComponent,
742
- FsHtmlRendererComponent,
743
- ],
744
- declarations: [
745
- FsHtmlEditorComponent,
746
- FsHtmlRendererComponent,
747
- ],
748
- }]
749
- }] });
750
- function FsHtmlEditorConfigFactory(config) {
751
- return Object.assign({ froalaPlugins: [
752
- "align" /* Align */,
753
- "colors" /* Colors */,
754
- "image" /* Image */,
755
- "link" /* Link */,
756
- "lists" /* Lists */,
757
- "paragraph_format" /* ParagraphFormat */,
758
- "quote" /* Quote */,
759
- "table" /* Table */,
760
- "url" /* Url */,
761
- "video" /* Video */,
762
- "draggable" /* Draggable */,
763
- "font_size" /* FontSize */,
764
- ] }, config);
711
+ class FsHtmlEditorModule {
712
+ static forRoot(config = {}) {
713
+ return {
714
+ ngModule: FsHtmlEditorModule,
715
+ providers: [
716
+ { provide: FS_HTML_EDITOR_DEFAULT_CONFIG, useValue: config },
717
+ {
718
+ provide: FS_HTML_EDITOR_CONFIG,
719
+ useFactory: FsHtmlEditorConfigFactory,
720
+ deps: [FS_HTML_EDITOR_DEFAULT_CONFIG]
721
+ }
722
+ ]
723
+ };
724
+ }
725
+ }
726
+ FsHtmlEditorModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
727
+ FsHtmlEditorModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorModule, declarations: [FsHtmlEditorComponent,
728
+ FsHtmlRendererComponent], imports: [CommonModule,
729
+ FsSkeletonModule,
730
+ FsLabelModule], exports: [FsHtmlEditorComponent,
731
+ FsHtmlRendererComponent] });
732
+ FsHtmlEditorModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorModule, imports: [[
733
+ CommonModule,
734
+ FsSkeletonModule,
735
+ FsLabelModule,
736
+ ]] });
737
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: FsHtmlEditorModule, decorators: [{
738
+ type: NgModule,
739
+ args: [{
740
+ imports: [
741
+ CommonModule,
742
+ FsSkeletonModule,
743
+ FsLabelModule,
744
+ ],
745
+ exports: [
746
+ FsHtmlEditorComponent,
747
+ FsHtmlRendererComponent,
748
+ ],
749
+ declarations: [
750
+ FsHtmlEditorComponent,
751
+ FsHtmlRendererComponent,
752
+ ],
753
+ }]
754
+ }] });
755
+ function FsHtmlEditorConfigFactory(config) {
756
+ return Object.assign({ froalaPlugins: [
757
+ "align" /* Align */,
758
+ "colors" /* Colors */,
759
+ "image" /* Image */,
760
+ "link" /* Link */,
761
+ "lists" /* Lists */,
762
+ "paragraph_format" /* ParagraphFormat */,
763
+ "quote" /* Quote */,
764
+ "table" /* Table */,
765
+ "url" /* Url */,
766
+ "video" /* Video */,
767
+ "draggable" /* Draggable */,
768
+ "font_size" /* FontSize */,
769
+ ] }, config);
765
770
  }
766
771
 
767
- class Plugin {
768
- initialize() { }
769
- destroy() { }
772
+ class Plugin {
773
+ initialize() { }
774
+ destroy() { }
770
775
  }
771
776
 
772
- class MentionPlugin extends Plugin {
773
- constructor(config) {
774
- super();
775
- this.config = config;
776
- this._guid = guid();
777
- this.selected = (event) => {
778
- if (this.config.selected) {
779
- if (MentionPlugin._currentGuid === this._guid) {
780
- this.config.selected(event.detail.item.original);
781
- }
782
- }
783
- };
784
- this.config = Object.assign(Object.assign({}, config), { buttons: [
785
- {
786
- name: config.name,
787
- tooltip: config.tooltip,
788
- svgPath: config.iconPath,
789
- click: (editor) => {
790
- this._closeAll();
791
- this._tribute.showMenuForCollection(editor.el);
792
- }
793
- }
794
- ] });
795
- this._tribute = new Tribute({
796
- values: debounce(this._loadValues.bind(this), 200),
797
- searchOpts: {
798
- pre: '',
799
- post: '',
800
- skip: true
801
- },
802
- containerClass: `fs-html-editor-mention-container ${this.config.containerClass || ''}`,
803
- itemClass: `fs-html-editor-mention-menu-item ${this.config.menuItemClass || ''}`,
804
- trigger: this.config.trigger,
805
- allowSpaces: true,
806
- menuItemTemplate: (item) => {
807
- return this.config.menuItemTemplate(item.original);
808
- },
809
- selectTemplate: (item) => {
810
- const template = this.config.selectedTemplate(item.original);
811
- let p = document.createElement('p');
812
- p.innerHTML = template;
813
- let node = p.firstElementChild;
814
- if (!node) {
815
- node = document.createElement('span');
816
- node.innerHTML = template;
817
- }
818
- node.setAttribute('contenteditable', 'false');
819
- node.classList.add('fr-deletable');
820
- return node.outerHTML;
821
- },
822
- });
823
- MentionPlugin._tributes.push(this._tribute);
824
- }
825
- initialize() {
826
- this._tribute.attach(this.editor.el);
827
- this.editor.events.on('keydown', (e) => {
828
- if (e.key === 'Enter' && this._tribute.isActive) {
829
- return false;
830
- }
831
- }, true);
832
- this.editor.el.addEventListener("tribute-replaced", this.selected);
833
- }
834
- destroy() {
835
- this.editor.el.removeEventListener("tribute-replaced", this.selected);
836
- }
837
- _closeAll() {
838
- MentionPlugin._tributes.forEach((tribute) => {
839
- if (tribute.menu) {
840
- tribute.hideMenu();
841
- }
842
- });
843
- }
844
- _loadValues(keyword, cb) {
845
- MentionPlugin._currentGuid = this._guid;
846
- return this.config.fetch(keyword)
847
- .subscribe((data) => {
848
- cb(data);
849
- });
850
- }
851
- }
852
- MentionPlugin._currentGuid = null;
777
+ class MentionPlugin extends Plugin {
778
+ constructor(config) {
779
+ super();
780
+ this.config = config;
781
+ this._guid = guid();
782
+ this.selected = (event) => {
783
+ if (this.config.selected) {
784
+ if (MentionPlugin._currentGuid === this._guid) {
785
+ this.config.selected(event.detail.item.original);
786
+ }
787
+ }
788
+ };
789
+ this.config = Object.assign(Object.assign({}, config), { buttons: [
790
+ {
791
+ name: config.name,
792
+ tooltip: config.tooltip,
793
+ svgPath: config.iconPath,
794
+ click: (editor) => {
795
+ this._closeAll();
796
+ this._tribute.showMenuForCollection(editor.el);
797
+ }
798
+ }
799
+ ] });
800
+ this._tribute = new Tribute({
801
+ values: debounce(this._loadValues.bind(this), 200),
802
+ searchOpts: {
803
+ pre: '',
804
+ post: '',
805
+ skip: true
806
+ },
807
+ containerClass: `fs-html-editor-mention-container ${this.config.containerClass || ''}`,
808
+ itemClass: `fs-html-editor-mention-menu-item ${this.config.menuItemClass || ''}`,
809
+ trigger: this.config.trigger,
810
+ allowSpaces: true,
811
+ menuItemTemplate: (item) => {
812
+ return this.config.menuItemTemplate(item.original);
813
+ },
814
+ selectTemplate: (item) => {
815
+ const template = this.config.selectedTemplate(item.original);
816
+ let p = document.createElement('p');
817
+ p.innerHTML = template;
818
+ let node = p.firstElementChild;
819
+ if (!node) {
820
+ node = document.createElement('span');
821
+ node.innerHTML = template;
822
+ }
823
+ node.setAttribute('contenteditable', 'false');
824
+ node.classList.add('fr-deletable');
825
+ return node.outerHTML;
826
+ },
827
+ });
828
+ MentionPlugin._tributes.push(this._tribute);
829
+ }
830
+ initialize() {
831
+ this._tribute.attach(this.editor.el);
832
+ this.editor.events.on('keydown', (e) => {
833
+ if (e.key === 'Enter' && this._tribute.isActive) {
834
+ return false;
835
+ }
836
+ }, true);
837
+ this.editor.el.addEventListener("tribute-replaced", this.selected);
838
+ }
839
+ destroy() {
840
+ this.editor.el.removeEventListener("tribute-replaced", this.selected);
841
+ }
842
+ _closeAll() {
843
+ MentionPlugin._tributes.forEach((tribute) => {
844
+ if (tribute.menu) {
845
+ tribute.hideMenu();
846
+ }
847
+ });
848
+ }
849
+ _loadValues(keyword, cb) {
850
+ MentionPlugin._currentGuid = this._guid;
851
+ return this.config.fetch(keyword)
852
+ .subscribe((data) => {
853
+ cb(data);
854
+ });
855
+ }
856
+ }
857
+ MentionPlugin._currentGuid = null;
853
858
  MentionPlugin._tributes = [];
854
859
 
855
- class ChecklistPlugin extends Plugin {
856
- constructor(config = { name: 'checklist' }) {
857
- super();
858
- this.config = config;
859
- this.config = Object.assign(Object.assign({}, config), { name: 'checklist', buttons: [
860
- {
861
- name: 'checklist',
862
- svgPath: 'M8.4,10.3l-1.2,1.2l4,4L20,6.7l-1.2-1.2l-7.6,7.6L8.4,10.3z M18.2,18.2H5.8V5.8h8.9V4H5.8C4.8,4,4,4.8,4,5.8v12.4c0,1,0.8,1.8,1.8,1.8h12.4c1,0,1.8-0.8,1.8-1.8v-7.1h-1.8V18.2z',
863
- click: (editor) => {
864
- editor.html.insert('<ul class="checklist"><li></li></ul>');
865
- }
866
- }
867
- ] });
868
- }
869
- initialize() {
870
- this.editor.events.on('click', (e) => {
871
- const li = e.target;
872
- if (li.nodeName === 'LI' && e.offsetX < 0) {
873
- const items = [li, ...li.querySelectorAll('li')];
874
- if (li.classList.contains('checked')) {
875
- items.forEach((item) => {
876
- item.classList.remove('checked');
877
- });
878
- }
879
- else {
880
- items.forEach((item) => {
881
- item.classList.add('checked');
882
- });
883
- }
884
- }
885
- }, false);
886
- }
860
+ class ChecklistPlugin extends Plugin {
861
+ constructor(config = { name: 'checklist' }) {
862
+ super();
863
+ this.config = config;
864
+ this.config = Object.assign(Object.assign({}, config), { name: 'checklist', buttons: [
865
+ {
866
+ name: 'checklist',
867
+ svgPath: 'M8.4,10.3l-1.2,1.2l4,4L20,6.7l-1.2-1.2l-7.6,7.6L8.4,10.3z M18.2,18.2H5.8V5.8h8.9V4H5.8C4.8,4,4,4.8,4,5.8v12.4c0,1,0.8,1.8,1.8,1.8h12.4c1,0,1.8-0.8,1.8-1.8v-7.1h-1.8V18.2z',
868
+ click: (editor) => {
869
+ editor.html.insert('<ul class="checklist"><li></li></ul>');
870
+ }
871
+ }
872
+ ] });
873
+ }
874
+ initialize() {
875
+ this.editor.events.on('click', (e) => {
876
+ const li = e.target;
877
+ if (li.nodeName === 'LI' && e.offsetX < 0) {
878
+ const items = [li, ...li.querySelectorAll('li')];
879
+ if (li.classList.contains('checked')) {
880
+ items.forEach((item) => {
881
+ item.classList.remove('checked');
882
+ });
883
+ }
884
+ else {
885
+ items.forEach((item) => {
886
+ item.classList.add('checked');
887
+ });
888
+ }
889
+ }
890
+ }, false);
891
+ }
887
892
  }
888
893
 
889
- class CodePlugin extends Plugin {
890
- constructor(config = { name: 'code' }) {
891
- super();
892
- this.config = config;
893
- this.config = Object.assign(Object.assign({}, config), { name: 'code', buttons: [
894
- {
895
- name: 'code',
896
- svgPath: 'M8.9,7.3L4.2,12l4.7,4.7l1.1-1.1L6.4,12L10,8.4L8.9,7.3z M15.1,7.3L14,8.4l3.6,3.6L14,15.6l1.1,1.1l4.7-4.7L15.1,7.3z',
897
- click: (editor) => {
898
- const node = editor.selection.get();
899
- const codeEl = this._getCodeEl(node.baseNode);
900
- if (codeEl) {
901
- codeEl.classList.remove('code');
902
- }
903
- else {
904
- editor.html.insert('<p class="code">' + editor.selection.get().toString().replace(/\n{1,}/g, '<br>') + '</p>');
905
- }
906
- }
907
- }
908
- ] });
909
- }
910
- initialize() {
911
- this.editor.events.on('keydown', (e) => {
912
- if (e.keyCode === 13) {
913
- const selection = this.editor.selection.get();
914
- if (selection && this._getCodeEl(selection.baseNode)) {
915
- e.preventDefault();
916
- e.stopPropagation();
917
- e.stopImmediatePropagation();
918
- let br = '<br>';
919
- const base = selection.baseNode;
920
- if (base.nodeName === '#text' && selection.baseOffset === base.length) {
921
- if (!(base.nextSibling && base.nextSibling.nodeName === 'BR')) {
922
- br += '<br>';
923
- }
924
- }
925
- else {
926
- const el = Array.from(base.childNodes).splice(selection.baseOffset - 1, 1).pop();
927
- if (el && el.nodeName !== 'BR') {
928
- br += '<br>';
929
- }
930
- }
931
- this.editor.html.insert(br);
932
- return false;
933
- }
934
- }
935
- }, true);
936
- }
937
- destroy() { }
938
- _getCodeEl(el) {
939
- if (el) {
940
- if (el.classList && el.classList.contains('code')) {
941
- return el;
942
- }
943
- if (el.parentElement) {
944
- return this._getCodeEl(el.parentElement);
945
- }
946
- }
947
- return null;
948
- }
894
+ class CodePlugin extends Plugin {
895
+ constructor(config = { name: 'code' }) {
896
+ super();
897
+ this.config = config;
898
+ this.config = Object.assign(Object.assign({}, config), { name: 'code', buttons: [
899
+ {
900
+ name: 'code',
901
+ svgPath: 'M8.9,7.3L4.2,12l4.7,4.7l1.1-1.1L6.4,12L10,8.4L8.9,7.3z M15.1,7.3L14,8.4l3.6,3.6L14,15.6l1.1,1.1l4.7-4.7L15.1,7.3z',
902
+ click: (editor) => {
903
+ const node = editor.selection.get();
904
+ const codeEl = this._getCodeEl(node.baseNode);
905
+ if (codeEl) {
906
+ codeEl.classList.remove('code');
907
+ }
908
+ else {
909
+ editor.html.insert('<p class="code">' + editor.selection.get().toString().replace(/\n{1,}/g, '<br>') + '</p>');
910
+ }
911
+ }
912
+ }
913
+ ] });
914
+ }
915
+ initialize() {
916
+ this.editor.events.on('keydown', (e) => {
917
+ if (e.keyCode === 13) {
918
+ const selection = this.editor.selection.get();
919
+ if (selection && this._getCodeEl(selection.baseNode)) {
920
+ e.preventDefault();
921
+ e.stopPropagation();
922
+ e.stopImmediatePropagation();
923
+ let br = '<br>';
924
+ const base = selection.baseNode;
925
+ if (base.nodeName === '#text' && selection.baseOffset === base.length) {
926
+ if (!(base.nextSibling && base.nextSibling.nodeName === 'BR')) {
927
+ br += '<br>';
928
+ }
929
+ }
930
+ else {
931
+ const el = Array.from(base.childNodes).splice(selection.baseOffset - 1, 1).pop();
932
+ if (el && el.nodeName !== 'BR') {
933
+ br += '<br>';
934
+ }
935
+ }
936
+ this.editor.html.insert(br);
937
+ return false;
938
+ }
939
+ }
940
+ }, true);
941
+ }
942
+ destroy() { }
943
+ _getCodeEl(el) {
944
+ if (el) {
945
+ if (el.classList && el.classList.contains('code')) {
946
+ return el;
947
+ }
948
+ if (el.parentElement) {
949
+ return this._getCodeEl(el.parentElement);
950
+ }
951
+ }
952
+ return null;
953
+ }
949
954
  }
950
955
 
951
- class ScreenRecordPlugin extends Plugin {
952
- constructor(config = {}) {
953
- super();
954
- this.config = config;
955
- this.config = Object.assign(Object.assign({}, config), { name: config.name || 'screenRecord', buttons: [
956
- {
957
- name: 'screenRecord',
956
+ class ScreenRecordPlugin extends Plugin {
957
+ constructor(config = {}) {
958
+ super();
959
+ this.config = config;
960
+ this.config = Object.assign(Object.assign({}, config), { name: config.name || 'screenRecord', buttons: [
961
+ {
962
+ name: 'screenRecord',
958
963
  svgPath: `M14.7,7.7v6.1H7.1V7.7H14.7 M16.3,9.6V6.9c0-0.4-0.3-0.8-0.8-0.8H6.4c-0.4,0-0.8,0.3-0.8,0.8v7.6c0,0.4,0.3,0.8,0.8,0.8h9.1c0.4,0,0.8-0.3,0.8-0.8v-2.7l1.7,1.7c0.3,0.3,0.8,0.3,1.1,0c0.1-0.1,0.2-0.3,0.2-0.5V8.4c0-0.4-0.3-0.8-0.8-0.8
959
964
  c-0.2,0-0.4,0.1-0.5,0.2L16.3,9.6z M21.5,1.6H2.9c-0.7,0-1.3,0.7-1.3,1.6v14.6c0,0.9,0.6,1.6,1.3,1.6h7.6v1.1l-5,1c-0.2,0-0.4,0.3-0.4,0.6c0,0.3,0.2,0.4,0.4,0.4c0,0,0,0,0.1,0l5.3-1h2.6l5.3,1c0,0,0,0,0.1,0c0.2,0,0.4-0.2,0.4-0.4
960
- c0-0.3-0.1-0.6-0.4-0.6l-5-1v-1.1h7.6c0.7,0,1.3-0.7,1.3-1.6V3.2C22.9,2.3,22.3,1.6,21.5,1.6z M21.1,17.3H3.3V3.7h17.8L21.1,17.3L21.1,17.3z`,
961
- tooltip: 'Screen Record',
962
- click: (editor) => {
963
- navigator.mediaDevices.getDisplayMedia({ video: true })
964
- .then((stream) => {
965
- const chunks = [];
966
- const options = { mimeType: 'video/webm; codecs=vp9' };
967
- let mediaRecorder = new MediaRecorder(stream, options);
968
- stream.addEventListener('inactive', (e) => {
969
- mediaRecorder.stop();
970
- mediaRecorder = null;
971
- stream.getTracks().forEach((track) => track.stop());
972
- stream = null;
973
- const blob = new Blob(chunks, { type: 'video/webm' });
974
- config.upload(blob)
975
- .subscribe((url) => {
976
- editor.html.insert(`<span class="fr-screen-record fr-deletable fr-draggable"><video controls style="max-width:${config.maxWidth}"><source src="${url}" type="video/webm"></video></span>`);
977
- });
978
- });
979
- mediaRecorder.addEventListener('dataavailable', (event) => {
980
- if (event.data && event.data.size > 0) {
981
- chunks.push(event.data);
982
- }
983
- });
984
- mediaRecorder.start(10);
985
- })
986
- .catch((e) => { });
987
- }
988
- }
989
- ] });
990
- }
991
- initialize() {
992
- return __awaiter(this, void 0, void 0, function* () {
993
- yield ResourceLoader.loadStyles('froala')
994
- .toPromise();
995
- const FroalaEditor = window.FroalaEditor;
996
- this.editor.events.on('mouseup', (e) => {
997
- const el = this._getEl(e.target);
998
- if (el) {
999
- this.editor.popups.hideAll();
1000
- this._activeEl = el;
1001
- this._showPopup(el);
1002
- }
1003
- });
1004
- FroalaEditor.POPUP_TEMPLATES["screenRecordPlugin.popup"] = '[_BUTTONS_]';
1005
- FroalaEditor.DefineIcon('popupClose', { NAME: 'trash', SVG_KEY: 'remove' });
1006
- FroalaEditor.RegisterCommand('popupClose', {
1007
- title: 'Remove',
1008
- undo: false,
1009
- focus: false,
1010
- callback: () => {
1011
- if (this._activeEl) {
1012
- this._activeEl.remove();
1013
- }
1014
- }
1015
- });
1016
- });
1017
- }
1018
- _createPopup() {
1019
- const template = {
1020
- buttons: `<div class="fr-buttons">${this.editor.button.buildList(['popupClose'])}</div>`,
1021
- };
1022
- return this.editor.popups.create('screenRecordPlugin.popup', template);
1023
- }
1024
- _showPopup(el) {
1025
- var $popup = this.editor.popups.get('screenRecordPlugin.popup');
1026
- if (!$popup) {
1027
- $popup = this._createPopup();
1028
- }
1029
- this.editor.popups.setContainer('screenRecordPlugin.popup', this.editor.$(el));
1030
- const left = el.offsetLeft + el.offsetWidth / 2;
1031
- this.editor.popups.show('screenRecordPlugin.popup', left, null);
1032
- }
1033
- _getEl(el) {
1034
- if (el && el.classList) {
1035
- if (el.classList.contains('fr-screen-record'))
1036
- return el;
1037
- return this._getEl(el.parentNode);
1038
- }
1039
- }
965
+ c0-0.3-0.1-0.6-0.4-0.6l-5-1v-1.1h7.6c0.7,0,1.3-0.7,1.3-1.6V3.2C22.9,2.3,22.3,1.6,21.5,1.6z M21.1,17.3H3.3V3.7h17.8L21.1,17.3L21.1,17.3z`,
966
+ tooltip: 'Screen Record',
967
+ click: (editor) => {
968
+ navigator.mediaDevices.getDisplayMedia({ video: true })
969
+ .then((stream) => {
970
+ const chunks = [];
971
+ const options = { mimeType: 'video/webm; codecs=vp9' };
972
+ let mediaRecorder = new MediaRecorder(stream, options);
973
+ stream.addEventListener('inactive', (e) => {
974
+ mediaRecorder.stop();
975
+ mediaRecorder = null;
976
+ stream.getTracks().forEach((track) => track.stop());
977
+ stream = null;
978
+ const blob = new Blob(chunks, { type: 'video/webm' });
979
+ config.upload(blob)
980
+ .subscribe((url) => {
981
+ editor.html.insert(`<span class="fr-screen-record fr-deletable fr-draggable"><video controls style="max-width:${config.maxWidth}"><source src="${url}" type="video/webm"></video></span>`);
982
+ });
983
+ });
984
+ mediaRecorder.addEventListener('dataavailable', (event) => {
985
+ if (event.data && event.data.size > 0) {
986
+ chunks.push(event.data);
987
+ }
988
+ });
989
+ mediaRecorder.start(10);
990
+ })
991
+ .catch((e) => { });
992
+ }
993
+ }
994
+ ] });
995
+ }
996
+ initialize() {
997
+ return __awaiter(this, void 0, void 0, function* () {
998
+ yield ResourceLoader.loadStyles('froala')
999
+ .toPromise();
1000
+ const FroalaEditor = window.FroalaEditor;
1001
+ this.editor.events.on('mouseup', (e) => {
1002
+ const el = this._getEl(e.target);
1003
+ if (el) {
1004
+ this.editor.popups.hideAll();
1005
+ this._activeEl = el;
1006
+ this._showPopup(el);
1007
+ }
1008
+ });
1009
+ FroalaEditor.POPUP_TEMPLATES["screenRecordPlugin.popup"] = '[_BUTTONS_]';
1010
+ FroalaEditor.DefineIcon('popupClose', { NAME: 'trash', SVG_KEY: 'remove' });
1011
+ FroalaEditor.RegisterCommand('popupClose', {
1012
+ title: 'Remove',
1013
+ undo: false,
1014
+ focus: false,
1015
+ callback: () => {
1016
+ if (this._activeEl) {
1017
+ this._activeEl.remove();
1018
+ }
1019
+ }
1020
+ });
1021
+ });
1022
+ }
1023
+ _createPopup() {
1024
+ const template = {
1025
+ buttons: `<div class="fr-buttons">${this.editor.button.buildList(['popupClose'])}</div>`,
1026
+ };
1027
+ return this.editor.popups.create('screenRecordPlugin.popup', template);
1028
+ }
1029
+ _showPopup(el) {
1030
+ var $popup = this.editor.popups.get('screenRecordPlugin.popup');
1031
+ if (!$popup) {
1032
+ $popup = this._createPopup();
1033
+ }
1034
+ this.editor.popups.setContainer('screenRecordPlugin.popup', this.editor.$(el));
1035
+ const left = el.offsetLeft + el.offsetWidth / 2;
1036
+ this.editor.popups.show('screenRecordPlugin.popup', left, null);
1037
+ }
1038
+ _getEl(el) {
1039
+ if (el && el.classList) {
1040
+ if (el.classList.contains('fr-screen-record'))
1041
+ return el;
1042
+ return this._getEl(el.parentNode);
1043
+ }
1044
+ }
1040
1045
  }
1041
1046
 
1042
- /**
1043
- * Generated bundle index. Do not edit.
1047
+ /**
1048
+ * Generated bundle index. Do not edit.
1044
1049
  */
1045
1050
 
1046
1051
  export { ChecklistPlugin, CodePlugin, FS_HTML_EDITOR_CONFIG, FsHtmlEditorComponent, FsHtmlEditorModule, FsHtmlRendererComponent, MentionPlugin, ParagraphButtons, RichButtons, ScreenRecordPlugin, TextButtons };