@firestitch/html-editor 12.7.5 → 13.0.1

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