@lenne.tech/ng-base 0.3.1 → 0.3.5

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 (36) hide show
  1. package/bundles/lenne.tech-ng-base-src-lib-base.umd.js +568 -444
  2. package/bundles/lenne.tech-ng-base-src-lib-base.umd.js.map +1 -1
  3. package/bundles/lenne.tech-ng-base.umd.js +568 -444
  4. package/bundles/lenne.tech-ng-base.umd.js.map +1 -1
  5. package/esm2015/lib/base/classes/helper.class.js +19 -1
  6. package/esm2015/lib/base/directives/match-height.directive.js +50 -29
  7. package/esm2015/lib/base/factories/apollo-options.factory.js +3 -2
  8. package/esm2015/lib/base/public-api.js +2 -1
  9. package/esm2015/lib/base/services/image.service.js +99 -0
  10. package/esm2015/lib/base/services/scroll.service.js +16 -112
  11. package/esm2015/lib/base/services/seo.service.js +70 -14
  12. package/esm2015/src/lib/base/classes/helper.class.js +19 -1
  13. package/esm2015/src/lib/base/directives/match-height.directive.js +50 -29
  14. package/esm2015/src/lib/base/factories/apollo-options.factory.js +3 -2
  15. package/esm2015/src/lib/base/public-api.js +2 -1
  16. package/esm2015/src/lib/base/services/image.service.js +99 -0
  17. package/esm2015/src/lib/base/services/scroll.service.js +16 -112
  18. package/esm2015/src/lib/base/services/seo.service.js +70 -14
  19. package/fesm2015/lenne.tech-ng-base-src-lib-base.js +493 -402
  20. package/fesm2015/lenne.tech-ng-base-src-lib-base.js.map +1 -1
  21. package/fesm2015/lenne.tech-ng-base.js +493 -402
  22. package/fesm2015/lenne.tech-ng-base.js.map +1 -1
  23. package/lib/base/classes/helper.class.d.ts +7 -0
  24. package/lib/base/directives/match-height.directive.d.ts +10 -6
  25. package/lib/base/public-api.d.ts +1 -0
  26. package/lib/base/services/image.service.d.ts +24 -0
  27. package/lib/base/services/scroll.service.d.ts +6 -23
  28. package/lib/base/services/seo.service.d.ts +36 -3
  29. package/package.json +1 -1
  30. package/src/lib/base/classes/helper.class.d.ts +7 -0
  31. package/src/lib/base/directives/match-height.directive.d.ts +10 -6
  32. package/src/lib/base/lenne.tech-ng-base-src-lib-base.metadata.json +1 -1
  33. package/src/lib/base/public-api.d.ts +1 -0
  34. package/src/lib/base/services/image.service.d.ts +24 -0
  35. package/src/lib/base/services/scroll.service.d.ts +6 -23
  36. package/src/lib/base/services/seo.service.d.ts +36 -3
@@ -2,19 +2,20 @@ import * as i1 from 'apollo-angular';
2
2
  import { APOLLO_OPTIONS, gql, Apollo } from 'apollo-angular';
3
3
  import { HttpLink } from 'apollo-angular/http';
4
4
  import * as i0 from '@angular/core';
5
- import { InjectionToken, Injectable, Inject, Directive, ElementRef, Input, Output, HostBinding, HostListener, Pipe, NgModule } from '@angular/core';
5
+ import { InjectionToken, Injectable, Inject, Directive, ElementRef, Input, Output, HostBinding, PLATFORM_ID, HostListener, Pipe, NgModule } from '@angular/core';
6
6
  import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
7
7
  import { ApolloLink, split, concat } from '@apollo/client/core';
8
8
  import { InMemoryCache } from '@apollo/client/cache';
9
9
  import { WebSocketLink } from '@apollo/client/link/ws';
10
10
  import { getMainDefinition } from '@apollo/client/utilities';
11
+ import { __awaiter } from 'tslib';
12
+ import { isPlatformBrowser } from '@angular/common';
11
13
  import * as i1$2 from '@angular/platform-browser';
12
14
  import { DomSanitizer, Title, Meta } from '@angular/platform-browser';
13
15
  import * as i1$1 from '@angular/router';
14
16
  import { Router } from '@angular/router';
15
17
  import { buildClientSchema, getIntrospectionQuery } from 'graphql';
16
18
  import { map } from 'rxjs/operators';
17
- import { __awaiter } from 'tslib';
18
19
 
19
20
  const BASE_MODULE_CONFIG = new InjectionToken('BASE_MODULE_CONFIG');
20
21
 
