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