@firestitch/html-editor 9.5.4 → 9.5.7

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.
@@ -4,8 +4,8 @@ import { CommonModule } from '@angular/common';
4
4
  import { FsSkeletonModule } from '@firestitch/skeleton';
5
5
  import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
6
6
  import { FileProcessor, FsFile } from '@firestitch/file';
7
- import { Observable, BehaviorSubject, combineLatest, ReplaySubject, Subject } from 'rxjs';
8
- import { shareReplay, map, tap, concatMap, takeUntil, switchMap, filter } from 'rxjs/operators';
7
+ import { Observable, BehaviorSubject, combineLatest, ReplaySubject, Subject, fromEventPattern } from 'rxjs';
8
+ import { shareReplay, map, tap, concatMap, switchMap, filter, startWith, distinctUntilChanged, skip, takeUntil } from 'rxjs/operators';
9
9
  import { merge, debounce } from 'lodash-es';
10
10
  import { DomSanitizer } from '@angular/platform-browser';
11
11
  import Tribute from 'tributejs';
@@ -134,7 +134,7 @@ let FsFroalaLoaderService = class FsFroalaLoaderService {
134
134
  this._defaultConfig = _defaultConfig;
135
135
  this._froalaLoaded = new BehaviorSubject(false);
136
136
  this._defaultPluginsLoaded = new BehaviorSubject(false);
137
- this._ready$ = combineLatest([this._froalaLoaded, this._defaultPluginsLoaded])
137
+ this._loaded$ = combineLatest([this._froalaLoaded, this._defaultPluginsLoaded])
138
138
  .pipe(map(([FroalaReady, pluginsRead]) => {
139
139
  return pluginsRead && FroalaReady;
140
140
  }), shareReplay(1));
@@ -148,8 +148,8 @@ let FsFroalaLoaderService = class FsFroalaLoaderService {
148
148
  }
149
149
  this._load();
150
150
  }