@@ -212,8 +213,9 @@ AuthService.ctorParameters = () => [
212
213
  */
213
214
  function apolloOptionsFactory(baseModuleConfig, httpLink, authService) {
214
215
  const links = [];
216
+ const defaultUrl = 'api.' + window.location.href + '/graphql';
215
217
  const http = httpLink.create({
216
- uri: baseModuleConfig.apiUrl,
218
+ uri: baseModuleConfig.apiUrl ? baseModuleConfig.apiUrl : 'https://' + defaultUrl,
217
219
  });
218
220
  const authMiddleware = new ApolloLink((operation, forward) => {
219
221
  const headers = {};
@@ -418,238 +420,6 @@ LazyLoadDirective.propDecorators = {
418
420
  src: [{ type: Input }]
419
421
  };
420
422
 
421
- /**
422
- * Set same height (maximum) to different elements
423
- */
424
- class MatchHeightDirective {
425
- constructor(el) {
426
- this.el = el;
427
- }
428
- onResize() {
429
- this.matchHeight(this.el.nativeElement, this.ltMatchHeight);
430
- }
431
- ngAfterViewChecked() {
432
- this.matchHeight(this.el.nativeElement, this.ltMatchHeight);
433
- }
434
- matchHeight(parent, target) {
435
- if (!parent) {
436
- return;
437
- }
438
- let classNames;
439
- if (!Array.isArray(target)) {
440
- classNames = [target];
441
- }
442
- else {
443
- classNames = target;
444
- }
445
- classNames.forEach((className) => {
446
- // find all child structureElements with the selected class name
447
- const children = parent.getElementsByClassName(className);
448
- if (!children) {
449
- return;
450
- }
451
- // reset all children height
452
- Array.from(children).forEach((x) => {
453
- x.style.height = 'initial';
454
- });
455
- // get all the child structureElements heights
456
- const itemHeights = Array.from(children).map((x) => x.getBoundingClientRect().height);
457
- // find out the tallest one
458
- const maxHeight = itemHeights.reduce((prev, curr) => (curr > prev ? curr : prev), 0);
459
- // update the child structureElements height to the tallest one
460
- Array.from(children).forEach((x) => (x.style.height = `${maxHeight}px`));
461
- });
462
- }
463
- }
464
- MatchHeightDirective.decorators = [
465
- { type: Directive, args: [{
466
- selector: '[ltMatchHeight]',
467
- },] }
468
- ];
469
- MatchHeightDirective.ctorParameters = () => [
470
- { type: ElementRef }
471
- ];
472
- MatchHeightDirective.propDecorators = {
473
- ltMatchHeight: [{ type: Input }],
474
- onResize: [{ type: HostListener, args: ['window:resize',] }]
475
- };
476
-
477
- /**
478
- * Reszise detection
479
- * (inpsired by: https://stackblitz.com/edit/angular-resizable)
480
- */
481
- class ResizableDirective {
482
- constructor(el) {
483
- this.el = el;
484
- this.resizableGrabWidth = 8;
485
- this.resizableMinWidth = 10;
486
- this.dragging = false;
487
- }
488
- ngOnInit() {
489
- this.addListenerToDocument();
490
- this.addListenerToElement();
491
- }
492
- mouseMoveG(evt) {
493
- if (!this.dragging) {
494
- return;
495
- }
496
- this.newWidth(evt.clientX - this.el.nativeElement.offsetLeft);
497
- evt.stopPropagation();
498
- }
499
- newWidth(wid) {
500
- const result = Math.max(this.resizableMinWidth, wid);
501
- this.el.nativeElement.style.width = result + 'px';
502
- }
503
- mouseDown(evt) {
504
- if (this.inDragRegion(evt)) {
505
- this.dragging = true;
506
- this.preventGlobalMouseEvents();
507
- evt.stopPropagation();
508
- }
509
- }
510
- restoreGlobalMouseEvents() {
511
- document.body.style['pointer-events'] = 'auto';
512
- }
513
- mouseUpG(evt) {
514
- if (!this.dragging) {
515
- return;
516
- }
517
- this.restoreGlobalMouseEvents();
518
- this.dragging = false;
519
- evt.stopPropagation();
520
- }
521
- mouseMove(evt) {
522
- if (this.inDragRegion(evt) || this.dragging) {
523
- this.el.nativeElement.style.cursor = 'col-resize';
524
- }
525
- else {
526
- this.el.nativeElement.style.cursor = 'default';
527
- }
528
- }
529
- preventGlobalMouseEvents() {
530
- document.body.style['pointer-events'] = 'none';
531
- }
532
- inDragRegion(evt) {
533
- return this.el.nativeElement.clientWidth - evt.clientX + this.el.nativeElement.offsetLeft < this.resizableGrabWidth;
534
- }
535
- addListenerToDocument() {
536
- document.addEventListener('mousemove', (e) => {
537
- this.mouseMoveG(e);
538
- }, true);
539
- document.addEventListener('mouseup', (e) => {
540
- this.mouseUpG(e);
541
- }, true);
542
- }
543
- addListenerToElement() {
544
- this.el.nativeElement.addEventListener('mousedown', (e) => {
545
- this.mouseDown(e);
546
- }, true);
547
- this.el.nativeElement.addEventListener('mousemove', (e) => {
548
- this.mouseMove(e);
549
- }, true);
550
- }
551
- }
552
- ResizableDirective.decorators = [
553
- { type: Directive, args: [{
554
- selector: '[ltResizable]',
555
- },] }
556
- ];
557
- ResizableDirective.ctorParameters = () => [
558
- { type: ElementRef }
559
- ];
560
- ResizableDirective.propDecorators = {
561
- resizableGrabWidth: [{ type: Input }],
562
- resizableMinWidth: [{ type: Input }]
563
- };
564
-
565
- /**
566
- * Truncate string with ellipses
567
- */
568
- class EllipsesPipe {
569
- transform(value, limit) {
570
- if (!value) {
571
- return null;
572
- }
573
- return value.length > limit ? value.substring(0, limit) + '...' : value;
574
- }
575
- }
576
- EllipsesPipe.decorators = [
577
- { type: Pipe, args: [{
578
- name: 'ltEllipses',
579
- },] }
580
- ];
581
-
582
- /**
583
- * Declarate html as safe
584
- */
585
- class SafeHtmlPipe {
586
- constructor(sanitized) {
587
- this.sanitized = sanitized;
588
- }
589
- transform(value) {
590
- return this.sanitized.bypassSecurityTrustHtml(value);
591
- }
592
- }
593
- SafeHtmlPipe.decorators = [
594
- { type: Pipe, args: [{ name: 'ltSafeHtml' },] }
595
- ];
596
- SafeHtmlPipe.ctorParameters = () => [
597
- { type: DomSanitizer }
598
- ];
599
-
600
- // Imported and exported elements
601
- const elements = [
602
- // Directives
603
- ContextMenuDirective,
604
- LazyLoadDirective,
605
- MatchHeightDirective,
606
- ResizableDirective,
607
- // Pipes
608
- EllipsesPipe,
609
- SafeHtmlPipe,
610
- ];
611
- /**
612
- * Base module
613
- */
614
- class BaseModule {
615
- /**
616
- * Set configuration of base module in root module of the app
617
- */
618
- static forRoot(baseModuleConfig = {}) {
619
- // Default config
620
- const config = Object.assign({ apiUrl: 'localhost:3000', authGuardRedirectUrl: '/auth', logging: false, version: null, prefix: null, scrollDetectionOffset: 200, scrollOffset: 100, storageType: 'local' }, baseModuleConfig);
621
- // Prepare providers
622
- const providers = [
623
- {
624
- provide: BASE_MODULE_CONFIG,
625
- useValue: baseModuleConfig,
626
- },
627
- ];
628
- // Add apollo features if API URL is set
629
- if (baseModuleConfig.apiUrl) {
630
- providers.push({
631
- provide: APOLLO_OPTIONS,
632
- useFactory: apolloOptionsFactory,
633
- deps: [BASE_MODULE_CONFIG, HttpLink, AuthService],
634
- });
635
- }
636
- else if (config.logging) {
637
- console.log('apiUrl is missing, ApolloLink is not initialized');
638
- }
639
- // Return root module
640
- return {
641
- ngModule: BaseModule,
642
- providers,
643
- };
644
- }
645
- }
646
- BaseModule.decorators = [
647
- { type: NgModule, args: [{
648
- declarations: elements,
649
- exports: elements,
650
- },] }
651
- ];
652
-
653
423
  /**
654
424
  * Helper class
655
425
  */
@@ -868,59 +638,422 @@ class Helper {
868
638
  if (a === b) {
869
639
  return true;
870
640
  }
871
- // Different type
872
- if (typeof a !== typeof b) {
873
- return false;
641
+ // Different type
642
+ if (typeof a !== typeof b) {
643
+ return false;
644
+ }
645
+ // Process array
646
+ if (Array.isArray(a)) {
647
+ for (const [key, value] of Object.entries(a)) {
648
+ if (Helper.isSubset(value, b[key])) {
649
+ return false;
650
+ }
651
+ }
652
+ return true;
653
+ }
654
+ // Process object
655
+ if (typeof a === 'object') {
656
+ const plain = JSON.parse(JSON.stringify(a));
657
+ for (const [key, value] of Object.entries(plain)) {
658
+ if (Helper.isSubset(value, b[key])) {
659
+ return false;
660
+ }
661
+ }
662
+ return true;
663
+ }
664
+ // Else
665
+ return true;
666
+ }
667
+ /**
668
+ * Whether parameters are equal (have equal properties)
669
+ * Hint: functions will be ignored
670
+ */
671
+ static isEqual(a, b) {
672
+ return Helper.isSubset(a, b) && Helper.isSubset(b, a);
673
+ }
674
+ /**
675
+ * First level difference of two objects
676
+ * Hint: functions will be ignored and when an array or an object is different the hole array or object will used as property value
677
+ *
678
+ * @returns Record<string, any> Object with properties from first object
679
+ */
680
+ static getDiff(a, b, clone = true) {
681
+ const result = {};
682
+ let firstObject = a;
683
+ if (clone) {
684
+ firstObject = JSON.parse(JSON.stringify(a));
685
+ }
686
+ for (const [key, value] of Object.entries(firstObject)) {
687
+ if (!Helper.isSubset(value, b[key])) {
688
+ result[key] = value;
689
+ }
690
+ }
691
+ return result;
692
+ }
693
+ /**
694
+ * Check if propertie is type of HTMLCollection or NodeList
695
+ * See https://stackoverflow.com/a/7238344
696
+ *
697
+ * @param nodes
698
+ */
699
+ static isNodeList(nodes) {
700
+ try {
701
+ const stringRepr = Object.prototype.toString.call(nodes);
702
+ return (typeof nodes === 'object' &&
703
+ /^\[object (HTMLCollection|NodeList|Object)\]$/.test(stringRepr) &&
704
+ typeof nodes.length === 'number' &&
705
+ (nodes.length === 0 || (typeof nodes[0] === 'object' && nodes[0].nodeType > 0)));
706
+ }
707
+ catch (e) {
708
+ return false;
709
+ }
710
+ }
711
+ }
712
+
713
+ /**
714
+ * Image service
715
+ */
716
+ class ImageService {
717
+ /**
718
+ * Preaload images (for lazy handling)
719
+ *
720
+ * @param options
721
+ * @protected
722
+ */
723
+ preLoadImages(options) {
724
+ // Init configuration
725
+ const config = Object.assign({ doneFunnction: undefined, elements: document, regExp: undefined }, options);
726
+ return new Promise((resolve) => {
727
+ var _a;
728
+ // Convert element(s) into array
729
+ if (!Array.isArray(config.elements)) {
730
+ // NodeList or HTMLCollection
731
+ if (Helper.isNodeList(config.elements)) {
732
+ config.elements = Array.from(config.elements);
733
+ }
734
+ // Single element
735
+ else {
736
+ config.elements = [config.elements];
737
+ }
738
+ }
739
+ // Init loading
740
+ const promises = [];
741
+ // Get all images
742
+ let images = [];
743
+ config.elements.forEach((element) => {
744
+ images = images.concat(Array.from(element.getElementsByTagName('img')));
745
+ });
746
+ // Prepare images
747
+ for (let image of images) {
748
+ const src = image.getAttribute('src');
749
+ // Check if changes are necessary and possible
750
+ if ((image.complete && image.naturalHeight !== 0) || // already laoded
751
+ !src || // Image has no src
752
+ ((_a = config.regExp) === null || _a === void 0 ? void 0 : _a.test(src)) // Test if src matches regular expression
753
+ ) {
754
+ continue;
755
+ }
756
+ // Replace lazy loading image with immediately loading image
757
+ if (image.getAttribute('loading') === 'lazy') {
758
+ const newImage = document.createElement('img');
759
+ Array.from(image.attributes).forEach((attr) => {
760
+ if (attr.name !== 'loading' && attr.name !== 'src') {
761
+ newImage.setAttribute(attr.name, attr.value);
762
+ }
763
+ });
764
+ image.replaceWith(newImage);
765
+ image = newImage;
766
+ }
767
+ // Wait for laoding
768
+ promises.push(this.loadImage(image, src));
769
+ }
770
+ // Wait until all images loaded
771
+ Promise.all(promises)
772
+ .catch((e) => console.error('Error during image loading', e))
773
+ .finally(() => __awaiter(this, void 0, void 0, function* () {
774
+ // Return result
775
+ let result;
776
+ if (config.doneFunnction) {
777
+ result = yield config.doneFunction();
778
+ }
779
+ resolve(result);
780
+ }));
781
+ });
782
+ }
783
+ /**
784
+ * Load image for image element
785
+ *
786
+ * @param imageElement
787
+ * @param src
788
+ * @protected
789
+ */
790
+ loadImage(imageElement, src) {
791
+ return __awaiter(this, void 0, void 0, function* () {
792
+ src = src || imageElement.src;
793
+ return new Promise((resolve, reject) => {
794
+ imageElement.onload = () => resolve(imageElement);
795
+ imageElement.onerror = reject;
796
+ imageElement.src = src;
797
+ });
798
+ });
799
+ }
800
+ }
801
+ ImageService.ɵprov = i0.ɵɵdefineInjectable({ factory: function ImageService_Factory() { return new ImageService(); }, token: ImageService, providedIn: "root" });
802
+ ImageService.decorators = [
803
+ { type: Injectable, args: [{
804
+ providedIn: 'root',
805
+ },] }
806
+ ];
807
+
808
+ /**
809
+ * Set same height (maximum) to different elements
810
+ */
811
+ class MatchHeightDirective {
812
+ constructor(el, imageService, platformId) {
813
+ this.el = el;
814
+ this.imageService = imageService;
815
+ // Replace lazy loading images with immediate loading images
816
+ this.loadImages = true;
817
+ this.isBrowser = isPlatformBrowser(platformId);
818
+ }
819
+ onResize() {
820
+ this.matchHeight(this.el.nativeElement, this.ltMatchHeight);
821
+ }
822
+ ngOnInit() {
823
+ this.matchHeight(this.el.nativeElement, this.ltMatchHeight);
824
+ }
825
+ matchHeight(parent, target) {
826
+ return __awaiter(this, void 0, void 0, function* () {
827
+ if (!parent) {
828
+ return;
829
+ }
830
+ let classNames;
831
+ if (!Array.isArray(target)) {
832
+ classNames = [target];
833
+ }
834
+ else {
835
+ classNames = target;
836
+ }
837
+ for (const className of classNames) {
838
+ // find all child structureElements with the selected class name
839
+ const children = parent.getElementsByClassName(className);
840
+ if (!children || children.length < 2) {
841
+ continue;
842
+ }
843
+ // Load images of children
844
+ if (this.loadImages) {
845
+ yield this.imageService.preLoadImages({ elements: children });
846
+ }
847
+ // reset all children height
848
+ Array.from(children).forEach((element) => {
849
+ element.style.height = 'initial';
850
+ });
851
+ // get all the child structureElements heights
852
+ const itemHeights = Array.from(children).map((element) => {
853
+ if (element.getBoundingClientRect) {
854
+ return element.getBoundingClientRect().height;
855
+ }
856
+ return 0;
857
+ });
858
+ // find out the tallest one
859
+ const maxHeight = itemHeights.reduce((prev, curr) => (curr > prev ? curr : prev), 0);
860
+ // update the child structureElements height to the tallest one
861
+ Array.from(children).forEach((element) => (element.style.height = `${maxHeight}px`));
862
+ }
863
+ });
864
+ }
865
+ }
866
+ MatchHeightDirective.decorators = [
867
+ { type: Directive, args: [{
868
+ selector: '[ltMatchHeight]',
869
+ },] }
870
+ ];
871
+ MatchHeightDirective.ctorParameters = () => [
872
+ { type: ElementRef },
873
+ { type: ImageService },
874
+ { type: String, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] }
875
+ ];
876
+ MatchHeightDirective.propDecorators = {
877
+ ltMatchHeight: [{ type: Input }],
878
+ loadImages: [{ type: Input }],
879
+ onResize: [{ type: HostListener, args: ['window:resize',] }]
880
+ };
881
+
882
+ /**
883
+ * Reszise detection
884
+ * (inpsired by: https://stackblitz.com/edit/angular-resizable)
885
+ */
886
+ class ResizableDirective {
887
+ constructor(el) {
888
+ this.el = el;
889
+ this.resizableGrabWidth = 8;
890
+ this.resizableMinWidth = 10;
891
+ this.dragging = false;
892
+ }
893
+ ngOnInit() {
894
+ this.addListenerToDocument();
895
+ this.addListenerToElement();
896
+ }
897
+ mouseMoveG(evt) {
898
+ if (!this.dragging) {
899
+ return;
900
+ }
901
+ this.newWidth(evt.clientX - this.el.nativeElement.offsetLeft);
902
+ evt.stopPropagation();
903
+ }
904
+ newWidth(wid) {
905
+ const result = Math.max(this.resizableMinWidth, wid);
906
+ this.el.nativeElement.style.width = result + 'px';
907
+ }
908
+ mouseDown(evt) {
909
+ if (this.inDragRegion(evt)) {
910
+ this.dragging = true;
911
+ this.preventGlobalMouseEvents();
912
+ evt.stopPropagation();
913
+ }
914
+ }
915
+ restoreGlobalMouseEvents() {
916
+ document.body.style['pointer-events'] = 'auto';
917
+ }
918
+ mouseUpG(evt) {
919
+ if (!this.dragging) {
920
+ return;
921
+ }
922
+ this.restoreGlobalMouseEvents();
923
+ this.dragging = false;
924
+ evt.stopPropagation();
925
+ }
926
+ mouseMove(evt) {
927
+ if (this.inDragRegion(evt) || this.dragging) {
928
+ this.el.nativeElement.style.cursor = 'col-resize';
874
929
  }
875
- // Process array
876
- if (Array.isArray(a)) {
877
- for (const [key, value] of Object.entries(a)) {
878
- if (Helper.isSubset(value, b[key])) {
879
- return false;
880
- }
881
- }
882
- return true;
930
+ else {
931
+ this.el.nativeElement.style.cursor = 'default';
883
932
  }
884
- // Process object
885
- if (typeof a === 'object') {
886
- const plain = JSON.parse(JSON.stringify(a));
887
- for (const [key, value] of Object.entries(plain)) {
888
- if (Helper.isSubset(value, b[key])) {
889
- return false;
890
- }
891
- }
892
- return true;
933
+ }
934
+ preventGlobalMouseEvents() {
935
+ document.body.style['pointer-events'] = 'none';
936
+ }
937
+ inDragRegion(evt) {
938
+ return this.el.nativeElement.clientWidth - evt.clientX + this.el.nativeElement.offsetLeft < this.resizableGrabWidth;
939
+ }
940
+ addListenerToDocument() {
941
+ document.addEventListener('mousemove', (e) => {
942
+ this.mouseMoveG(e);
943
+ }, true);
944
+ document.addEventListener('mouseup', (e) => {
945
+ this.mouseUpG(e);
946
+ }, true);
947
+ }
948
+ addListenerToElement() {
949
+ this.el.nativeElement.addEventListener('mousedown', (e) => {
950
+ this.mouseDown(e);
951
+ }, true);
952
+ this.el.nativeElement.addEventListener('mousemove', (e) => {
953
+ this.mouseMove(e);
954
+ }, true);
955
+ }
956
+ }
957
+ ResizableDirective.decorators = [
958
+ { type: Directive, args: [{
959
+ selector: '[ltResizable]',
960
+ },] }
961
+ ];
962
+ ResizableDirective.ctorParameters = () => [
963
+ { type: ElementRef }
964
+ ];
965
+ ResizableDirective.propDecorators = {
966
+ resizableGrabWidth: [{ type: Input }],
967
+ resizableMinWidth: [{ type: Input }]
968
+ };
969
+
970
+ /**
971
+ * Truncate string with ellipses
972
+ */
973
+ class EllipsesPipe {
974
+ transform(value, limit) {
975
+ if (!value) {
976
+ return null;
893
977
  }
894
- // Else
895
- return true;
978
+ return value.length > limit ? value.substring(0, limit) + '...' : value;
896
979
  }
897
- /**
898
- * Whether parameters are equal (have equal properties)
899
- * Hint: functions will be ignored
900
- */
901
- static isEqual(a, b) {
902
- return Helper.isSubset(a, b) && Helper.isSubset(b, a);
980
+ }
981
+ EllipsesPipe.decorators = [
982
+ { type: Pipe, args: [{
983
+ name: 'ltEllipses',
984
+ },] }
985
+ ];
986
+
987
+ /**
988
+ * Declarate html as safe
989
+ */
990
+ class SafeHtmlPipe {
991
+ constructor(sanitized) {
992
+ this.sanitized = sanitized;
993
+ }
994
+ transform(value) {
995
+ return this.sanitized.bypassSecurityTrustHtml(value);
903
996
  }
997
+ }
998
+ SafeHtmlPipe.decorators = [
999
+ { type: Pipe, args: [{ name: 'ltSafeHtml' },] }
1000
+ ];
1001
+ SafeHtmlPipe.ctorParameters = () => [
1002
+ { type: DomSanitizer }
1003
+ ];
1004
+
1005
+ // Imported and exported elements
1006
+ const elements = [
1007
+ // Directives
1008
+ ContextMenuDirective,
1009
+ LazyLoadDirective,
1010
+ MatchHeightDirective,
1011
+ ResizableDirective,
1012
+ // Pipes
1013
+ EllipsesPipe,
1014
+ SafeHtmlPipe,
1015
+ ];
1016
+ /**
1017
+ * Base module
1018
+ */
1019
+ class BaseModule {
904
1020
  /**
905
- * First level difference of two objects
906
- * Hint: functions will be ignored and when an array or an object is different the hole array or object will used as property value
907
- *
908
- * @returns Record<string, any> Object with properties from first object
1021
+ * Set configuration of base module in root module of the app
909
1022
  */
910
- static getDiff(a, b, clone = true) {
911
- const result = {};
912
- let firstObject = a;
913
- if (clone) {
914
- firstObject = JSON.parse(JSON.stringify(a));
1023
+ static forRoot(baseModuleConfig = {}) {
1024
+ // Default config
1025
+ const config = Object.assign({ apiUrl: 'localhost:3000', authGuardRedirectUrl: '/auth', logging: false, version: null, prefix: null, scrollDetectionOffset: 200, scrollOffset: 100, storageType: 'local' }, baseModuleConfig);
1026
+ // Prepare providers
1027
+ const providers = [
1028
+ {
1029
+ provide: BASE_MODULE_CONFIG,
1030
+ useValue: baseModuleConfig,
1031
+ },
1032
+ ];
1033
+ // Add apollo features if API URL is set
1034
+ if (baseModuleConfig.apiUrl) {
1035
+ providers.push({
1036
+ provide: APOLLO_OPTIONS,
1037
+ useFactory: apolloOptionsFactory,
1038
+ deps: [BASE_MODULE_CONFIG, HttpLink, AuthService],
1039
+ });
915
1040
  }
916
- for (const [key, value] of Object.entries(firstObject)) {
917
- if (!Helper.isSubset(value, b[key])) {
918
- result[key] = value;
919
- }
1041
+ else if (config.logging) {
1042
+ console.log('apiUrl is missing, ApolloLink is not initialized');
920
1043
  }
921
- return result;
1044
+ // Return root module
1045
+ return {
1046
+ ngModule: BaseModule,
1047
+ providers,
1048
+ };
922
1049
  }
923
1050
  }
1051
+ BaseModule.decorators = [
1052
+ { type: NgModule, args: [{
1053
+ declarations: elements,
1054
+ exports: elements,
1055
+ },] }
1056
+ ];
924
1057
 
925
1058
  /**
926
1059
  * Standard class
@@ -2001,9 +2134,10 @@ class ScrollService {
2001
2134
  /**
2002
2135
  * Initializer
2003
2136
  */
2004
- constructor(router, moduleConfig) {
2137
+ constructor(router, moduleConfig, imageService) {
2005
2138
  this.router = router;
2006
2139
  this.moduleConfig = moduleConfig;
2140
+ this.imageService = imageService;
2007
2141
  /**
2008
2142
  * Is a scroll listener set
2009
2143
  *
@@ -2053,7 +2187,7 @@ class ScrollService {
2053
2187
  }
2054
2188
  // Load images before scrolling (once)
2055
2189
  if (config.loadImagesBefore) {
2056
- yield this.preLoadImages();
2190
+ yield this.imageService.preLoadImages();
2057
2191
  }
2058
2192
  // Get get bounding rect of target element
2059
2193
  const cords = el.getBoundingClientRect();
@@ -2105,37 +2239,16 @@ class ScrollService {
2105
2239
  * Scroll done promise
2106
2240
  */
2107
2241
  scrollDone(options = {}) {
2108
- // Init configuration
2109
- const config = Object.assign({ doneFunction: undefined, timeout: undefined }, options);
2110
- // Return promise for scroll done
2111
- return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
2112
- // Init variables
2113
- let rejected = false;
2114
- let timeoutId;
2115
- // Set timeout handling if necessary
2116
- if (config.timeout) {
2117
- timeoutId = window.setTimeout(() => {
2118
- rejected = true;
2119
- reject(new Error('Timeout for scrollDone after:' + config.timeout));
2120
- }, config.timeout);
2121
- }
2242
+ return __awaiter(this, void 0, void 0, function* () {
2243
+ // Init configuration
2244
+ const config = Object.assign({ doneFunction: undefined }, options);
2122
2245
  // Wait for scroll done
2123
2246
  yield this.scrollDoneDetection();
2124
- // Resolve promise if not already rejected
2125
- if (!rejected) {
2126
- // Stop timeout if set
2127
- if (timeoutId) {
2128
- window.clearTimeout(timeoutId);
2129
- }
2130
- // Get result (if doneFunction is set)
2131
- let result;
2132
- if (config.doneFunction) {
2133
- result = yield config.doneFunction();
2134
- }
2135
- // Scroll done
2136
- resolve(result);
2247
+ // Get result
2248
+ if (config.doneFunction) {
2249
+ return config.doneFunction();
2137
2250
  }
2138
- }));
2251
+ });
2139
2252
  }
2140
2253
  /**
2141
2254
  * Scroll done detecton
@@ -2164,87 +2277,8 @@ class ScrollService {
2164
2277
  // Return promise
2165
2278
  return this.scrollPromise;
2166
2279
  }
2167
- /**
2168
- * Preaload images (for lazy handling)
2169
- *
2170
- * @param options
2171
- * @protected
2172
- */
2173
- preLoadImages(options) {
2174
- // Init configuration
2175
- const config = Object.assign({ doneFunnction: undefined, timeout: undefined }, options);
2176
- return new Promise((resolve) => {
2177
- // Init variables
2178
- let resolved = false;
2179
- let timeoutId;
2180
- // Init loading
2181
- const images = Array.from(document.getElementsByTagName('img'));
2182
- const promises = [];
2183
- // Prepare images
2184
- for (const image of images) {
2185
- const src = image.getAttribute('src');
2186
- // Check if changes are necessary and possible
2187
- if ((image.complete && image.naturalHeight !== 0) || image.getAttribute('loading') !== 'lazy' || !src) {
2188
- continue;
2189
- }
2190
- const newImage = document.createElement('img');
2191
- Array.from(image.attributes).forEach((attr) => {
2192
- if (attr.name !== 'loading' && attr.name !== 'src') {
2193
- newImage.setAttribute(attr.name, attr.value);
2194
- }
2195
- });
2196
- image.replaceWith(newImage);
2197
- promises.push(this.loadImage(newImage, src));
2198
- }
2199
- if (config.timeout) {
2200
- timeoutId = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
2201
- resolved = true;
2202
- if (config.doneFunnction) {
2203
- yield config.doneFunction();
2204
- }
2205
- resolve(undefined);
2206
- }));
2207
- }
2208
- // Wait until all images loaded
2209
- Promise.all(promises)
2210
- .catch((e) => console.error('Error during image loading', e))
2211
- .finally(() => __awaiter(this, void 0, void 0, function* () {
2212
- // Check if already resolved
2213
- if (resolved) {
2214
- return;
2215
- }
2216
- // Stop timeout
2217
- if (timeoutId) {
2218
- clearTimeout(timeoutId);
2219
- }
2220
- // Return result
2221
- let result;
2222
- if (config.doneFunnction) {
2223
- result = yield config.doneFunction();
2224
- }
2225
- resolve(result);
2226
- }));
2227
- });
2228
- }
2229
- /**
2230
- * Load image for image element
2231
- *
2232
- * @param imageElement
2233
- * @param src
2234
- * @protected
2235
- */
2236
- loadImage(imageElement, src) {
2237
- return __awaiter(this, void 0, void 0, function* () {
2238
- src = src || imageElement.src;
2239
- return new Promise((resolve, reject) => {
2240
- imageElement.onload = () => resolve(imageElement);
2241
- imageElement.onerror = reject;
2242
- imageElement.src = src;
2243
- });
2244
- });
2245
- }
2246
2280
  }
2247
- ScrollService.ɵprov = i0.ɵɵdefineInjectable({ factory: function ScrollService_Factory() { return new ScrollService(i0.ɵɵinject(i1$1.Router), i0.ɵɵinject(BASE_MODULE_CONFIG)); }, token: ScrollService, providedIn: "root" });
2281
+ ScrollService.ɵprov = i0.ɵɵdefineInjectable({ factory: function ScrollService_Factory() { return new ScrollService(i0.ɵɵinject(i1$1.Router), i0.ɵɵinject(BASE_MODULE_CONFIG), i0.ɵɵinject(ImageService)); }, token: ScrollService, providedIn: "root" });
2248
2282
  ScrollService.decorators = [
2249
2283
  { type: Injectable, args: [{
2250
2284
  providedIn: 'root',
@@ -2252,7 +2286,8 @@ ScrollService.decorators = [
2252
2286
  ];
2253
2287
  ScrollService.ctorParameters = () => [
2254
2288
  { type: Router },
2255
- { type: undefined, decorators: [{ type: Inject, args: [BASE_MODULE_CONFIG,] }] }
2289
+ { type: undefined, decorators: [{ type: Inject, args: [BASE_MODULE_CONFIG,] }] },
2290
+ { type: ImageService }
2256
2291
  ];
2257
2292
 
2258
2293
  /**
@@ -2263,23 +2298,79 @@ class SEOService {
2263
2298
  this.titleService = titleService;
2264
2299
  this.metaTagService = metaTagService;
2265
2300
  }
2266
- initPageForSEO(title, description, keywords) {
2267
- this.setPageTitle(title);
2268
- this.setPageDescription(description);
2269
- if (keywords) {
2270
- this.setPageKeywords(keywords);
2301
+ initPageForSEO(input) {
2302
+ this.setGeneralData(input);
2303
+ this.setTwitterData(input);
2304
+ this.setTwitterData(input);
2305
+ this.setFacebookData(input);
2306
+ if (input.keywords) {
2307
+ this.setPageKeywords(input.keywords);
2271
2308
  }
2272
2309
  }
2273
- setPageTitle(title) {
2274
- this.titleService.setTitle(title);
2275
- this.metaTagService.updateTag({ property: 'og:title', content: title });
2276
- this.metaTagService.updateTag({ name: 'twitter:title', content: title });
2310
+ /**
2311
+ * Set general metadata
2312
+ *
2313
+ * @param data
2314
+ * @private
2315
+ */
2316
+ setGeneralData(data) {
2317
+ this.titleService.setTitle(data.title);
2318
+ this.metaTagService.updateTag({ name: 'description', content: data.description });
2319
+ }
2320
+ /**
2321
+ * Set metadata for twitter
2322
+ *
2323
+ * @param data
2324
+ * @private
2325
+ */
2326
+ setTwitterData(data) {
2327
+ this.metaTagService.updateTag({ name: 'twitter:title', content: data.title });
2328
+ this.metaTagService.updateTag({ name: 'twitter:description', content: data.description });
2329
+ this.metaTagService.updateTag({
2330
+ property: 'twitter:url',
2331
+ content: data.url ? data.url : window.location.href,
2332
+ });
2333
+ if (data.image) {
2334
+ this.metaTagService.updateTag({
2335
+ property: 'twitter:image',
2336
+ content: data.image,
2337
+ });
2338
+ this.metaTagService.updateTag({
2339
+ property: 'twitter:card',
2340
+ content: 'summary_large_image',
2341
+ });
2342
+ }
2277
2343
  }
2278
- setPageDescription(description) {
2279
- this.metaTagService.updateTag({ name: 'description', content: description });
2280
- this.metaTagService.updateTag({ property: 'og:description', content: description });
2281
- this.metaTagService.updateTag({ name: 'twitter:description', content: description });
2344
+ /**
2345
+ * Set metadata for facebook
2346
+ *
2347
+ * @param data
2348
+ * @private
2349
+ */
2350
+ setFacebookData(data) {
2351
+ this.metaTagService.updateTag({ property: 'og:title', content: data.title });
2352
+ this.metaTagService.updateTag({ property: 'og:description', content: data.description });
2353
+ this.metaTagService.updateTag({
2354
+ property: 'og:type',
2355
+ content: 'website',
2356
+ });
2357
+ this.metaTagService.updateTag({
2358
+ property: 'og:url',
2359
+ content: data.url ? data.url : window.location.href,
2360
+ });
2361
+ if (data.image) {
2362
+ this.metaTagService.updateTag({
2363
+ property: 'og:image',
2364
+ content: data.image,
2365
+ });
2366
+ }
2282
2367
  }
2368
+ /**
2369
+ * Set page keywords
2370
+ *
2371
+ * @param keywords
2372
+ * @private
2373
+ */
2283
2374
  setPageKeywords(keywords) {
2284
2375
  this.metaTagService.updateTag({ name: 'keywords', content: keywords });
2285
2376
  }
@@ -2327,5 +2418,5 @@ function MustMatch(controlName, matchingControlName) {
2327
2418
  * Generated bundle index. Do not edit.
2328
2419
  */
2329
2420
 
2330
- export { AuthGuard, AuthService, BASE_MODULE_CONFIG, BaseModule, BasicUser, CanDeactivateGuard, ComparisonOperatorEnum, ContextMenuDirective, EllipsesPipe, FullscreenService, GraphQLEnum, GraphQLMeta, GraphQLMetaService, GraphQLPlusService, GraphQLRequestType, GraphQLService, GraphQLType, Helper, LazyLoadDirective, LoaderService, LogicalOperatorEnum, LogoutGuard, MatchHeightDirective, MustMatch, ResizableDirective, SEOService, SafeHtmlPipe, ScrollService, SortOrderEnum, Standard, StorageService, apolloOptionsFactory };
2421
+ export { AuthGuard, AuthService, BASE_MODULE_CONFIG, BaseModule, BasicUser, CanDeactivateGuard, ComparisonOperatorEnum, ContextMenuDirective, EllipsesPipe, FullscreenService, GraphQLEnum, GraphQLMeta, GraphQLMetaService, GraphQLPlusService, GraphQLRequestType, GraphQLService, GraphQLType, Helper, ImageService, LazyLoadDirective, LoaderService, LogicalOperatorEnum, LogoutGuard, MatchHeightDirective, MustMatch, ResizableDirective, SEOService, SafeHtmlPipe, ScrollService, SortOrderEnum, Standard, StorageService, apolloOptionsFactory };
2331
2422
  //# sourceMappingURL=lenne.tech-ng-base.js.map