151
- get ready$() {
152
- return this._ready$;
151
+ get loaded$() {
152
+ return this._loaded$;
153
153
  }
154
154
  get ready() {
155
155
  return this._froalaLoaded.getValue() && this._defaultPluginsLoaded.getValue();
@@ -212,6 +212,7 @@ let FsHtmlEditorComponent = FsHtmlEditorComponent_1 = class FsHtmlEditorComponen
212
212
  this.config = {};
213
213
  this.initialized = false;
214
214
  this._initialize$ = new ReplaySubject();
215
+ this._froalaReady$ = new BehaviorSubject(null);
215
216
  this._destroy$ = new Subject();
216
217
  this.onChange = (data) => { };
217
218
  this.onTouched = () => { };
@@ -225,8 +226,8 @@ let FsHtmlEditorComponent = FsHtmlEditorComponent_1 = class FsHtmlEditorComponen
225
226
  get editor() {
226
227
  return this._editor;
227
228
  }
228
- get froalaReady$() {
229
- return this._fr.ready$;
229
+ get froalaLoaded$() {
230
+ return this._fr.loaded$;
230
231
  }
231
232
  ngOnInit() {
232
233
  this.config = this.config || {};
@@ -251,94 +252,9 @@ let FsHtmlEditorComponent = FsHtmlEditorComponent_1 = class FsHtmlEditorComponen
251
252
  initialize(options = {}) {
252
253
  const config = this._createConfig();
253
254
  this._initPlugins(config);
254
- this.el.innerHTML = this._html;
255
+ // this.el.innerHTML = this._html || '';
255
256
  this._editor = new this._fr.FroalaEditor(this.el, this._createOptions(), () => {
256
- this._cdRef.markForCheck();
257
- if (config.disabled) {
258
- this.disable();
259
- }
260
- if (config.froalaConfig.events) {
261
- if (config.froalaConfig.events.initialized) {
262
- config.froalaConfig.events.initialized();
263
- }
264
- }
265
- this._editor.events.on('focus', () => {
266
- this.classFocused = true;
267
- this._cdRef.markForCheck();
268
- });
269
- this._editor.events.on('blur', () => {
270
- this.classFocused = false;
271
- this._cdRef.markForCheck();
272
- });
273
- this._editor.events.on('contentChanged', () => {
274
- this._contentChanged();
275
- });
276
- this._editor.events.on('paste.afterCleanup', (html) => {
277
- var div = document.createElement('div');
278
- div.innerHTML = html;
279
- div.querySelectorAll('*')
280
- .forEach((el) => {
281
- for (var i = 0; i < el.attributes.length; i++) {
282
- const name = el.attributes[0].name;
283
- if (name !== 'href') {
284
- el.removeAttribute(name);
285
- }
286
- }
287
- });
288
- return div.innerHTML;
289
- });
290
- this._editor.events.on('keydown', () => {
291
- this.onTouched();
292
- });
293
- if (this.config.image && this.config.image.upload) {
294
- this._editor.events.on('image.beforePasteUpload', (image) => {
295
- fetch(image.getAttribute('src'))
296
- .then((res) => {
297
- res.blob()
298
- .then((blob) => {
299
- this._processImageUpload(blob)
300
- .pipe(takeUntil(this._destroy$))
301
- .subscribe((url) => {
302
- image.setAttribute('src', url);
303
- image.removeAttribute('data-fr-image-pasted');
304
- this._contentChanged();
305
- });
306
- });
307
- });
308
- return false;
309
- });
310
- this._editor.events.on('image.beforeUpload', (blobs) => {
311
- this._processImageUpload(blobs[0])
312
- .pipe(takeUntil(this._destroy$))
313
- .subscribe((url) => {
314
- this.editor.image.insert(url, null, null, this.editor.image.get());
315
- });
316
- return false;
317
- });
318
- }
319
- if (this.el.querySelector('.fr-second-toolbar')) {
320
- this.el.querySelector('.fr-second-toolbar').remove();
321
- }
322
- const selection = options.selection;
323
- if (selection) {
324
- const node = Array.from(this.el.querySelectorAll('.fr-element *')).find((node) => {
325
- return selection.node === node.textContent;
326
- });
327
- if (node) {
328
- const range = document.createRange();
329
- const sel = window.getSelection();
330
- range.setStart(this._getTextNode(node), selection.offset);
331
- range.collapse(true);
332
- sel.removeAllRanges();
333
- sel.addRange(range);
334
- }
335
- else if (this.config.autofocus) {
336
- this.focus();
337
- }
338
- }
339
- else if (this.config.autofocus) {
340
- this.focus();
341
- }
257
+ this._froalaReady$.next({ config, options });
342
258
  });
343
259
  }
344
260
  _processImageUpload(blob) {
@@ -423,21 +339,14 @@ let FsHtmlEditorComponent = FsHtmlEditorComponent_1 = class FsHtmlEditorComponen
423
339
  }
424
340
  writeValue(html) {
425
341
  this._html = html || '';
426
- this._cdRef.markForCheck();
427
- // SP-T2298 timeout needed for the case, when froala was already initated and
428
- // second instance opened in dialog/drawer. In this case init step pass much faster, writeValue trigger
429
- // after init and froala ignore new html. At the same time froala was initied but editor not yet, and next
430
- // code doesn't work without timeout. We've got time range when too late for initiate value, and to early
431
- // for change event.
432
- setTimeout(() => {
433
- if (this.editor && this.editor.html) {
434
- try {
435
- this.editor.html.set(this._html);
436
- }
437
- catch (e) {
438
- }
342
+ if (this.editor && this.editor.html) {
343
+ try {
344
+ this.editor.html.set(this._html);
439
345
  }
440
- });
346
+ catch (e) {
347
+ }
348
+ }
349
+ this._cdRef.markForCheck();
441
350
  }
442
351
  registerOnChange(fn) {
443
352
  this.onChange = fn;
@@ -463,7 +372,7 @@ let FsHtmlEditorComponent = FsHtmlEditorComponent_1 = class FsHtmlEditorComponen
463
372
  return merge({ froalaConfig: {} }, this._defaultConfig, this.config);
464
373
  }
465
374
  _change(html) {
466
- this._html = html;
375
+ // this._html = html;
467
376
  this.onChange(html);
468
377
  }
469
378
  _initPlugins(config) {
@@ -557,9 +466,8 @@ let FsHtmlEditorComponent = FsHtmlEditorComponent_1 = class FsHtmlEditorComponen
557
466
  }, config.froalaConfig);
558
467
  }
559
468
  _listenLazyInit() {
560
- this.froalaReady$
561
- .pipe(filter((ready) => ready), switchMap(() => this._initialize$))
562
- .subscribe(() => {
469
+ this.froalaLoaded$
470
+ .pipe(filter((ready) => ready), switchMap(() => this._initialize$), tap(() => {
563
471
  const selection = window.getSelection();
564
472
  const options = {};
565
473
  if (selection.baseNode) {
@@ -569,7 +477,10 @@ let FsHtmlEditorComponent = FsHtmlEditorComponent_1 = class FsHtmlEditorComponen
569
477
  };
570
478
  }
571
479
  this.initialize(options);
572
- });
480
+ }), switchMap(() => this._froalaReady$), filter((data) => !!data), tap(({ config, options }) => {
481
+ this._setupFroala(config, options);
482
+ }))
483
+ .subscribe();
573
484
  }
574
485
  _contentChanged() {
575
486
  const editorHTML = this._editor.html.get();
@@ -577,6 +488,105 @@ let FsHtmlEditorComponent = FsHtmlEditorComponent_1 = class FsHtmlEditorComponen
577
488
  this._change(editorHTML);
578
489
  }
579
490
  }
491
+ _setupFroala(config, options) {
492
+ this._cdRef.markForCheck();
493
+ this.setHtml(this._html);
494
+ if (config.disabled) {
495
+ this.disable();
496
+ }
497
+ if (config.froalaConfig.events) {
498
+ if (config.froalaConfig.events.initialized) {
499
+ config.froalaConfig.events.initialized();
500
+ }
501
+ }
502
+ this._editor.events.on('focus', () => {
503
+ this.classFocused = true;
504
+ this._cdRef.markForCheck();
505
+ });
506
+ this._editor.events.on('blur', () => {
507
+ this.classFocused = false;
508
+ this._cdRef.markForCheck();
509
+ });
510
+ fromEventPattern((handler) => {
511
+ this._editor.events.on('contentChanged', handler);
512
+ }, () => { })
513
+ .pipe(startWith(this._html), map(() => this.editor.html.get()), distinctUntilChanged(), skip(1))
514
+ .subscribe(() => {
515
+ this._contentChanged();
516
+ });
517
+ // this._editor.events.on('contentChanged', () => {
518
+ // console.log('test');
519
+ // if (!this._firstChange) {
520
+ // this._contentChanged();
521
+ // }
522
+ // });
523
+ this._editor.events.on('paste.afterCleanup', (html) => {
524
+ var div = document.createElement('div');
525
+ div.innerHTML = html;
526
+ div.querySelectorAll('*')
527
+ .forEach((el) => {
528
+ for (var i = 0; i < el.attributes.length; i++) {
529
+ const name = el.attributes[0].name;
530
+ if (name !== 'href') {
531
+ el.removeAttribute(name);
532
+ }
533
+ }
534
+ });
535
+ return div.innerHTML;
536
+ });
537
+ this._editor.events.on('keydown', () => {
538
+ this.onTouched();
539
+ });
540
+ if (this.config.image && this.config.image.upload) {
541
+ this._editor.events.on('image.beforePasteUpload', (image) => {
542
+ fetch(image.getAttribute('src'))
543
+ .then((res) => {
544
+ res.blob()
545
+ .then((blob) => {
546
+ this._processImageUpload(blob)
547
+ .pipe(takeUntil(this._destroy$))
548
+ .subscribe((url) => {
549
+ image.setAttribute('src', url);
550
+ image.removeAttribute('data-fr-image-pasted');
551
+ this._contentChanged();
552
+ });
553
+ });
554
+ });
555
+ return false;
556
+ });
557
+ this._editor.events.on('image.beforeUpload', (blobs) => {
558
+ this._processImageUpload(blobs[0])
559
+ .pipe(takeUntil(this._destroy$))
560
+ .subscribe((url) => {
561
+ this.editor.image.insert(url, null, null, this.editor.image.get());
562
+ });
563
+ return false;
564
+ });
565
+ }
566
+ if (this.el.querySelector('.fr-second-toolbar')) {
567
+ this.el.querySelector('.fr-second-toolbar').remove();
568
+ }
569
+ const selection = options.selection;
570
+ if (selection) {
571
+ const node = Array.from(this.el.querySelectorAll('.fr-element *')).find((node) => {
572
+ return selection.node === node.textContent;
573
+ });
574
+ if (node) {
575
+ const range = document.createRange();
576
+ const sel = window.getSelection();
577
+ range.setStart(this._getTextNode(node), selection.offset);
578
+ range.collapse(true);
579
+ sel.removeAllRanges();
580
+ sel.addRange(range);
581
+ }
582
+ else if (this.config.autofocus) {
583
+ this.focus();
584
+ }
585
+ }
586
+ else if (this.config.autofocus) {
587
+ this.focus();
588
+ }
589
+ }
580
590
  };
581
591
  FsHtmlEditorComponent.ctorParameters = () => [
582
592
  { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [FS_HTML_EDITOR_CONFIG,] }] },
@@ -602,7 +612,7 @@ __decorate([
602
612
  FsHtmlEditorComponent = FsHtmlEditorComponent_1 = __decorate([
603
613
  Component({
604
614
  selector: 'fs-html-editor',
605
- template: "<div class=\"fs-html-editor-default\">\n <ng-container *ngIf=\"initialized\">\n <ng-container *fsSkeleton=\"(froalaReady$ | 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",
615
+ 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",
606
616
  providers: [
607
617
  {
608
618
  provide: NG_VALUE_ACCESSOR,
@@ -616,7 +626,7 @@ FsHtmlEditorComponent = FsHtmlEditorComponent_1 = __decorate([
616
626
  },
617
627
  ],
618
628
  changeDetection: ChangeDetectionStrategy.OnPush,
619
- 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{position:-webkit-sticky;position:sticky;z-index:2;border-radius:0;background-color:transparent}:host ::ng-deep .fr-toolbar .fr-btn-grp{margin:0 20px 0 0}:host ::ng-deep .fr-toolbar .fr-btn-grp:nth-child(1):after,:host ::ng-deep .fr-toolbar .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 div:first-of-type:before{border:none}:host ::ng-deep .fr-toolbar.fr-toolbar-open .fr-newline{height:auto}:host ::ng-deep .fr-toolbar .fr-btn-grp:first-child{margin-left:0}:host ::ng-deep .fr-toolbar.fr-top{border:0}:host ::ng-deep .fr-toolbar .fr-newline{margin:0}:host ::ng-deep .fr-toolbar .fr-more-toolbar{z-index:auto}:host ::ng-deep .fr-toolbar .fr-command.fr-btn{z-index:auto}:host ::ng-deep .fr-toolbar .fr-command.fr-btn.fr-dropdown:after{right:3px}:host ::ng-deep .fr-toolbar .fr-command.fr-btn.fr-dropdown.fr-selection span{margin-right:24px}:host ::ng-deep .fr-toolbar .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}"]
629
+ 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{position:-webkit-sticky;position:sticky;z-index:2;border-radius:0;background-color:#fff}:host ::ng-deep .fr-toolbar .fr-btn-grp{margin:0 20px 0 0}:host ::ng-deep .fr-toolbar .fr-btn-grp:nth-child(1):after,:host ::ng-deep .fr-toolbar .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 div:first-of-type:before{border:none}:host ::ng-deep .fr-toolbar.fr-toolbar-open .fr-newline{height:auto}:host ::ng-deep .fr-toolbar .fr-btn-grp:first-child{margin-left:0}:host ::ng-deep .fr-toolbar.fr-top{border:0}:host ::ng-deep .fr-toolbar .fr-newline{margin:0}:host ::ng-deep .fr-toolbar .fr-more-toolbar{z-index:auto}:host ::ng-deep .fr-toolbar .fr-command.fr-btn{z-index:auto}:host ::ng-deep .fr-toolbar .fr-command.fr-btn.fr-dropdown:after{right:3px}:host ::ng-deep .fr-toolbar .fr-command.fr-btn.fr-dropdown.fr-selection span{margin-right:24px}:host ::ng-deep .fr-toolbar .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}"]
620
630
  }),
621
631
  __param(0, Optional()), __param(0, Inject(FS_HTML_EDITOR_CONFIG)),
622
632
  __metadata("design:paramtypes", [Object, ChangeDetectorRef,