@rocketshow/designer 1.65.0 → 1.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, HostListener, ViewChild, Component, Pipe, EventEmitter, Output, Input, Directive, ChangeDetectionStrategy, ViewEncapsulation, NgModule } from '@angular/core';
2
+ import { Injectable, HostListener, ViewChild, Component, Pipe, EventEmitter, Output, Input, Directive, ChangeDetectionStrategy, ViewEncapsulation, ContentChild, NgModule } from '@angular/core';
3
3
  import * as i2 from '@ngx-translate/core';
4
4
  import { TranslateModule } from '@ngx-translate/core';
5
5
  import * as i1$1 from 'ngx-bootstrap/modal';
@@ -11,7 +11,7 @@ import * as i1 from '@angular/common/http';
11
11
  import { HttpHeaders, HttpErrorResponse, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
12
12
  import * as i3$1 from 'ngx-toastr';
13
13
  import { ToastrModule } from 'ngx-toastr';
14
- import { of, forkJoin, Observable, Subject, timer } from 'rxjs';
14
+ import { Subject, of, forkJoin, Observable, timer } from 'rxjs';
15
15
  import * as i3 from '@angular/router';
16
16
  import { RouterModule } from '@angular/router';
17
17
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
@@ -34,7 +34,6 @@ import { NgxBootstrapSliderModule } from 'ngx-bootstrap-slider';
34
34
  import * as i10$1 from '@angular/common';
35
35
  import * as i6 from 'ngx-bootstrap/accordion';
36
36
  import { AccordionModule } from 'ngx-bootstrap/accordion';
37
- import { KEYS, TREE_ACTIONS, TreeModule } from '@ali-hm/angular-tree-component';
38
37
  import { BrowserModule } from '@angular/platform-browser';
39
38
  import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
40
39
 
@@ -815,6 +814,9 @@ class Project {
815
814
  this.fixtures = [];
816
815
  // the fixtures and pixel keys in order to be selectable
817
816
  this.presetFixtures = [];
817
+ // the universes (name + uuid) the project's fixtures are assigned to
818
+ // (used when the user manages universes freely, see freeUniverseEdit)
819
+ this.universes = [];
818
820
  this.scenes = [];
819
821
  this.presets = [];
820
822
  if (!data) {
@@ -856,6 +858,11 @@ class Project {
856
858
  this.presetFixtures.push(new PresetFixture(presetFixture));
857
859
  }
858
860
  }
861
+ if (data.universes) {
862
+ for (const universe of data.universes) {
863
+ this.universes.push({ uuid: universe.uuid, name: universe.name });
864
+ }
865
+ }
859
866
  if (data.scenes) {
860
867
  for (const scene of data.scenes) {
861
868
  this.scenes.push(new Scene(scene));
@@ -953,14 +960,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
953
960
  }]
954
961
  }], ctorParameters: () => [{ type: i1.HttpClient }] });
955
962
 
963
+ class ConfigService {
964
+ constructor() {
965
+ this.enableMediaLibrary = false;
966
+ this.loginAvailable = false;
967
+ this.shareAvailable = false;
968
+ this.menuHeightPx = 0;
969
+ this.languageSwitch = false;
970
+ this.livePreview = false;
971
+ this.localProfiles = false;
972
+ this.intro = false;
973
+ this.uniqueProjectNames = false;
974
+ this.dropzoneChunking = true;
975
+ // List of universes (with name and uuid) the user can choose from
976
+ // in the fixture pool. Typically supplied by the host application
977
+ // (e.g. fetched from a backend).
978
+ this.universes = [];
979
+ // When true, the user can freely add their own universes (only
980
+ // names) in the fixture pool, without depending on a list provided
981
+ // by the host application.
982
+ this.freeUniverseEdit = false;
983
+ this.menuHeightChanged = new Subject();
984
+ }
985
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
986
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ConfigService, providedIn: 'root' }); }
987
+ }
988
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ConfigService, decorators: [{
989
+ type: Injectable,
990
+ args: [{
991
+ providedIn: 'root',
992
+ }]
993
+ }], ctorParameters: () => [] });
994
+
956
995
  class ProjectService {
957
- constructor(http, userService, router, activatedRoute, translateService, toastrService) {
996
+ constructor(http, userService, router, activatedRoute, translateService, toastrService, configService) {
958
997
  this.http = http;
959
998
  this.userService = userService;
960
999
  this.router = router;
961
1000
  this.activatedRoute = activatedRoute;
962
1001
  this.translateService = translateService;
963
1002
  this.toastrService = toastrService;
1003
+ this.configService = configService;
964
1004
  // the project-file versions used in this version of the designer (older ones will be migrated, newer ones are not supported)
965
1005
  this.currentProjectVersion = 2;
966
1006
  }
@@ -986,6 +1026,10 @@ class ProjectService {
986
1026
  }
987
1027
  save(project) {
988
1028
  // tries to save the project and returns, whether it has been saved or not
1029
+ if (this.configService.freeUniverseEdit) {
1030
+ // persist the freely edited universes together with the project
1031
+ project.universes = this.configService.universes;
1032
+ }
989
1033
  this.saveApi(project).subscribe(() => {
990
1034
  const msg = 'designer.project.save-success';
991
1035
  const title = 'designer.project.save-success-title';
@@ -1022,7 +1066,7 @@ class ProjectService {
1022
1066
  return project;
1023
1067
  }));
1024
1068
  }
1025
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectService, deps: [{ token: i1.HttpClient }, { token: UserService }, { token: i3.Router }, { token: i3.ActivatedRoute }, { token: i2.TranslateService }, { token: i3$1.ToastrService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1069
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectService, deps: [{ token: i1.HttpClient }, { token: UserService }, { token: i3.Router }, { token: i3.ActivatedRoute }, { token: i2.TranslateService }, { token: i3$1.ToastrService }, { token: ConfigService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1026
1070
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectService, providedIn: 'root' }); }
1027
1071
  }
1028
1072
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectService, decorators: [{
@@ -1030,7 +1074,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1030
1074
  args: [{
1031
1075
  providedIn: 'root',
1032
1076
  }]
1033
- }], ctorParameters: () => [{ type: i1.HttpClient }, { type: UserService }, { type: i3.Router }, { type: i3.ActivatedRoute }, { type: i2.TranslateService }, { type: i3$1.ToastrService }] });
1077
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: UserService }, { type: i3.Router }, { type: i3.ActivatedRoute }, { type: i2.TranslateService }, { type: i3$1.ToastrService }, { type: ConfigService }] });
1034
1078
 
1035
1079
  class UuidService {
1036
1080
  constructor() { }
@@ -2040,38 +2084,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2040
2084
  }]
2041
2085
  }], ctorParameters: () => [] });
2042
2086
 
2043
- class ConfigService {
2044
- constructor() {
2045
- this.enableMediaLibrary = false;
2046
- this.loginAvailable = false;
2047
- this.shareAvailable = false;
2048
- this.menuHeightPx = 0;
2049
- this.languageSwitch = false;
2050
- this.livePreview = false;
2051
- this.localProfiles = false;
2052
- this.intro = false;
2053
- this.uniqueProjectNames = false;
2054
- this.dropzoneChunking = true;
2055
- // List of universes (with name and uuid) the user can choose from
2056
- // in the fixture pool. Typically supplied by the host application
2057
- // (e.g. fetched from a backend).
2058
- this.universes = [];
2059
- // When true, the user can freely add their own universes (only
2060
- // names) in the fixture pool, without depending on a list provided
2061
- // by the host application.
2062
- this.freeUniverseEdit = false;
2063
- this.menuHeightChanged = new Subject();
2064
- }
2065
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2066
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ConfigService, providedIn: 'root' }); }
2067
- }
2068
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ConfigService, decorators: [{
2069
- type: Injectable,
2070
- args: [{
2071
- providedIn: 'root',
2072
- }]
2073
- }], ctorParameters: () => [] });
2074
-
2075
2087
  class TimelineStateService {
2076
2088
  constructor() {
2077
2089
  this.playState = 'paused';
@@ -3558,12 +3570,14 @@ class PreviewService {
3558
3570
  }
3559
3571
  const fixture = this.fixtureService.getFixtureByUuid(projectFixture.fixtureUuid);
3560
3572
  const firstDmxChannelAndFixtureUuid = {
3573
+ dmxUniverseUuid: fixture.dmxUniverseUuid,
3561
3574
  firstDmxChannel: fixture.dmxFirstChannel,
3562
3575
  pixelKey: projectFixture.pixelKey,
3563
3576
  };
3564
- const exists = countedFirstDmxChannelPixelKey.some((item) => item.firstDmxChannel === firstDmxChannelAndFixtureUuid.firstDmxChannel &&
3577
+ const exists = countedFirstDmxChannelPixelKey.some((item) => item.dmxUniverseUuid === firstDmxChannelAndFixtureUuid.dmxUniverseUuid &&
3578
+ item.firstDmxChannel === firstDmxChannelAndFixtureUuid.firstDmxChannel &&
3565
3579
  ((!item.pixelKey && !firstDmxChannelAndFixtureUuid.pixelKey) || item.pixelKey === firstDmxChannelAndFixtureUuid.pixelKey));
3566
- // don't count fixtures on the same channel as already counted ones
3580
+ // don't count fixtures on the same universe and channel as already counted ones
3567
3581
  if (!exists) {
3568
3582
  index++;
3569
3583
  countedFirstDmxChannelPixelKey.push(firstDmxChannelAndFixtureUuid);
@@ -4682,7 +4696,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
4682
4696
  }], ctorParameters: () => [{ type: i1$1.BsModalService }, { type: i2.TranslateService }] });
4683
4697
 
4684
4698
  class ProjectLoadService {
4685
- constructor(projectService, previewService, fixtureService, presetService, sceneService, modalService, uuidService, timelineService, router, activatedRoute, warningDialogService, livePreviewService) {
4699
+ constructor(projectService, previewService, fixtureService, presetService, sceneService, modalService, uuidService, timelineService, router, activatedRoute, warningDialogService, livePreviewService, configService) {
4686
4700
  this.projectService = projectService;
4687
4701
  this.previewService = previewService;
4688
4702
  this.fixtureService = fixtureService;
@@ -4695,6 +4709,7 @@ class ProjectLoadService {
4695
4709
  this.activatedRoute = activatedRoute;
4696
4710
  this.warningDialogService = warningDialogService;
4697
4711
  this.livePreviewService = livePreviewService;
4712
+ this.configService = configService;
4698
4713
  }
4699
4714
  migrateToVersion2() {
4700
4715
  for (let fixture of this.projectService.project.fixtures) {
@@ -4742,8 +4757,32 @@ class ProjectLoadService {
4742
4757
  this.warningDialogService.show('designer.project.migrated').subscribe();
4743
4758
  }
4744
4759
  }
4760
+ setupUniverses() {
4761
+ if (this.configService.freeUniverseEdit) {
4762
+ // when the user manages universes freely, the project itself stores the
4763
+ // universes -> use them as the working list
4764
+ this.configService.universes = this.projectService.project.universes;
4765
+ // ensure a default universe exists
4766
+ if (this.configService.universes.length === 0) {
4767
+ this.configService.universes.push({ uuid: this.uuidService.getUuid(), name: 'Universe 1' });
4768
+ }
4769
+ }
4770
+ if (this.configService.universes.length === 0) {
4771
+ // no universe available to assign the fixtures to (e.g. the host
4772
+ // application did not provide any)
4773
+ return;
4774
+ }
4775
+ // assign all fixtures without a universe to the first (default) universe
4776
+ const defaultUniverseUuid = this.configService.universes[0].uuid;
4777
+ for (const fixture of this.projectService.project.fixtures) {
4778
+ if (!fixture.dmxUniverseUuid) {
4779
+ fixture.dmxUniverseUuid = defaultUniverseUuid;
4780
+ }
4781
+ }
4782
+ }
4745
4783
  afterLoad() {
4746
4784
  this.migrateFromOldProject();
4785
+ this.setupUniverses();
4747
4786
  this.fixtureService.updateCachedFixtures();
4748
4787
  this.presetService.removeDeletedFixtures();
4749
4788
  this.previewService.updateFixtureSetup();
@@ -4851,7 +4890,7 @@ class ProjectLoadService {
4851
4890
  this.selectScenesPresetComposition();
4852
4891
  this.afterLoad();
4853
4892
  }
4854
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectLoadService, deps: [{ token: ProjectService }, { token: PreviewService }, { token: FixtureService }, { token: PresetService }, { token: SceneService }, { token: i1$1.BsModalService }, { token: UuidService }, { token: TimelineService }, { token: i3.Router }, { token: i3.ActivatedRoute }, { token: WarningDialogService }, { token: LivePreviewService }], target: i0.ɵɵFactoryTarget.Injectable }); }
4893
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectLoadService, deps: [{ token: ProjectService }, { token: PreviewService }, { token: FixtureService }, { token: PresetService }, { token: SceneService }, { token: i1$1.BsModalService }, { token: UuidService }, { token: TimelineService }, { token: i3.Router }, { token: i3.ActivatedRoute }, { token: WarningDialogService }, { token: LivePreviewService }, { token: ConfigService }], target: i0.ɵɵFactoryTarget.Injectable }); }
4855
4894
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectLoadService, providedIn: 'root' }); }
4856
4895
  }
4857
4896
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectLoadService, decorators: [{
@@ -4859,7 +4898,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
4859
4898
  args: [{
4860
4899
  providedIn: 'root',
4861
4900
  }]
4862
- }], ctorParameters: () => [{ type: ProjectService }, { type: PreviewService }, { type: FixtureService }, { type: PresetService }, { type: SceneService }, { type: i1$1.BsModalService }, { type: UuidService }, { type: TimelineService }, { type: i3.Router }, { type: i3.ActivatedRoute }, { type: WarningDialogService }, { type: LivePreviewService }] });
4901
+ }], ctorParameters: () => [{ type: ProjectService }, { type: PreviewService }, { type: FixtureService }, { type: PresetService }, { type: SceneService }, { type: i1$1.BsModalService }, { type: UuidService }, { type: TimelineService }, { type: i3.Router }, { type: i3.ActivatedRoute }, { type: WarningDialogService }, { type: LivePreviewService }, { type: ConfigService }] });
4863
4902
 
4864
4903
  class ArraySortPipe {
4865
4904
  transform(array, field) {
@@ -5199,7 +5238,7 @@ class FixturePoolCreateFromFileComponent {
5199
5238
  // validate the file
5200
5239
  const text = event.target.result;
5201
5240
  const json = JSON.parse(text);
5202
- if (json['$schema'].match('https://raw.githubusercontent.com/OpenLightingProject/open-fixture-library/schema-(.*)/schemas/fixture.json')) {
5241
+ if (json['$schema'].match(/open-fixture-library\/(?:schema-(.*?)|master)\/schemas\/fixture\.json/)) {
5203
5242
  // create the metadata from the file. we don't have the corresponding
5204
5243
  // manufacturer-file, therefore we just assume the attributes from the
5205
5244
  // fixture-file.
@@ -7579,12 +7618,14 @@ class EffectCurveComponent {
7579
7618
  for (const presetFixture of this.presetService.selectedPreset.fixtures) {
7580
7619
  const fixture = this.fixtureService.getFixtureByUuid(presetFixture.fixtureUuid);
7581
7620
  const firstDmxChannelAndFixtureUuid = {
7621
+ dmxUniverseUuid: fixture.dmxUniverseUuid,
7582
7622
  firstDmxChannel: fixture.dmxFirstChannel,
7583
7623
  pixelKey: presetFixture.pixelKey,
7584
7624
  };
7585
- const exists = countedFirstDmxChannelPixelKey.some((item) => item.firstDmxChannel === firstDmxChannelAndFixtureUuid.firstDmxChannel &&
7625
+ const exists = countedFirstDmxChannelPixelKey.some((item) => item.dmxUniverseUuid === firstDmxChannelAndFixtureUuid.dmxUniverseUuid &&
7626
+ item.firstDmxChannel === firstDmxChannelAndFixtureUuid.firstDmxChannel &&
7586
7627
  ((!item.pixelKey && !firstDmxChannelAndFixtureUuid.pixelKey) || item.pixelKey === firstDmxChannelAndFixtureUuid.pixelKey));
7587
- // don't count it, if it has the same DMX address and pixel key (if available),
7628
+ // don't count it, if it has the same DMX universe, address and pixel key (if available),
7588
7629
  // as an already counted one
7589
7630
  if (!exists) {
7590
7631
  count++;
@@ -7951,64 +7992,6 @@ class PresetComponent {
7951
7992
  this.projectService = projectService;
7952
7993
  this.introService = introService;
7953
7994
  this.modalService = modalService;
7954
- let actionMapping = {
7955
- mouse: {
7956
- click: (tree, node, $event) => {
7957
- // block activation for folders (or any condition you want)
7958
- if (node.data.isFolder) {
7959
- // optional: make click expand/collapse instead
7960
- TREE_ACTIONS.TOGGLE_EXPANDED(tree, node, $event);
7961
- return;
7962
- }
7963
- TREE_ACTIONS.ACTIVATE(tree, node, $event);
7964
- },
7965
- },
7966
- keys: {
7967
- [KEYS.ENTER]: (tree, node, $event) => {
7968
- if (!node.data.isFolder)
7969
- TREE_ACTIONS.ACTIVATE(tree, node, $event);
7970
- },
7971
- [KEYS.SPACE]: (tree, node, $event) => {
7972
- if (!node.data.isFolder)
7973
- TREE_ACTIONS.ACTIVATE(tree, node, $event);
7974
- },
7975
- },
7976
- };
7977
- this.treeOptions = {
7978
- displayField: 'name',
7979
- isExpandedField: 'expanded',
7980
- idField: 'uuid',
7981
- hasChildrenField: 'nodes',
7982
- // actionMapping: {
7983
- // mouse: {
7984
- // dblClick: (tree, node, $event) => {
7985
- // if (node.hasChildren) TREE_ACTIONS.TOGGLE_EXPANDED(tree, node, $event);
7986
- // }
7987
- // },
7988
- // keys: {
7989
- // [KEYS.ENTER]: (tree, node, $event) => {
7990
- // node.expandAll();
7991
- // }
7992
- // }
7993
- // },
7994
- actionMapping: actionMapping,
7995
- nodeHeight: 23,
7996
- allowDrag: (node) => {
7997
- return true;
7998
- },
7999
- allowDrop: (node, target) => {
8000
- // only allow dropping into folders or to the root level
8001
- return !target.parent.data.id || target.parent.data.isFolder;
8002
- },
8003
- allowDragoverStyling: true,
8004
- levelPadding: 10,
8005
- useVirtualScroll: true,
8006
- animateExpand: true,
8007
- scrollOnActivate: true,
8008
- animateSpeed: 10,
8009
- animateAcceleration: 1.2,
8010
- scrollContainer: document.documentElement,
8011
- };
8012
7995
  this.treeNodes = [
8013
7996
  {
8014
7997
  id: 1,
@@ -8021,7 +8004,7 @@ class PresetComponent {
8021
8004
  },
8022
8005
  {
8023
8006
  id: 4,
8024
- name: 'root1',
8007
+ name: 'root2',
8025
8008
  isFolder: true,
8026
8009
  children: [
8027
8010
  { id: 5, name: 'child1', isFolder: false },
@@ -8031,8 +8014,8 @@ class PresetComponent {
8031
8014
  ];
8032
8015
  }
8033
8016
  ngOnInit() { }
8034
- onActivate(event) {
8035
- console.log(event);
8017
+ onActivate(node) {
8018
+ console.log(node);
8036
8019
  }
8037
8020
  selectPreset(index) {
8038
8021
  this.projectService.project.previewPreset = true;
@@ -8081,15 +8064,12 @@ class PresetComponent {
8081
8064
  });
8082
8065
  }
8083
8066
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PresetComponent, deps: [{ token: PresetService }, { token: SceneService }, { token: ProjectService }, { token: IntroService }, { token: i1$1.BsModalService }], target: i0.ɵɵFactoryTarget.Component }); }
8084
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: PresetComponent, isStandalone: false, selector: "lib-app-preset", viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true }], ngImport: i0, template: "<div\n class=\"card border-secondary h-100 panel card-intro-preset card-intro-active\"\n [class.card-intro-active]=\"introService.showStep('presets')\"\n>\n <div class=\"card-header d-flex\">\n <div>\n {{ 'designer.preset.title' | translate }}\n </div>\n <div class=\"ml-auto\">\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"removePreset(); (false)\"\n ><i class=\"fa fa-minus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"addPreset(); (false)\"\n ><i class=\"fa fa-plus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n </div>\n </div>\n\n <div class=\"card-body h-100 p-0\">\n <!-- List of presets -->\n <!-- <tree-root #tree [nodes]=\"treeNodes\" [options]=\"treeOptions\" (activate)=\"onActivate($event)\">\n <ng-template #treeNodeTemplate let-node let-index=\"index\">\n <span>{{ node.data.name }}</span>\n </ng-template>\n </tree-root> -->\n\n <div class=\"list-group\" [sortablejs]=\"projectService.project.presets\">\n @for (item of projectService.project.presets; track item; let i = $index) {\n <div\n class=\"list-group-item\"\n (click)=\"selectPreset(i)\"\n [class.active-light]=\"!projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.active]=\"projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.inactive-list-item]=\"!sceneService.presetIsSelected(item)\"\n >\n <div class=\"row d-flex\" style=\"cursor: pointer\">\n <div class=\"col-auto list-sort-handle my-auto\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n [disabled]=\"!enableCheckbox()\"\n class=\"form-check-input\"\n id=\"active\"\n [ngModel]=\"sceneService.presetIsSelected(item)\"\n (ngModelChange)=\"activatePreset($event, i)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n <div>\n {{ item.name }}\n </div>\n <div class=\"my-auto ml-auto mr-2 d-flex\">\n <button type=\"button\" class=\"btn btn-secondary btn-sm py-0 px-1\" (click)=\"openSettings(item); (false)\">...</button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i3$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: SortablejsDirective, selector: "[sortablejs]", inputs: ["sortablejs", "sortablejsContainer", "sortablejsOptions"], outputs: ["sortablejsInit"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] }); }
8067
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: PresetComponent, isStandalone: false, selector: "lib-app-preset", ngImport: i0, template: "<div\n class=\"card border-secondary h-100 panel card-intro-preset card-intro-active\"\n [class.card-intro-active]=\"introService.showStep('presets')\"\n>\n <div class=\"card-header d-flex\">\n <div>\n {{ 'designer.preset.title' | translate }}\n </div>\n <div class=\"ml-auto\">\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"removePreset(); (false)\"\n ><i class=\"fa fa-minus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"addPreset(); (false)\"\n ><i class=\"fa fa-plus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n </div>\n </div>\n\n <div class=\"card-body h-100 p-0\">\n <!-- List of presets -->\n <!-- <lib-tree [nodes]=\"treeNodes\" (activate)=\"onActivate($event)\">\n <ng-template #nodeTemplate let-node>\n <span>{{ node.name }}</span>\n </ng-template>\n </lib-tree> -->\n\n <div class=\"list-group\" [sortablejs]=\"projectService.project.presets\">\n @for (item of projectService.project.presets; track item; let i = $index) {\n <div\n class=\"list-group-item\"\n (click)=\"selectPreset(i)\"\n [class.active-light]=\"!projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.active]=\"projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.inactive-list-item]=\"!sceneService.presetIsSelected(item)\"\n >\n <div class=\"row d-flex\" style=\"cursor: pointer\">\n <div class=\"col-auto list-sort-handle my-auto\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n [disabled]=\"!enableCheckbox()\"\n class=\"form-check-input\"\n id=\"active\"\n [ngModel]=\"sceneService.presetIsSelected(item)\"\n (ngModelChange)=\"activatePreset($event, i)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n <div>\n {{ item.name }}\n </div>\n <div class=\"my-auto ml-auto mr-2 d-flex\">\n <button type=\"button\" class=\"btn btn-secondary btn-sm py-0 px-1\" (click)=\"openSettings(item); (false)\">...</button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i3$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: SortablejsDirective, selector: "[sortablejs]", inputs: ["sortablejs", "sortablejsContainer", "sortablejsOptions"], outputs: ["sortablejsInit"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] }); }
8085
8068
  }
8086
8069
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PresetComponent, decorators: [{
8087
8070
  type: Component,
8088
- args: [{ selector: 'lib-app-preset', standalone: false, template: "<div\n class=\"card border-secondary h-100 panel card-intro-preset card-intro-active\"\n [class.card-intro-active]=\"introService.showStep('presets')\"\n>\n <div class=\"card-header d-flex\">\n <div>\n {{ 'designer.preset.title' | translate }}\n </div>\n <div class=\"ml-auto\">\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"removePreset(); (false)\"\n ><i class=\"fa fa-minus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"addPreset(); (false)\"\n ><i class=\"fa fa-plus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n </div>\n </div>\n\n <div class=\"card-body h-100 p-0\">\n <!-- List of presets -->\n <!-- <tree-root #tree [nodes]=\"treeNodes\" [options]=\"treeOptions\" (activate)=\"onActivate($event)\">\n <ng-template #treeNodeTemplate let-node let-index=\"index\">\n <span>{{ node.data.name }}</span>\n </ng-template>\n </tree-root> -->\n\n <div class=\"list-group\" [sortablejs]=\"projectService.project.presets\">\n @for (item of projectService.project.presets; track item; let i = $index) {\n <div\n class=\"list-group-item\"\n (click)=\"selectPreset(i)\"\n [class.active-light]=\"!projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.active]=\"projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.inactive-list-item]=\"!sceneService.presetIsSelected(item)\"\n >\n <div class=\"row d-flex\" style=\"cursor: pointer\">\n <div class=\"col-auto list-sort-handle my-auto\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n [disabled]=\"!enableCheckbox()\"\n class=\"form-check-input\"\n id=\"active\"\n [ngModel]=\"sceneService.presetIsSelected(item)\"\n (ngModelChange)=\"activatePreset($event, i)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n <div>\n {{ item.name }}\n </div>\n <div class=\"my-auto ml-auto mr-2 d-flex\">\n <button type=\"button\" class=\"btn btn-secondary btn-sm py-0 px-1\" (click)=\"openSettings(item); (false)\">...</button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
8089
- }], ctorParameters: () => [{ type: PresetService }, { type: SceneService }, { type: ProjectService }, { type: IntroService }, { type: i1$1.BsModalService }], propDecorators: { tree: [{
8090
- type: ViewChild,
8091
- args: ['tree']
8092
- }] } });
8071
+ args: [{ selector: 'lib-app-preset', standalone: false, template: "<div\n class=\"card border-secondary h-100 panel card-intro-preset card-intro-active\"\n [class.card-intro-active]=\"introService.showStep('presets')\"\n>\n <div class=\"card-header d-flex\">\n <div>\n {{ 'designer.preset.title' | translate }}\n </div>\n <div class=\"ml-auto\">\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"removePreset(); (false)\"\n ><i class=\"fa fa-minus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n <a href=\"#\" class=\"btn btn-secondary m-0, p-0\" (click)=\"addPreset(); (false)\"\n ><i class=\"fa fa-plus-circle fa-fw\" aria-hidden=\"true\"></i\n ></a>\n </div>\n </div>\n\n <div class=\"card-body h-100 p-0\">\n <!-- List of presets -->\n <!-- <lib-tree [nodes]=\"treeNodes\" (activate)=\"onActivate($event)\">\n <ng-template #nodeTemplate let-node>\n <span>{{ node.name }}</span>\n </ng-template>\n </lib-tree> -->\n\n <div class=\"list-group\" [sortablejs]=\"projectService.project.presets\">\n @for (item of projectService.project.presets; track item; let i = $index) {\n <div\n class=\"list-group-item\"\n (click)=\"selectPreset(i)\"\n [class.active-light]=\"!projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.active]=\"projectService.project.previewPreset && item == presetService.selectedPreset\"\n [class.inactive-list-item]=\"!sceneService.presetIsSelected(item)\"\n >\n <div class=\"row d-flex\" style=\"cursor: pointer\">\n <div class=\"col-auto list-sort-handle my-auto\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n [disabled]=\"!enableCheckbox()\"\n class=\"form-check-input\"\n id=\"active\"\n [ngModel]=\"sceneService.presetIsSelected(item)\"\n (ngModelChange)=\"activatePreset($event, i)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n <div>\n {{ item.name }}\n </div>\n <div class=\"my-auto ml-auto mr-2 d-flex\">\n <button type=\"button\" class=\"btn btn-secondary btn-sm py-0 px-1\" (click)=\"openSettings(item); (false)\">...</button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
8072
+ }], ctorParameters: () => [{ type: PresetService }, { type: SceneService }, { type: ProjectService }, { type: IntroService }, { type: i1$1.BsModalService }] });
8093
8073
 
8094
8074
  class MasterDimmerComponent {
8095
8075
  constructor(projectService, changeDetectorRef, livePreviewService) {
@@ -8684,11 +8664,11 @@ class DesignerComponent {
8684
8664
  localStorage.setItem('language', language);
8685
8665
  }
8686
8666
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DesignerComponent, deps: [{ token: i2.TranslateService }, { token: ProjectService }, { token: ConfigService }, { token: FixturePoolService }, { token: HotkeyTargetExcludeService }, { token: i1$1.BsModalService }, { token: UserService }, { token: UserEnsureLoginService }, { token: ProjectLoadService }, { token: WarningDialogService }, { token: ErrorDialogService }, { token: FixtureService }, { token: TimelineService }, { token: IntroService }, { token: EffectService }], target: i0.ɵɵFactoryTarget.Component }); }
8687
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DesignerComponent, isStandalone: false, selector: "lib-designer", inputs: { menuHeightPx: "menuHeightPx", externalCompositionsAvailable: "externalCompositionsAvailable", restUrl: "restUrl", enableMediaLibrary: "enableMediaLibrary", loginAvailable: "loginAvailable", shareAvailable: "shareAvailable", languageSwitch: "languageSwitch", livePreview: "livePreview", localProfiles: "localProfiles", intro: "intro", uniqueProjectNames: "uniqueProjectNames", dropzoneChunking: "dropzoneChunking", universes: "universes", freeUniverseEdit: "freeUniverseEdit" }, host: { listeners: { "document:keydown": "handleKeyboardEvent($event)" } }, viewQueries: [{ propertyName: "previewComponent", first: true, predicate: PreviewComponent, descendants: true }, { propertyName: "timelineComponent", first: true, predicate: TimelineComponent, descendants: true }], ngImport: i0, template: "<!-- Wrap the whole component inside a div to encapsulate the CSS -->\n\n<!-- 100 % height minus the menu bar above and the gutter size of 13 px -->\n<!-- <div style=\"height: calc(100% - 33px)\"> -->\n<div id=\"designer\" [ngStyle]=\"{ height: 'calc(100% - ' + totalMenuHeightPx + 'px)' }\">\n <!-- Navigation -->\n <nav\n class=\"navbar navbar-expand-lg navbar-light bg-light border-secondary\"\n style=\"border-bottom: 1px solid #444; height: 20px; z-index: 9999\"\n >\n <!-- Menu buttons -->\n <ul class=\"navbar-nav mr-auto\">\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"fileDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n {{ 'designer.navigation.project' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"fileDropdown\">\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectNew(); (false)\"\n ><i class=\"fa fa-fw fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-new' | translate }}</a\n >\n <a class=\"dropdown-item\" href=\"#\" (click)=\"projectOpen(); (false)\"\n ><i class=\"fa fa-fw fa-folder-open-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-open' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSave(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSaveAs(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save-as' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectImport(); (false)\"\n ><i class=\"fa fa-fw fa-download\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-import' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectExport(); (false)\"\n ><i class=\"fa fa-fw fa-external-link\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-export' | translate }}</a\n >\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.settings' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"openFixturePool(); (false)\">{{ 'designer.navigation.fixture-pool' | translate }}</a>\n <!-- <a class=\"dropdown-item\" href=\"#\">{{ 'designer.navigation.stage' | translate }}</a> -->\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.help' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"introService.reset(); (false)\">{{ 'designer.navigation.reset-intro' | translate }}</a>\n <a class=\"dropdown-item\" href=\"https://rocketshow.net/support/\" target=\"_blank\">{{\n 'designer.navigation.support' | translate\n }}</a>\n </div>\n </li>\n </ul>\n\n <ul class=\"navbar-nav\">\n @if (configService.shareAvailable) {\n <li class=\"nav-item d-none d-md-flex\" data-toggle=\"collapse\">\n <a class=\"nav-link text-primary\" (click)=\"projectShare(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-share-alt\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-share' | translate }}</a\n >\n </li>\n } @if (configService.languageSwitch) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-globe\" aria-hidden=\"true\"></i>\n {{ 'designer.misc.language' | translate }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('en'); (false)\" target=\"_blank\">English</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('de'); (false)\" target=\"_blank\">Deutsch</a>\n </div>\n </li>\n } @if (configService.loginAvailable && !userService.isLoggedIn()) {\n <li class=\"nav-item\" data-toggle=\"collapse\">\n <a class=\"nav-link\" (click)=\"userRegister(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i> {{ 'designer.navigation.login' | translate }}</a\n >\n </li>\n } @if (configService.loginAvailable && userService.isLoggedIn()) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i>\n {{ userService.username }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"userService.logout(); (false)\" target=\"_blank\"\n ><i class=\"fa fa-sign-out\" aria-hidden=\"true\"></i> {{ 'designer.navigation.logout' | translate }}</a\n >\n </div>\n </li>\n }\n </ul>\n </nav>\n\n <!-- The main content for larger screens -->\n <div class=\"h-100 d-none d-md-block\">\n <div id=\"row1\" class=\"split split-vertical clearfix pt-3 pl-3\">\n <!-- Scenes -->\n <div id=\"scenes\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n\n <!-- Presets -->\n <div id=\"presets\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-preset></lib-app-preset>\n }\n </div>\n\n <!-- 3D preview -->\n <div id=\"preview\" class=\"split split-horizontal h-100 pr-3\">\n @if (projectService.project) {\n <div class=\"card border-secondary h-100\" [class.card-intro-active]=\"introService.showStep('preview')\">\n <div class=\"card-body p-0 h-100\">\n <lib-app-preview class=\"h-100\"></lib-app-preview>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div id=\"row2\" class=\"split split-vertical clearfix px-3 d-flex\" style=\"flex-direction: row\">\n <!-- Menu for the capabilities, channels, effects and settings -->\n @if (projectService.project) {\n <div class=\"col col-auto p-0\" style=\"margin-right: -1px\">\n <div class=\"nav flex-column nav-pills\" id=\"v-pills-tab\" role=\"tablist\" aria-orientation=\"vertical\">\n <a\n class=\"nav-link px-3 active\"\n (click)=\"openTab('capabilities')\"\n id=\"v-pills-capabilities-tab\"\n popover=\"{{ 'designer.misc.capabilities' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-capabilities\"\n role=\"tab\"\n aria-controls=\"v-pills-capabilities\"\n aria-selected=\"true\"\n >\n <i class=\"fa fa-bolt fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('channels')\"\n id=\"v-pills-channels-tab\"\n popover=\"{{ 'designer.misc.channels' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-channels\"\n role=\"tab\"\n aria-controls=\"v-pills-channels\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-sliders fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('effects')\"\n id=\"v-pills-effects-tab\"\n popover=\"{{ 'designer.misc.effects' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-effects\"\n role=\"tab\"\n aria-controls=\"v-pills-effects\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-magic fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('settings')\"\n id=\"v-pills-settings-tab\"\n popover=\"{{ 'designer.misc.settings' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-settings\"\n role=\"tab\"\n aria-controls=\"v-pills-settings\"\n aria-selected=\"false\"\n style=\"border-top: 1px solid #444\"\n >\n <i class=\"fa fa-cog fa-fw\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n }\n\n <div id=\"capabilities\" class=\"split split-horizontal h-100\" style=\"padding-left: 1rem\">\n @if (projectService.project) {\n <div class=\"row h-100\">\n <!-- Content for the capabilities, channels, effects and settings -->\n <div class=\"col pl-0 h-100\">\n <div class=\"tab-content h-100\" id=\"v-pills-tabContent\">\n <div class=\"tab-pane h-100 show active\" id=\"v-pills-capabilities\" role=\"tabpanel\">\n <lib-app-fixture-capability></lib-app-fixture-capability>\n </div>\n <div class=\"tab-pane h-100 show\" id=\"v-pills-channels\" role=\"tabpanel\">\n <lib-app-fixture-channel></lib-app-fixture-channel>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-effects\" role=\"tabpanel\" aria-labelledby=\"v-pills-profile-tab\">\n <lib-app-effect></lib-app-effect>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-settings\" role=\"tabpanel\" aria-labelledby=\"v-pills-home-tab\">\n <lib-app-fixture-settings></lib-app-fixture-settings>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n\n <!-- Fixtures -->\n <div id=\"fixtures\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-fixture></lib-app-fixture>\n }\n </div>\n\n <!-- Master dimmer -->\n <div id=\"masterDimmer\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n </div>\n\n <!-- Timeline -->\n <div id=\"row3\" class=\"split split-vertical clearfix px-3 pb-3\" style=\"padding-bottom: 20px\">\n @if (projectService.project) {\n <lib-app-timeline></lib-app-timeline>\n }\n </div>\n </div>\n\n <!-- The components for small screens -->\n <div class=\"d-md-none p-3\">\n <div>\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n\n <div class=\"mt-3\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n </div>\n</div>\n\n<lib-app-intro class=\"d-none d-md-flex\"></lib-app-intro>\n", styles: [".modal-full{max-width:unset!important;margin:1.75rem!important}.modal{z-index:10001}#designer{font-size:14px}#designer .nav-link{cursor:pointer}#designer .dropdown-menu{z-index:100000}#designer .row{flex-wrap:unset}#designer .navbar-nav{flex-direction:row!important}#designer .navbar-expand-lg{flex-flow:row nowrap!important;justify-content:flex-start!important}#designer .navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem!important;padding-left:.5rem!important}#designer .list-group-item.active{color:#fff!important;background-color:#fd7e14!important;border-color:#fd7e14!important}#designer *:focus{outline:none}#designer select{font-size:.8rem;cursor:pointer}#designer input{font-size:.7rem;background-color:transparent;color:#fff;border:1px solid}#designer input,#designer select{border-color:#dee2e6}#designer .list-group-item{padding:.3rem 1.25rem;border-left:none;border-right:none}#designer .nav-pills .nav-link.active,#designer .nav-pills .show>.nav-link{background-color:#444}#designer .nav a,#designer .panel-title .btn-link{color:#fff!important}#designer .accordion-toggle .btn,#designer .accordion-toggle button{padding:0}#designer .card-body{overflow:auto;padding:.8rem}#designer .card-header{margin-bottom:0}#designer .card-header{padding:.4rem}#designer .panel-body{background-color:#303030}#designer .list-group-item.active-light{background-color:#906542;border-color:#906542}#designer .inactive-list-item{opacity:.3}#designer span.bigcheck-target{font-family:FontAwesome}#designer input[type=checkbox].bigcheck{position:relative;left:-999em}#designer input[type=checkbox].bigcheck+span.bigcheck-target:after{content:\"\\f096\"}#designer input[type=checkbox].bigcheck:checked+span.bigcheck-target:after{content:\"\\f046\"}#designer .gutter.gutter-horizontal{cursor:ew-resize}#designer .gutter.gutter-vertical{cursor:ns-resize}#designer .split-horizontal,#designer .gutter.gutter-horizontal{float:left}#designer .split-vertical,#designer .gutter.gutter-horizontal{height:100%}#designer .split{overflow-y:hidden;overflow-x:hidden}#designer .panel{background-color:transparent}#designer .slider{cursor:pointer}#designer .slider-horizontal .slider-track{height:2px!important;margin-top:-2px!important}#designer .slider-vertical .slider-track{width:2px!important;margin-left:4px!important}#designer .slider-track,#designer .slider-track-low,#designer .slider-track-high,#designer .slider-selection{background-color:#fff!important;border-radius:0!important;background-image:none!important;box-shadow:none!important}#designer .slider-handle{width:13px!important;height:13px!important;background-color:#fff!important;background-image:none!important;border:1px solid #303030}#designer .slider-vertical .slider-handle{margin-left:4px!important}#designer .slider-horizontal .slider-handle{margin-top:2px!important}#designer .slider-input{width:35px;margin-left:8px;text-align:center}#designer .slider-input-readonly{width:35px;margin-left:8px;text-align:center;color:#a9a9a9;border-color:gray}#designer .capability-container{padding:0;height:0}#designer accordion-group>div{border:none!important}#designer .popover{background-color:#fff}#designer .popover-body{color:#000}#designer .bs-popover-right .arrow:after,#designer .bs-popover-auto[x-placement^=right] .arrow:after{border-right-color:#fff}#designer .bs-popover-left .arrow:after,#designer .bs-popover-auto[x-placement^=left] .arrow:after{border-left-color:#fff}#designer .bs-popover-bottom .arrow:after,#designer .bs-popover-auto[x-placement^=bottom] .arrow:after{border-bottom-color:#fff}#designer .bs-popover-top .arrow:after,#designer .bs-popover-auto[x-placement^=top] .arrow:after{border-top-color:#fff}#designer .iro__wheel,#designer .iro__slider{cursor:pointer}#designer .pan-tilt-grid path{fill:none;stroke:#fff;stroke-width:10px}#designer .pan-tilt-grid circle{fill:#4682b4;stroke:#fff;stroke-width:3px}#designer .capability{overflow:visible}#designer .capability-deactivated{opacity:.4}#designer .wavesurfer-region{z-index:5!important}#designer .wavesurfer-region[data-region-selected=true]{z-index:6!important}#designer .wavesurfer-region[data-region-preset=true]{height:50%!important;top:50%!important}#designer .wavesurfer-region[data-region-name]:after{content:attr(data-region-name);margin-left:20px}#designer .wavesurfer-handle{width:10px!important;max-width:10px!important;border:1px solid #ddd;background:#0000001a;box-sizing:border-box;opacity:0;cursor:unset!important}#designer .wavesurfer-handle-end{margin-left:-10px}#designer .wavesurfer-region[data-region-selectable=true] .wavesurfer-handle{opacity:1;cursor:col-resize!important}#designer .wavesurfer-handle:before,#designer .wavesurfer-handle:after{content:\"\";display:block;position:absolute;z-index:1;border-top:1px solid #fff;border-bottom:1px solid #fff;height:4px;left:5%;right:5%;top:50%;transform:translateY(-50%)}#designer .wavesurfer-handle:before{margin-top:-3px}#designer timeline{overflow:scroll!important;overflow:-moz-scrollbars-none;-ms-overflow-style:none;scrollbar-width:none;cursor:s-resize}#designer timeline::-webkit-scrollbar{height:0!important}#designer cursor{top:-20px!important}#designer wave{border-right-color:#fd7e14!important;cursor:col-resize}#designer wave canvas{opacity:.2}\n"], dependencies: [{ kind: "directive", type: i3.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: i10$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i12.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: PreviewComponent, selector: "lib-app-preview" }, { kind: "component", type: SceneComponent, selector: "lib-app-scene" }, { kind: "component", type: TimelineComponent, selector: "lib-app-timeline" }, { kind: "component", type: FixtureComponent, selector: "lib-app-fixture" }, { kind: "component", type: FixtureCapabilityComponent, selector: "lib-app-fixture-capability" }, { kind: "component", type: FixtureSettingsComponent, selector: "lib-app-fixture-settings" }, { kind: "component", type: EffectComponent, selector: "lib-app-effect" }, { kind: "component", type: PresetComponent, selector: "lib-app-preset" }, { kind: "component", type: MasterDimmerComponent, selector: "lib-app-master-dimmer" }, { kind: "component", type: FixtureChannelComponent, selector: "lib-app-fixture-channel" }, { kind: "component", type: IntroComponent, selector: "lib-app-intro" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
8667
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DesignerComponent, isStandalone: false, selector: "lib-designer", inputs: { menuHeightPx: "menuHeightPx", externalCompositionsAvailable: "externalCompositionsAvailable", restUrl: "restUrl", enableMediaLibrary: "enableMediaLibrary", loginAvailable: "loginAvailable", shareAvailable: "shareAvailable", languageSwitch: "languageSwitch", livePreview: "livePreview", localProfiles: "localProfiles", intro: "intro", uniqueProjectNames: "uniqueProjectNames", dropzoneChunking: "dropzoneChunking", universes: "universes", freeUniverseEdit: "freeUniverseEdit" }, host: { listeners: { "document:keydown": "handleKeyboardEvent($event)" } }, viewQueries: [{ propertyName: "previewComponent", first: true, predicate: PreviewComponent, descendants: true }, { propertyName: "timelineComponent", first: true, predicate: TimelineComponent, descendants: true }], ngImport: i0, template: "<!-- Wrap the whole component inside a div to encapsulate the CSS -->\n\n<!-- 100 % height minus the menu bar above and the gutter size of 13 px -->\n<!-- <div style=\"height: calc(100% - 33px)\"> -->\n<div id=\"designer\" [ngStyle]=\"{ height: 'calc(100% - ' + totalMenuHeightPx + 'px)' }\">\n <!-- Navigation -->\n <nav\n class=\"navbar navbar-expand-lg navbar-light bg-light border-secondary\"\n style=\"border-bottom: 1px solid #444; height: 20px; z-index: 9999\"\n >\n <!-- Menu buttons -->\n <ul class=\"navbar-nav mr-auto\">\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"fileDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n {{ 'designer.navigation.project' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"fileDropdown\">\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectNew(); (false)\"\n ><i class=\"fa fa-fw fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-new' | translate }}</a\n >\n <a class=\"dropdown-item\" href=\"#\" (click)=\"projectOpen(); (false)\"\n ><i class=\"fa fa-fw fa-folder-open-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-open' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSave(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSaveAs(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save-as' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectImport(); (false)\"\n ><i class=\"fa fa-fw fa-download\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-import' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectExport(); (false)\"\n ><i class=\"fa fa-fw fa-external-link\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-export' | translate }}</a\n >\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.settings' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"openFixturePool(); (false)\">{{ 'designer.navigation.fixture-pool' | translate }}</a>\n <!-- <a class=\"dropdown-item\" href=\"#\">{{ 'designer.navigation.stage' | translate }}</a> -->\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.help' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"introService.reset(); (false)\">{{ 'designer.navigation.reset-intro' | translate }}</a>\n <a class=\"dropdown-item\" href=\"https://rocketshow.net/support/\" target=\"_blank\">{{\n 'designer.navigation.support' | translate\n }}</a>\n </div>\n </li>\n </ul>\n\n <ul class=\"navbar-nav\">\n @if (configService.shareAvailable) {\n <li class=\"nav-item d-none d-md-flex\" data-toggle=\"collapse\">\n <a class=\"nav-link text-primary\" (click)=\"projectShare(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-share-alt\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-share' | translate }}</a\n >\n </li>\n } @if (configService.languageSwitch) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-globe\" aria-hidden=\"true\"></i>\n {{ 'designer.misc.language' | translate }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('de'); (false)\" target=\"_blank\">Deutsch</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('en'); (false)\" target=\"_blank\">English</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('es'); (false)\" target=\"_blank\">Espa\u00F1ol</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('fr'); (false)\" target=\"_blank\">Fran\u00E7ais</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('it'); (false)\" target=\"_blank\">Italiano</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('pt'); (false)\" target=\"_blank\">Portugu\u00EAs</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('zh'); (false)\" target=\"_blank\">\u4E2D\u6587</a>\n </div>\n </li>\n } @if (configService.loginAvailable && !userService.isLoggedIn()) {\n <li class=\"nav-item\" data-toggle=\"collapse\">\n <a class=\"nav-link\" (click)=\"userRegister(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i> {{ 'designer.navigation.login' | translate }}</a\n >\n </li>\n } @if (configService.loginAvailable && userService.isLoggedIn()) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i>\n {{ userService.username }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"userService.logout(); (false)\" target=\"_blank\"\n ><i class=\"fa fa-sign-out\" aria-hidden=\"true\"></i> {{ 'designer.navigation.logout' | translate }}</a\n >\n </div>\n </li>\n }\n </ul>\n </nav>\n\n <!-- The main content for larger screens -->\n <div class=\"h-100 d-none d-md-block\">\n <div id=\"row1\" class=\"split split-vertical clearfix pt-3 pl-3\">\n <!-- Scenes -->\n <div id=\"scenes\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n\n <!-- Presets -->\n <div id=\"presets\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-preset></lib-app-preset>\n }\n </div>\n\n <!-- 3D preview -->\n <div id=\"preview\" class=\"split split-horizontal h-100 pr-3\">\n @if (projectService.project) {\n <div class=\"card border-secondary h-100\" [class.card-intro-active]=\"introService.showStep('preview')\">\n <div class=\"card-body p-0 h-100\">\n <lib-app-preview class=\"h-100\"></lib-app-preview>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div id=\"row2\" class=\"split split-vertical clearfix px-3 d-flex\" style=\"flex-direction: row\">\n <!-- Menu for the capabilities, channels, effects and settings -->\n @if (projectService.project) {\n <div class=\"col col-auto p-0\" style=\"margin-right: -1px\">\n <div class=\"nav flex-column nav-pills\" id=\"v-pills-tab\" role=\"tablist\" aria-orientation=\"vertical\">\n <a\n class=\"nav-link px-3 active\"\n (click)=\"openTab('capabilities')\"\n id=\"v-pills-capabilities-tab\"\n popover=\"{{ 'designer.misc.capabilities' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-capabilities\"\n role=\"tab\"\n aria-controls=\"v-pills-capabilities\"\n aria-selected=\"true\"\n >\n <i class=\"fa fa-bolt fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('channels')\"\n id=\"v-pills-channels-tab\"\n popover=\"{{ 'designer.misc.channels' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-channels\"\n role=\"tab\"\n aria-controls=\"v-pills-channels\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-sliders fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('effects')\"\n id=\"v-pills-effects-tab\"\n popover=\"{{ 'designer.misc.effects' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-effects\"\n role=\"tab\"\n aria-controls=\"v-pills-effects\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-magic fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('settings')\"\n id=\"v-pills-settings-tab\"\n popover=\"{{ 'designer.misc.settings' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-settings\"\n role=\"tab\"\n aria-controls=\"v-pills-settings\"\n aria-selected=\"false\"\n style=\"border-top: 1px solid #444\"\n >\n <i class=\"fa fa-cog fa-fw\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n }\n\n <div id=\"capabilities\" class=\"split split-horizontal h-100\" style=\"padding-left: 1rem\">\n @if (projectService.project) {\n <div class=\"row h-100\">\n <!-- Content for the capabilities, channels, effects and settings -->\n <div class=\"col pl-0 h-100\">\n <div class=\"tab-content h-100\" id=\"v-pills-tabContent\">\n <div class=\"tab-pane h-100 show active\" id=\"v-pills-capabilities\" role=\"tabpanel\">\n <lib-app-fixture-capability></lib-app-fixture-capability>\n </div>\n <div class=\"tab-pane h-100 show\" id=\"v-pills-channels\" role=\"tabpanel\">\n <lib-app-fixture-channel></lib-app-fixture-channel>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-effects\" role=\"tabpanel\" aria-labelledby=\"v-pills-profile-tab\">\n <lib-app-effect></lib-app-effect>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-settings\" role=\"tabpanel\" aria-labelledby=\"v-pills-home-tab\">\n <lib-app-fixture-settings></lib-app-fixture-settings>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n\n <!-- Fixtures -->\n <div id=\"fixtures\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-fixture></lib-app-fixture>\n }\n </div>\n\n <!-- Master dimmer -->\n <div id=\"masterDimmer\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n </div>\n\n <!-- Timeline -->\n <div id=\"row3\" class=\"split split-vertical clearfix px-3 pb-3\" style=\"padding-bottom: 20px\">\n @if (projectService.project) {\n <lib-app-timeline></lib-app-timeline>\n }\n </div>\n </div>\n\n <!-- The components for small screens -->\n <div class=\"d-md-none p-3\">\n <div>\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n\n <div class=\"mt-3\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n </div>\n</div>\n\n<lib-app-intro class=\"d-none d-md-flex\"></lib-app-intro>\n", styles: [".modal-full{max-width:unset!important;margin:1.75rem!important}.modal{z-index:10001}#designer{font-size:14px}#designer .nav-link{cursor:pointer}#designer .dropdown-menu{z-index:100000}#designer .row{flex-wrap:unset}#designer .navbar-nav{flex-direction:row!important}#designer .navbar-expand-lg{flex-flow:row nowrap!important;justify-content:flex-start!important}#designer .navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem!important;padding-left:.5rem!important}#designer .list-group-item.active{color:#fff!important;background-color:#fd7e14!important;border-color:#fd7e14!important}#designer *:focus{outline:none}#designer select{font-size:.8rem;cursor:pointer}#designer input{font-size:.7rem;background-color:transparent;color:#fff;border:1px solid}#designer input,#designer select{border-color:#dee2e6}#designer .list-group-item{padding:.3rem 1.25rem;border-left:none;border-right:none}#designer .nav-pills .nav-link.active,#designer .nav-pills .show>.nav-link{background-color:#444}#designer .nav a,#designer .panel-title .btn-link{color:#fff!important}#designer .accordion-toggle .btn,#designer .accordion-toggle button{padding:0}#designer .card-body{overflow:auto;padding:.8rem}#designer .card-header{margin-bottom:0}#designer .card-header{padding:.4rem}#designer .panel-body{background-color:#303030}#designer .list-group-item.active-light{background-color:#906542;border-color:#906542}#designer .inactive-list-item{opacity:.3}#designer span.bigcheck-target{font-family:FontAwesome}#designer input[type=checkbox].bigcheck{position:relative;left:-999em}#designer input[type=checkbox].bigcheck+span.bigcheck-target:after{content:\"\\f096\"}#designer input[type=checkbox].bigcheck:checked+span.bigcheck-target:after{content:\"\\f046\"}#designer .gutter.gutter-horizontal{cursor:ew-resize}#designer .gutter.gutter-vertical{cursor:ns-resize}#designer .split-horizontal,#designer .gutter.gutter-horizontal{float:left}#designer .split-vertical,#designer .gutter.gutter-horizontal{height:100%}#designer .split{overflow-y:hidden;overflow-x:hidden}#designer .panel{background-color:transparent}#designer .slider{cursor:pointer}#designer .slider-horizontal .slider-track{height:2px!important;margin-top:-2px!important}#designer .slider-vertical .slider-track{width:2px!important;margin-left:4px!important}#designer .slider-track,#designer .slider-track-low,#designer .slider-track-high,#designer .slider-selection{background-color:#fff!important;border-radius:0!important;background-image:none!important;box-shadow:none!important}#designer .slider-handle{width:13px!important;height:13px!important;background-color:#fff!important;background-image:none!important;border:1px solid #303030}#designer .slider-vertical .slider-handle{margin-left:4px!important}#designer .slider-horizontal .slider-handle{margin-top:2px!important}#designer .slider-input{width:35px;margin-left:8px;text-align:center}#designer .slider-input-readonly{width:35px;margin-left:8px;text-align:center;color:#a9a9a9;border-color:gray}#designer .capability-container{padding:0;height:0}#designer accordion-group>div{border:none!important}#designer .popover{background-color:#fff}#designer .popover-body{color:#000}#designer .bs-popover-right .arrow:after,#designer .bs-popover-auto[x-placement^=right] .arrow:after{border-right-color:#fff}#designer .bs-popover-left .arrow:after,#designer .bs-popover-auto[x-placement^=left] .arrow:after{border-left-color:#fff}#designer .bs-popover-bottom .arrow:after,#designer .bs-popover-auto[x-placement^=bottom] .arrow:after{border-bottom-color:#fff}#designer .bs-popover-top .arrow:after,#designer .bs-popover-auto[x-placement^=top] .arrow:after{border-top-color:#fff}#designer .iro__wheel,#designer .iro__slider{cursor:pointer}#designer .pan-tilt-grid path{fill:none;stroke:#fff;stroke-width:10px}#designer .pan-tilt-grid circle{fill:#4682b4;stroke:#fff;stroke-width:3px}#designer .capability{overflow:visible}#designer .capability-deactivated{opacity:.4}#designer .wavesurfer-region{z-index:5!important}#designer .wavesurfer-region[data-region-selected=true]{z-index:6!important}#designer .wavesurfer-region[data-region-preset=true]{height:50%!important;top:50%!important}#designer .wavesurfer-region[data-region-name]:after{content:attr(data-region-name);margin-left:20px}#designer .wavesurfer-handle{width:10px!important;max-width:10px!important;border:1px solid #ddd;background:#0000001a;box-sizing:border-box;opacity:0;cursor:unset!important}#designer .wavesurfer-handle-end{margin-left:-10px}#designer .wavesurfer-region[data-region-selectable=true] .wavesurfer-handle{opacity:1;cursor:col-resize!important}#designer .wavesurfer-handle:before,#designer .wavesurfer-handle:after{content:\"\";display:block;position:absolute;z-index:1;border-top:1px solid #fff;border-bottom:1px solid #fff;height:4px;left:5%;right:5%;top:50%;transform:translateY(-50%)}#designer .wavesurfer-handle:before{margin-top:-3px}#designer timeline{overflow:scroll!important;overflow:-moz-scrollbars-none;-ms-overflow-style:none;scrollbar-width:none;cursor:s-resize}#designer timeline::-webkit-scrollbar{height:0!important}#designer cursor{top:-20px!important}#designer wave{border-right-color:#fd7e14!important;cursor:col-resize}#designer wave canvas{opacity:.2}\n"], dependencies: [{ kind: "directive", type: i3.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: i10$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i12.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: PreviewComponent, selector: "lib-app-preview" }, { kind: "component", type: SceneComponent, selector: "lib-app-scene" }, { kind: "component", type: TimelineComponent, selector: "lib-app-timeline" }, { kind: "component", type: FixtureComponent, selector: "lib-app-fixture" }, { kind: "component", type: FixtureCapabilityComponent, selector: "lib-app-fixture-capability" }, { kind: "component", type: FixtureSettingsComponent, selector: "lib-app-fixture-settings" }, { kind: "component", type: EffectComponent, selector: "lib-app-effect" }, { kind: "component", type: PresetComponent, selector: "lib-app-preset" }, { kind: "component", type: MasterDimmerComponent, selector: "lib-app-master-dimmer" }, { kind: "component", type: FixtureChannelComponent, selector: "lib-app-fixture-channel" }, { kind: "component", type: IntroComponent, selector: "lib-app-intro" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
8688
8668
  }
8689
8669
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DesignerComponent, decorators: [{
8690
8670
  type: Component,
8691
- args: [{ selector: 'lib-designer', encapsulation: ViewEncapsulation.None, standalone: false, template: "<!-- Wrap the whole component inside a div to encapsulate the CSS -->\n\n<!-- 100 % height minus the menu bar above and the gutter size of 13 px -->\n<!-- <div style=\"height: calc(100% - 33px)\"> -->\n<div id=\"designer\" [ngStyle]=\"{ height: 'calc(100% - ' + totalMenuHeightPx + 'px)' }\">\n <!-- Navigation -->\n <nav\n class=\"navbar navbar-expand-lg navbar-light bg-light border-secondary\"\n style=\"border-bottom: 1px solid #444; height: 20px; z-index: 9999\"\n >\n <!-- Menu buttons -->\n <ul class=\"navbar-nav mr-auto\">\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"fileDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n {{ 'designer.navigation.project' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"fileDropdown\">\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectNew(); (false)\"\n ><i class=\"fa fa-fw fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-new' | translate }}</a\n >\n <a class=\"dropdown-item\" href=\"#\" (click)=\"projectOpen(); (false)\"\n ><i class=\"fa fa-fw fa-folder-open-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-open' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSave(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSaveAs(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save-as' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectImport(); (false)\"\n ><i class=\"fa fa-fw fa-download\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-import' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectExport(); (false)\"\n ><i class=\"fa fa-fw fa-external-link\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-export' | translate }}</a\n >\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.settings' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"openFixturePool(); (false)\">{{ 'designer.navigation.fixture-pool' | translate }}</a>\n <!-- <a class=\"dropdown-item\" href=\"#\">{{ 'designer.navigation.stage' | translate }}</a> -->\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.help' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"introService.reset(); (false)\">{{ 'designer.navigation.reset-intro' | translate }}</a>\n <a class=\"dropdown-item\" href=\"https://rocketshow.net/support/\" target=\"_blank\">{{\n 'designer.navigation.support' | translate\n }}</a>\n </div>\n </li>\n </ul>\n\n <ul class=\"navbar-nav\">\n @if (configService.shareAvailable) {\n <li class=\"nav-item d-none d-md-flex\" data-toggle=\"collapse\">\n <a class=\"nav-link text-primary\" (click)=\"projectShare(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-share-alt\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-share' | translate }}</a\n >\n </li>\n } @if (configService.languageSwitch) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-globe\" aria-hidden=\"true\"></i>\n {{ 'designer.misc.language' | translate }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('en'); (false)\" target=\"_blank\">English</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('de'); (false)\" target=\"_blank\">Deutsch</a>\n </div>\n </li>\n } @if (configService.loginAvailable && !userService.isLoggedIn()) {\n <li class=\"nav-item\" data-toggle=\"collapse\">\n <a class=\"nav-link\" (click)=\"userRegister(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i> {{ 'designer.navigation.login' | translate }}</a\n >\n </li>\n } @if (configService.loginAvailable && userService.isLoggedIn()) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i>\n {{ userService.username }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"userService.logout(); (false)\" target=\"_blank\"\n ><i class=\"fa fa-sign-out\" aria-hidden=\"true\"></i> {{ 'designer.navigation.logout' | translate }}</a\n >\n </div>\n </li>\n }\n </ul>\n </nav>\n\n <!-- The main content for larger screens -->\n <div class=\"h-100 d-none d-md-block\">\n <div id=\"row1\" class=\"split split-vertical clearfix pt-3 pl-3\">\n <!-- Scenes -->\n <div id=\"scenes\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n\n <!-- Presets -->\n <div id=\"presets\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-preset></lib-app-preset>\n }\n </div>\n\n <!-- 3D preview -->\n <div id=\"preview\" class=\"split split-horizontal h-100 pr-3\">\n @if (projectService.project) {\n <div class=\"card border-secondary h-100\" [class.card-intro-active]=\"introService.showStep('preview')\">\n <div class=\"card-body p-0 h-100\">\n <lib-app-preview class=\"h-100\"></lib-app-preview>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div id=\"row2\" class=\"split split-vertical clearfix px-3 d-flex\" style=\"flex-direction: row\">\n <!-- Menu for the capabilities, channels, effects and settings -->\n @if (projectService.project) {\n <div class=\"col col-auto p-0\" style=\"margin-right: -1px\">\n <div class=\"nav flex-column nav-pills\" id=\"v-pills-tab\" role=\"tablist\" aria-orientation=\"vertical\">\n <a\n class=\"nav-link px-3 active\"\n (click)=\"openTab('capabilities')\"\n id=\"v-pills-capabilities-tab\"\n popover=\"{{ 'designer.misc.capabilities' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-capabilities\"\n role=\"tab\"\n aria-controls=\"v-pills-capabilities\"\n aria-selected=\"true\"\n >\n <i class=\"fa fa-bolt fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('channels')\"\n id=\"v-pills-channels-tab\"\n popover=\"{{ 'designer.misc.channels' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-channels\"\n role=\"tab\"\n aria-controls=\"v-pills-channels\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-sliders fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('effects')\"\n id=\"v-pills-effects-tab\"\n popover=\"{{ 'designer.misc.effects' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-effects\"\n role=\"tab\"\n aria-controls=\"v-pills-effects\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-magic fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('settings')\"\n id=\"v-pills-settings-tab\"\n popover=\"{{ 'designer.misc.settings' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-settings\"\n role=\"tab\"\n aria-controls=\"v-pills-settings\"\n aria-selected=\"false\"\n style=\"border-top: 1px solid #444\"\n >\n <i class=\"fa fa-cog fa-fw\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n }\n\n <div id=\"capabilities\" class=\"split split-horizontal h-100\" style=\"padding-left: 1rem\">\n @if (projectService.project) {\n <div class=\"row h-100\">\n <!-- Content for the capabilities, channels, effects and settings -->\n <div class=\"col pl-0 h-100\">\n <div class=\"tab-content h-100\" id=\"v-pills-tabContent\">\n <div class=\"tab-pane h-100 show active\" id=\"v-pills-capabilities\" role=\"tabpanel\">\n <lib-app-fixture-capability></lib-app-fixture-capability>\n </div>\n <div class=\"tab-pane h-100 show\" id=\"v-pills-channels\" role=\"tabpanel\">\n <lib-app-fixture-channel></lib-app-fixture-channel>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-effects\" role=\"tabpanel\" aria-labelledby=\"v-pills-profile-tab\">\n <lib-app-effect></lib-app-effect>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-settings\" role=\"tabpanel\" aria-labelledby=\"v-pills-home-tab\">\n <lib-app-fixture-settings></lib-app-fixture-settings>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n\n <!-- Fixtures -->\n <div id=\"fixtures\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-fixture></lib-app-fixture>\n }\n </div>\n\n <!-- Master dimmer -->\n <div id=\"masterDimmer\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n </div>\n\n <!-- Timeline -->\n <div id=\"row3\" class=\"split split-vertical clearfix px-3 pb-3\" style=\"padding-bottom: 20px\">\n @if (projectService.project) {\n <lib-app-timeline></lib-app-timeline>\n }\n </div>\n </div>\n\n <!-- The components for small screens -->\n <div class=\"d-md-none p-3\">\n <div>\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n\n <div class=\"mt-3\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n </div>\n</div>\n\n<lib-app-intro class=\"d-none d-md-flex\"></lib-app-intro>\n", styles: [".modal-full{max-width:unset!important;margin:1.75rem!important}.modal{z-index:10001}#designer{font-size:14px}#designer .nav-link{cursor:pointer}#designer .dropdown-menu{z-index:100000}#designer .row{flex-wrap:unset}#designer .navbar-nav{flex-direction:row!important}#designer .navbar-expand-lg{flex-flow:row nowrap!important;justify-content:flex-start!important}#designer .navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem!important;padding-left:.5rem!important}#designer .list-group-item.active{color:#fff!important;background-color:#fd7e14!important;border-color:#fd7e14!important}#designer *:focus{outline:none}#designer select{font-size:.8rem;cursor:pointer}#designer input{font-size:.7rem;background-color:transparent;color:#fff;border:1px solid}#designer input,#designer select{border-color:#dee2e6}#designer .list-group-item{padding:.3rem 1.25rem;border-left:none;border-right:none}#designer .nav-pills .nav-link.active,#designer .nav-pills .show>.nav-link{background-color:#444}#designer .nav a,#designer .panel-title .btn-link{color:#fff!important}#designer .accordion-toggle .btn,#designer .accordion-toggle button{padding:0}#designer .card-body{overflow:auto;padding:.8rem}#designer .card-header{margin-bottom:0}#designer .card-header{padding:.4rem}#designer .panel-body{background-color:#303030}#designer .list-group-item.active-light{background-color:#906542;border-color:#906542}#designer .inactive-list-item{opacity:.3}#designer span.bigcheck-target{font-family:FontAwesome}#designer input[type=checkbox].bigcheck{position:relative;left:-999em}#designer input[type=checkbox].bigcheck+span.bigcheck-target:after{content:\"\\f096\"}#designer input[type=checkbox].bigcheck:checked+span.bigcheck-target:after{content:\"\\f046\"}#designer .gutter.gutter-horizontal{cursor:ew-resize}#designer .gutter.gutter-vertical{cursor:ns-resize}#designer .split-horizontal,#designer .gutter.gutter-horizontal{float:left}#designer .split-vertical,#designer .gutter.gutter-horizontal{height:100%}#designer .split{overflow-y:hidden;overflow-x:hidden}#designer .panel{background-color:transparent}#designer .slider{cursor:pointer}#designer .slider-horizontal .slider-track{height:2px!important;margin-top:-2px!important}#designer .slider-vertical .slider-track{width:2px!important;margin-left:4px!important}#designer .slider-track,#designer .slider-track-low,#designer .slider-track-high,#designer .slider-selection{background-color:#fff!important;border-radius:0!important;background-image:none!important;box-shadow:none!important}#designer .slider-handle{width:13px!important;height:13px!important;background-color:#fff!important;background-image:none!important;border:1px solid #303030}#designer .slider-vertical .slider-handle{margin-left:4px!important}#designer .slider-horizontal .slider-handle{margin-top:2px!important}#designer .slider-input{width:35px;margin-left:8px;text-align:center}#designer .slider-input-readonly{width:35px;margin-left:8px;text-align:center;color:#a9a9a9;border-color:gray}#designer .capability-container{padding:0;height:0}#designer accordion-group>div{border:none!important}#designer .popover{background-color:#fff}#designer .popover-body{color:#000}#designer .bs-popover-right .arrow:after,#designer .bs-popover-auto[x-placement^=right] .arrow:after{border-right-color:#fff}#designer .bs-popover-left .arrow:after,#designer .bs-popover-auto[x-placement^=left] .arrow:after{border-left-color:#fff}#designer .bs-popover-bottom .arrow:after,#designer .bs-popover-auto[x-placement^=bottom] .arrow:after{border-bottom-color:#fff}#designer .bs-popover-top .arrow:after,#designer .bs-popover-auto[x-placement^=top] .arrow:after{border-top-color:#fff}#designer .iro__wheel,#designer .iro__slider{cursor:pointer}#designer .pan-tilt-grid path{fill:none;stroke:#fff;stroke-width:10px}#designer .pan-tilt-grid circle{fill:#4682b4;stroke:#fff;stroke-width:3px}#designer .capability{overflow:visible}#designer .capability-deactivated{opacity:.4}#designer .wavesurfer-region{z-index:5!important}#designer .wavesurfer-region[data-region-selected=true]{z-index:6!important}#designer .wavesurfer-region[data-region-preset=true]{height:50%!important;top:50%!important}#designer .wavesurfer-region[data-region-name]:after{content:attr(data-region-name);margin-left:20px}#designer .wavesurfer-handle{width:10px!important;max-width:10px!important;border:1px solid #ddd;background:#0000001a;box-sizing:border-box;opacity:0;cursor:unset!important}#designer .wavesurfer-handle-end{margin-left:-10px}#designer .wavesurfer-region[data-region-selectable=true] .wavesurfer-handle{opacity:1;cursor:col-resize!important}#designer .wavesurfer-handle:before,#designer .wavesurfer-handle:after{content:\"\";display:block;position:absolute;z-index:1;border-top:1px solid #fff;border-bottom:1px solid #fff;height:4px;left:5%;right:5%;top:50%;transform:translateY(-50%)}#designer .wavesurfer-handle:before{margin-top:-3px}#designer timeline{overflow:scroll!important;overflow:-moz-scrollbars-none;-ms-overflow-style:none;scrollbar-width:none;cursor:s-resize}#designer timeline::-webkit-scrollbar{height:0!important}#designer cursor{top:-20px!important}#designer wave{border-right-color:#fd7e14!important;cursor:col-resize}#designer wave canvas{opacity:.2}\n"] }]
8671
+ args: [{ selector: 'lib-designer', encapsulation: ViewEncapsulation.None, standalone: false, template: "<!-- Wrap the whole component inside a div to encapsulate the CSS -->\n\n<!-- 100 % height minus the menu bar above and the gutter size of 13 px -->\n<!-- <div style=\"height: calc(100% - 33px)\"> -->\n<div id=\"designer\" [ngStyle]=\"{ height: 'calc(100% - ' + totalMenuHeightPx + 'px)' }\">\n <!-- Navigation -->\n <nav\n class=\"navbar navbar-expand-lg navbar-light bg-light border-secondary\"\n style=\"border-bottom: 1px solid #444; height: 20px; z-index: 9999\"\n >\n <!-- Menu buttons -->\n <ul class=\"navbar-nav mr-auto\">\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"fileDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n {{ 'designer.navigation.project' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"fileDropdown\">\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectNew(); (false)\"\n ><i class=\"fa fa-fw fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-new' | translate }}</a\n >\n <a class=\"dropdown-item\" href=\"#\" (click)=\"projectOpen(); (false)\"\n ><i class=\"fa fa-fw fa-folder-open-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-open' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSave(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectSaveAs(); (false)\"\n ><i class=\"fa fa-fw fa-floppy-o\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-save-as' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectImport(); (false)\"\n ><i class=\"fa fa-fw fa-download\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-import' | translate }}</a\n >\n <a class=\"dropdown-item d-none d-md-block\" href=\"#\" (click)=\"projectExport(); (false)\"\n ><i class=\"fa fa-fw fa-external-link\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-export' | translate }}</a\n >\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.settings' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"openFixturePool(); (false)\">{{ 'designer.navigation.fixture-pool' | translate }}</a>\n <!-- <a class=\"dropdown-item\" href=\"#\">{{ 'designer.navigation.stage' | translate }}</a> -->\n </div>\n </li>\n <li class=\"nav-item dropdown d-none d-md-block\">\n <a\n class=\"nav-link dropdown-toggle\"\n href=\"#\"\n id=\"settingsDropdown\"\n data-toggle=\"dropdown\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n {{ 'designer.navigation.help' | translate }}\n </a>\n <div class=\"dropdown-menu\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"introService.reset(); (false)\">{{ 'designer.navigation.reset-intro' | translate }}</a>\n <a class=\"dropdown-item\" href=\"https://rocketshow.net/support/\" target=\"_blank\">{{\n 'designer.navigation.support' | translate\n }}</a>\n </div>\n </li>\n </ul>\n\n <ul class=\"navbar-nav\">\n @if (configService.shareAvailable) {\n <li class=\"nav-item d-none d-md-flex\" data-toggle=\"collapse\">\n <a class=\"nav-link text-primary\" (click)=\"projectShare(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-share-alt\" aria-hidden=\"true\"></i> {{ 'designer.navigation.project-share' | translate }}</a\n >\n </li>\n } @if (configService.languageSwitch) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-globe\" aria-hidden=\"true\"></i>\n {{ 'designer.misc.language' | translate }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('de'); (false)\" target=\"_blank\">Deutsch</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('en'); (false)\" target=\"_blank\">English</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('es'); (false)\" target=\"_blank\">Espa\u00F1ol</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('fr'); (false)\" target=\"_blank\">Fran\u00E7ais</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('it'); (false)\" target=\"_blank\">Italiano</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('pt'); (false)\" target=\"_blank\">Portugu\u00EAs</a>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"switchLanguage('zh'); (false)\" target=\"_blank\">\u4E2D\u6587</a>\n </div>\n </li>\n } @if (configService.loginAvailable && !userService.isLoggedIn()) {\n <li class=\"nav-item\" data-toggle=\"collapse\">\n <a class=\"nav-link\" (click)=\"userRegister(); (false)\" routerLinkActive=\"active\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i> {{ 'designer.navigation.login' | translate }}</a\n >\n </li>\n } @if (configService.loginAvailable && userService.isLoggedIn()) {\n <li class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" href=\"#\" id=\"settingsDropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"\n ><i class=\"fa fa-user\" aria-hidden=\"true\"></i>\n {{ userService.username }}\n </a>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"settingsDropdown\">\n <a class=\"dropdown-item\" href=\"#\" (click)=\"userService.logout(); (false)\" target=\"_blank\"\n ><i class=\"fa fa-sign-out\" aria-hidden=\"true\"></i> {{ 'designer.navigation.logout' | translate }}</a\n >\n </div>\n </li>\n }\n </ul>\n </nav>\n\n <!-- The main content for larger screens -->\n <div class=\"h-100 d-none d-md-block\">\n <div id=\"row1\" class=\"split split-vertical clearfix pt-3 pl-3\">\n <!-- Scenes -->\n <div id=\"scenes\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n\n <!-- Presets -->\n <div id=\"presets\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-preset></lib-app-preset>\n }\n </div>\n\n <!-- 3D preview -->\n <div id=\"preview\" class=\"split split-horizontal h-100 pr-3\">\n @if (projectService.project) {\n <div class=\"card border-secondary h-100\" [class.card-intro-active]=\"introService.showStep('preview')\">\n <div class=\"card-body p-0 h-100\">\n <lib-app-preview class=\"h-100\"></lib-app-preview>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div id=\"row2\" class=\"split split-vertical clearfix px-3 d-flex\" style=\"flex-direction: row\">\n <!-- Menu for the capabilities, channels, effects and settings -->\n @if (projectService.project) {\n <div class=\"col col-auto p-0\" style=\"margin-right: -1px\">\n <div class=\"nav flex-column nav-pills\" id=\"v-pills-tab\" role=\"tablist\" aria-orientation=\"vertical\">\n <a\n class=\"nav-link px-3 active\"\n (click)=\"openTab('capabilities')\"\n id=\"v-pills-capabilities-tab\"\n popover=\"{{ 'designer.misc.capabilities' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-capabilities\"\n role=\"tab\"\n aria-controls=\"v-pills-capabilities\"\n aria-selected=\"true\"\n >\n <i class=\"fa fa-bolt fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('channels')\"\n id=\"v-pills-channels-tab\"\n popover=\"{{ 'designer.misc.channels' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-channels\"\n role=\"tab\"\n aria-controls=\"v-pills-channels\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-sliders fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('effects')\"\n id=\"v-pills-effects-tab\"\n popover=\"{{ 'designer.misc.effects' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-effects\"\n role=\"tab\"\n aria-controls=\"v-pills-effects\"\n aria-selected=\"false\"\n >\n <i class=\"fa fa-magic fa-fw\" aria-hidden=\"true\"></i>\n </a>\n <a\n class=\"nav-link px-3\"\n (click)=\"openTab('settings')\"\n id=\"v-pills-settings-tab\"\n popover=\"{{ 'designer.misc.settings' | translate }}\"\n container=\"body\"\n placement=\"right\"\n triggers=\"mouseenter:mouseleave\"\n data-toggle=\"pill\"\n href=\"#v-pills-settings\"\n role=\"tab\"\n aria-controls=\"v-pills-settings\"\n aria-selected=\"false\"\n style=\"border-top: 1px solid #444\"\n >\n <i class=\"fa fa-cog fa-fw\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n }\n\n <div id=\"capabilities\" class=\"split split-horizontal h-100\" style=\"padding-left: 1rem\">\n @if (projectService.project) {\n <div class=\"row h-100\">\n <!-- Content for the capabilities, channels, effects and settings -->\n <div class=\"col pl-0 h-100\">\n <div class=\"tab-content h-100\" id=\"v-pills-tabContent\">\n <div class=\"tab-pane h-100 show active\" id=\"v-pills-capabilities\" role=\"tabpanel\">\n <lib-app-fixture-capability></lib-app-fixture-capability>\n </div>\n <div class=\"tab-pane h-100 show\" id=\"v-pills-channels\" role=\"tabpanel\">\n <lib-app-fixture-channel></lib-app-fixture-channel>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-effects\" role=\"tabpanel\" aria-labelledby=\"v-pills-profile-tab\">\n <lib-app-effect></lib-app-effect>\n </div>\n <div class=\"tab-pane h-100\" id=\"v-pills-settings\" role=\"tabpanel\" aria-labelledby=\"v-pills-home-tab\">\n <lib-app-fixture-settings></lib-app-fixture-settings>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n\n <!-- Fixtures -->\n <div id=\"fixtures\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-fixture></lib-app-fixture>\n }\n </div>\n\n <!-- Master dimmer -->\n <div id=\"masterDimmer\" class=\"split split-horizontal h-100\">\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n </div>\n\n <!-- Timeline -->\n <div id=\"row3\" class=\"split split-vertical clearfix px-3 pb-3\" style=\"padding-bottom: 20px\">\n @if (projectService.project) {\n <lib-app-timeline></lib-app-timeline>\n }\n </div>\n </div>\n\n <!-- The components for small screens -->\n <div class=\"d-md-none p-3\">\n <div>\n @if (projectService.project) {\n <lib-app-master-dimmer></lib-app-master-dimmer>\n }\n </div>\n\n <div class=\"mt-3\">\n @if (projectService.project) {\n <lib-app-scene></lib-app-scene>\n }\n </div>\n </div>\n</div>\n\n<lib-app-intro class=\"d-none d-md-flex\"></lib-app-intro>\n", styles: [".modal-full{max-width:unset!important;margin:1.75rem!important}.modal{z-index:10001}#designer{font-size:14px}#designer .nav-link{cursor:pointer}#designer .dropdown-menu{z-index:100000}#designer .row{flex-wrap:unset}#designer .navbar-nav{flex-direction:row!important}#designer .navbar-expand-lg{flex-flow:row nowrap!important;justify-content:flex-start!important}#designer .navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem!important;padding-left:.5rem!important}#designer .list-group-item.active{color:#fff!important;background-color:#fd7e14!important;border-color:#fd7e14!important}#designer *:focus{outline:none}#designer select{font-size:.8rem;cursor:pointer}#designer input{font-size:.7rem;background-color:transparent;color:#fff;border:1px solid}#designer input,#designer select{border-color:#dee2e6}#designer .list-group-item{padding:.3rem 1.25rem;border-left:none;border-right:none}#designer .nav-pills .nav-link.active,#designer .nav-pills .show>.nav-link{background-color:#444}#designer .nav a,#designer .panel-title .btn-link{color:#fff!important}#designer .accordion-toggle .btn,#designer .accordion-toggle button{padding:0}#designer .card-body{overflow:auto;padding:.8rem}#designer .card-header{margin-bottom:0}#designer .card-header{padding:.4rem}#designer .panel-body{background-color:#303030}#designer .list-group-item.active-light{background-color:#906542;border-color:#906542}#designer .inactive-list-item{opacity:.3}#designer span.bigcheck-target{font-family:FontAwesome}#designer input[type=checkbox].bigcheck{position:relative;left:-999em}#designer input[type=checkbox].bigcheck+span.bigcheck-target:after{content:\"\\f096\"}#designer input[type=checkbox].bigcheck:checked+span.bigcheck-target:after{content:\"\\f046\"}#designer .gutter.gutter-horizontal{cursor:ew-resize}#designer .gutter.gutter-vertical{cursor:ns-resize}#designer .split-horizontal,#designer .gutter.gutter-horizontal{float:left}#designer .split-vertical,#designer .gutter.gutter-horizontal{height:100%}#designer .split{overflow-y:hidden;overflow-x:hidden}#designer .panel{background-color:transparent}#designer .slider{cursor:pointer}#designer .slider-horizontal .slider-track{height:2px!important;margin-top:-2px!important}#designer .slider-vertical .slider-track{width:2px!important;margin-left:4px!important}#designer .slider-track,#designer .slider-track-low,#designer .slider-track-high,#designer .slider-selection{background-color:#fff!important;border-radius:0!important;background-image:none!important;box-shadow:none!important}#designer .slider-handle{width:13px!important;height:13px!important;background-color:#fff!important;background-image:none!important;border:1px solid #303030}#designer .slider-vertical .slider-handle{margin-left:4px!important}#designer .slider-horizontal .slider-handle{margin-top:2px!important}#designer .slider-input{width:35px;margin-left:8px;text-align:center}#designer .slider-input-readonly{width:35px;margin-left:8px;text-align:center;color:#a9a9a9;border-color:gray}#designer .capability-container{padding:0;height:0}#designer accordion-group>div{border:none!important}#designer .popover{background-color:#fff}#designer .popover-body{color:#000}#designer .bs-popover-right .arrow:after,#designer .bs-popover-auto[x-placement^=right] .arrow:after{border-right-color:#fff}#designer .bs-popover-left .arrow:after,#designer .bs-popover-auto[x-placement^=left] .arrow:after{border-left-color:#fff}#designer .bs-popover-bottom .arrow:after,#designer .bs-popover-auto[x-placement^=bottom] .arrow:after{border-bottom-color:#fff}#designer .bs-popover-top .arrow:after,#designer .bs-popover-auto[x-placement^=top] .arrow:after{border-top-color:#fff}#designer .iro__wheel,#designer .iro__slider{cursor:pointer}#designer .pan-tilt-grid path{fill:none;stroke:#fff;stroke-width:10px}#designer .pan-tilt-grid circle{fill:#4682b4;stroke:#fff;stroke-width:3px}#designer .capability{overflow:visible}#designer .capability-deactivated{opacity:.4}#designer .wavesurfer-region{z-index:5!important}#designer .wavesurfer-region[data-region-selected=true]{z-index:6!important}#designer .wavesurfer-region[data-region-preset=true]{height:50%!important;top:50%!important}#designer .wavesurfer-region[data-region-name]:after{content:attr(data-region-name);margin-left:20px}#designer .wavesurfer-handle{width:10px!important;max-width:10px!important;border:1px solid #ddd;background:#0000001a;box-sizing:border-box;opacity:0;cursor:unset!important}#designer .wavesurfer-handle-end{margin-left:-10px}#designer .wavesurfer-region[data-region-selectable=true] .wavesurfer-handle{opacity:1;cursor:col-resize!important}#designer .wavesurfer-handle:before,#designer .wavesurfer-handle:after{content:\"\";display:block;position:absolute;z-index:1;border-top:1px solid #fff;border-bottom:1px solid #fff;height:4px;left:5%;right:5%;top:50%;transform:translateY(-50%)}#designer .wavesurfer-handle:before{margin-top:-3px}#designer timeline{overflow:scroll!important;overflow:-moz-scrollbars-none;-ms-overflow-style:none;scrollbar-width:none;cursor:s-resize}#designer timeline::-webkit-scrollbar{height:0!important}#designer cursor{top:-20px!important}#designer wave{border-right-color:#fd7e14!important;cursor:col-resize}#designer wave canvas{opacity:.2}\n"] }]
8692
8672
  }], ctorParameters: () => [{ type: i2.TranslateService }, { type: ProjectService }, { type: ConfigService }, { type: FixturePoolService }, { type: HotkeyTargetExcludeService }, { type: i1$1.BsModalService }, { type: UserService }, { type: UserEnsureLoginService }, { type: ProjectLoadService }, { type: WarningDialogService }, { type: ErrorDialogService }, { type: FixtureService }, { type: TimelineService }, { type: IntroService }, { type: EffectService }], propDecorators: { menuHeightPx: [{
8693
8673
  type: Input
8694
8674
  }], externalCompositionsAvailable: [{
@@ -8728,6 +8708,344 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
8728
8708
  args: ['document:keydown', ['$event']]
8729
8709
  }] } });
8730
8710
 
8711
+ /**
8712
+ * A lightweight, dependency-free tree:
8713
+ * - collapsible folders (closed/open folder icon)
8714
+ * - multi selection (ctrl/cmd to toggle, shift to select a range)
8715
+ * - native HTML5 drag & drop, dropping before/after a node or into a folder,
8716
+ * moving the whole current selection at once
8717
+ */
8718
+ class TreeComponent {
8719
+ constructor() {
8720
+ // the tree data (mutated in place when nodes are dragged around)
8721
+ this.nodes = [];
8722
+ // currently selected nodes (two-way bindable)
8723
+ this.selectedNodes = [];
8724
+ // decide whether a node may be dragged
8725
+ this.allowDrag = () => true;
8726
+ // decide whether the dragged nodes may be dropped onto a target/zone
8727
+ this.allowDrop = () => true;
8728
+ this.selectedNodesChange = new EventEmitter();
8729
+ // a (non-folder) leaf was activated by a plain click
8730
+ this.activate = new EventEmitter();
8731
+ // the tree structure changed because of a drag & drop move
8732
+ this.nodesChange = new EventEmitter();
8733
+ // the visible (expanded) nodes, flattened for rendering
8734
+ this.flatNodes = [];
8735
+ // currently dragged nodes and the live drop indicator
8736
+ this.draggingNodes = [];
8737
+ this.drop = null;
8738
+ // hovering the trailing strip drops at the very end of the root level
8739
+ this.dropTail = false;
8740
+ this.selection = new Set();
8741
+ this.lastClickedIndex = null;
8742
+ this.flatByNode = new Map();
8743
+ }
8744
+ // indentation of one tree level, in px (must match the template/CSS)
8745
+ static { this.INDENT_STEP = 15; }
8746
+ static { this.INDENT_BASE = 4; }
8747
+ indentPx(level) {
8748
+ return this.indent(level) + 'px';
8749
+ }
8750
+ indent(level) {
8751
+ return level * TreeComponent.INDENT_STEP + TreeComponent.INDENT_BASE;
8752
+ }
8753
+ ngOnChanges(changes) {
8754
+ if (changes['selectedNodes']) {
8755
+ this.selection = new Set(this.selectedNodes ?? []);
8756
+ }
8757
+ this.rebuild();
8758
+ }
8759
+ isSelected(node) {
8760
+ return this.selection.has(node);
8761
+ }
8762
+ isDragging(node) {
8763
+ return this.draggingNodes.includes(node);
8764
+ }
8765
+ toggleExpand(node) {
8766
+ node.expanded = !node.expanded;
8767
+ this.rebuild();
8768
+ }
8769
+ // ---- selection / clicking ----------------------------------------------
8770
+ onRowClick(flat, event) {
8771
+ const node = flat.node;
8772
+ const index = this.flatNodes.indexOf(flat);
8773
+ if (event.shiftKey && this.lastClickedIndex !== null) {
8774
+ this.selectRange(this.lastClickedIndex, index, event.ctrlKey || event.metaKey);
8775
+ }
8776
+ else if (event.ctrlKey || event.metaKey) {
8777
+ if (this.selection.has(node)) {
8778
+ this.selection.delete(node);
8779
+ }
8780
+ else {
8781
+ this.selection.add(node);
8782
+ }
8783
+ this.lastClickedIndex = index;
8784
+ }
8785
+ else {
8786
+ // plain click: folders toggle, leaves activate; either way select just this row
8787
+ if (node.isFolder) {
8788
+ this.toggleExpand(node);
8789
+ }
8790
+ this.selection.clear();
8791
+ this.selection.add(node);
8792
+ this.lastClickedIndex = index;
8793
+ if (!node.isFolder) {
8794
+ this.activate.emit(node);
8795
+ }
8796
+ }
8797
+ this.selectedNodesChange.emit([...this.selection]);
8798
+ }
8799
+ selectRange(from, to, additive) {
8800
+ if (!additive) {
8801
+ this.selection.clear();
8802
+ }
8803
+ const [lo, hi] = from <= to ? [from, to] : [to, from];
8804
+ for (let i = lo; i <= hi && i < this.flatNodes.length; i++) {
8805
+ this.selection.add(this.flatNodes[i].node);
8806
+ }
8807
+ }
8808
+ // ---- drag & drop --------------------------------------------------------
8809
+ onDragStart(flat, event) {
8810
+ const node = flat.node;
8811
+ if (!this.allowDrag(node)) {
8812
+ event.preventDefault();
8813
+ return;
8814
+ }
8815
+ // dragging an unselected node makes it the sole selection
8816
+ if (!this.selection.has(node)) {
8817
+ this.selection.clear();
8818
+ this.selection.add(node);
8819
+ this.lastClickedIndex = this.flatNodes.indexOf(flat);
8820
+ this.selectedNodesChange.emit([...this.selection]);
8821
+ }
8822
+ // drag the whole selection, but never a node together with its own ancestor
8823
+ this.draggingNodes = this.removeNested([...this.selection]);
8824
+ if (event.dataTransfer) {
8825
+ event.dataTransfer.effectAllowed = 'move';
8826
+ // required for Firefox to start a native drag
8827
+ event.dataTransfer.setData('text/plain', node.name ?? '');
8828
+ }
8829
+ }
8830
+ onDragOver(flat, event) {
8831
+ if (!this.draggingNodes.length) {
8832
+ return;
8833
+ }
8834
+ const resolved = this.resolveDrop(flat, event.currentTarget, event.clientX, event.clientY);
8835
+ if (!this.canDrop(resolved.ref, resolved.zone)) {
8836
+ this.clearIndicator();
8837
+ return;
8838
+ }
8839
+ // preventDefault marks this as a valid drop location
8840
+ event.preventDefault();
8841
+ if (event.dataTransfer) {
8842
+ event.dataTransfer.dropEffect = 'move';
8843
+ }
8844
+ this.drop = resolved;
8845
+ this.dropTail = false;
8846
+ }
8847
+ onDrop(flat, event) {
8848
+ event.preventDefault();
8849
+ if (this.drop) {
8850
+ this.moveNodes(this.draggingNodes, this.drop.ref, this.drop.zone);
8851
+ this.nodesChange.emit(this.nodes);
8852
+ }
8853
+ this.endDrag();
8854
+ }
8855
+ // The strip below the last row: drop after the last folder, at the root level.
8856
+ // (The last folder has no following row to act as a "before" drop target.)
8857
+ onTailDragOver(event) {
8858
+ if (!this.draggingNodes.length) {
8859
+ return;
8860
+ }
8861
+ event.preventDefault();
8862
+ if (event.dataTransfer) {
8863
+ event.dataTransfer.dropEffect = 'move';
8864
+ }
8865
+ this.drop = null;
8866
+ this.dropTail = true;
8867
+ }
8868
+ onTailDrop(event) {
8869
+ event.preventDefault();
8870
+ if (this.draggingNodes.length) {
8871
+ this.detachAll(this.draggingNodes);
8872
+ this.nodes.push(...this.draggingNodes);
8873
+ this.nodesChange.emit(this.nodes);
8874
+ }
8875
+ this.endDrag();
8876
+ }
8877
+ onDragEnd() {
8878
+ this.endDrag();
8879
+ }
8880
+ endDrag() {
8881
+ this.draggingNodes = [];
8882
+ this.clearIndicator();
8883
+ this.rebuild();
8884
+ }
8885
+ clearIndicator() {
8886
+ this.drop = null;
8887
+ this.dropTail = false;
8888
+ }
8889
+ // Resolve the hovered row + cursor position into a drop target.
8890
+ // - top of a row -> before it
8891
+ // - middle of a folder -> inside it
8892
+ // - bottom of a row -> after it (see afterRow for the inside/outside choice)
8893
+ // An expanded, non-empty folder has no "after" band on its header: dropping
8894
+ // after such a folder happens at the bottom of its last child instead, so the
8895
+ // indicator lands at the folder's visual end rather than under its header.
8896
+ resolveDrop(flat, row, clientX, clientY) {
8897
+ const node = flat.node;
8898
+ const rect = row.getBoundingClientRect();
8899
+ const offset = clientY - rect.top;
8900
+ if (node.isFolder) {
8901
+ if (offset < rect.height * 0.3) {
8902
+ return { row: node, zone: 'before', ref: node, indent: this.indent(flat.level) };
8903
+ }
8904
+ const expandedWithChildren = !!node.expanded && !!node.children?.length;
8905
+ if (expandedWithChildren || offset <= rect.height * 0.7) {
8906
+ return { row: node, zone: 'inside', ref: node, indent: this.indent(flat.level) };
8907
+ }
8908
+ return this.afterRow(flat, clientX, rect.left);
8909
+ }
8910
+ if (offset < rect.height / 2) {
8911
+ return { row: node, zone: 'before', ref: node, indent: this.indent(flat.level) };
8912
+ }
8913
+ return this.afterRow(flat, clientX, rect.left);
8914
+ }
8915
+ // "after" the hovered row. When the row is the last child of its subtree, the
8916
+ // horizontal cursor position chooses the level: keep the node inside next to
8917
+ // the row, or outdent to drop after one of its ancestor folders. The indicator
8918
+ // stays at this row's bottom edge but is indented to the chosen level.
8919
+ afterRow(flat, clientX, rowLeft) {
8920
+ const next = this.flatNodes[this.flatNodes.indexOf(flat) + 1];
8921
+ // not the last node of a subtree -> unambiguous "after this row"
8922
+ if (next && next.level >= flat.level) {
8923
+ return { row: flat.node, zone: 'after', ref: flat.node, indent: this.indent(flat.level) };
8924
+ }
8925
+ // last child: pick a drop level between the next visible row's level (how far
8926
+ // we may outdent) and this row's own level, from the cursor's x position
8927
+ const minLevel = next ? next.level : 0;
8928
+ const level = Math.max(minLevel, Math.min(flat.level, Math.round((clientX - rowLeft - TreeComponent.INDENT_BASE) / TreeComponent.INDENT_STEP)));
8929
+ // walk up to the ancestor sitting at the chosen level
8930
+ let ancestor = flat;
8931
+ while (ancestor && ancestor.level > level) {
8932
+ ancestor = ancestor.parent ? this.flatByNode.get(ancestor.parent) : undefined;
8933
+ }
8934
+ return { row: flat.node, zone: 'after', ref: ancestor?.node ?? flat.node, indent: this.indent(level) };
8935
+ }
8936
+ canDrop(ref, zone) {
8937
+ if (this.draggingNodes.includes(ref)) {
8938
+ return false;
8939
+ }
8940
+ // cannot drop a node into its own subtree
8941
+ if (this.draggingNodes.some((dragged) => this.contains(dragged, ref))) {
8942
+ return false;
8943
+ }
8944
+ if (zone === 'inside' && !ref.isFolder) {
8945
+ return false;
8946
+ }
8947
+ return this.allowDrop(this.draggingNodes, ref, zone);
8948
+ }
8949
+ moveNodes(dragged, ref, zone) {
8950
+ this.detachAll(dragged);
8951
+ if (zone === 'inside') {
8952
+ ref.children = ref.children ?? [];
8953
+ ref.children.push(...dragged);
8954
+ ref.isFolder = true;
8955
+ ref.expanded = true;
8956
+ return;
8957
+ }
8958
+ const location = this.locate(ref);
8959
+ if (!location) {
8960
+ this.nodes.push(...dragged);
8961
+ return;
8962
+ }
8963
+ const insertAt = location.index + (zone === 'after' ? 1 : 0);
8964
+ location.list.splice(insertAt, 0, ...dragged);
8965
+ }
8966
+ // detach every dragged node from its current parent (re-locate each time, so
8967
+ // indices stay correct while the arrays are being spliced)
8968
+ detachAll(dragged) {
8969
+ for (const node of dragged) {
8970
+ const location = this.locate(node);
8971
+ if (location) {
8972
+ location.list.splice(location.index, 1);
8973
+ }
8974
+ }
8975
+ }
8976
+ // ---- tree helpers -------------------------------------------------------
8977
+ rebuild() {
8978
+ this.flatNodes = [];
8979
+ this.flatByNode.clear();
8980
+ this.flatten(this.nodes ?? [], 0, null);
8981
+ }
8982
+ flatten(list, level, parent) {
8983
+ for (const node of list) {
8984
+ const flat = { node, level, parent };
8985
+ this.flatNodes.push(flat);
8986
+ this.flatByNode.set(node, flat);
8987
+ if (node.isFolder && node.expanded && node.children?.length) {
8988
+ this.flatten(node.children, level + 1, node);
8989
+ }
8990
+ }
8991
+ }
8992
+ // drop nodes that are already contained in another node of the set
8993
+ removeNested(nodes) {
8994
+ return nodes.filter((node) => !nodes.some((other) => other !== node && this.contains(other, node)));
8995
+ }
8996
+ // is `node` somewhere inside `ancestor`'s subtree?
8997
+ contains(ancestor, node) {
8998
+ if (!ancestor.children) {
8999
+ return false;
9000
+ }
9001
+ for (const child of ancestor.children) {
9002
+ if (child === node || this.contains(child, node)) {
9003
+ return true;
9004
+ }
9005
+ }
9006
+ return false;
9007
+ }
9008
+ // find the array and index that currently hold `target`
9009
+ locate(target, list = this.nodes) {
9010
+ for (let i = 0; i < list.length; i++) {
9011
+ if (list[i] === target) {
9012
+ return { list, index: i };
9013
+ }
9014
+ const children = list[i].children;
9015
+ if (children?.length) {
9016
+ const found = this.locate(target, children);
9017
+ if (found) {
9018
+ return found;
9019
+ }
9020
+ }
9021
+ }
9022
+ return null;
9023
+ }
9024
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9025
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: TreeComponent, isStandalone: false, selector: "lib-tree", inputs: { nodes: "nodes", selectedNodes: "selectedNodes", allowDrag: "allowDrag", allowDrop: "allowDrop" }, outputs: { selectedNodesChange: "selectedNodesChange", activate: "activate", nodesChange: "nodesChange" }, queries: [{ propertyName: "nodeTemplate", first: true, predicate: ["nodeTemplate"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"tree\" (dragend)=\"onDragEnd()\">\n @for (flat of flatNodes; track flat.node) {\n <div\n class=\"tree-node\"\n [class.selected]=\"isSelected(flat.node)\"\n [class.dragging]=\"isDragging(flat.node)\"\n [class.drop-before]=\"drop?.row === flat.node && drop?.zone === 'before'\"\n [class.drop-after]=\"drop?.row === flat.node && drop?.zone === 'after'\"\n [class.drop-inside]=\"drop?.row === flat.node && drop?.zone === 'inside'\"\n [style.--tree-indent]=\"indentPx(flat.level)\"\n [style.--tree-drop-indent]=\"drop?.row === flat.node ? drop.indent + 'px' : null\"\n [attr.draggable]=\"allowDrag(flat.node)\"\n (click)=\"onRowClick(flat, $event)\"\n (dragstart)=\"onDragStart(flat, $event)\"\n (dragover)=\"onDragOver(flat, $event)\"\n (drop)=\"onDrop(flat, $event)\"\n >\n <i\n class=\"tree-icon fa\"\n [class.fa-folder-open-o]=\"flat.node.isFolder && flat.node.expanded\"\n [class.fa-folder-o]=\"flat.node.isFolder && !flat.node.expanded\"\n [class.fa-file-o]=\"!flat.node.isFolder\"\n aria-hidden=\"true\"\n ></i>\n <span class=\"tree-label\">\n <ng-container *ngTemplateOutlet=\"nodeTemplate || defaultTemplate; context: { $implicit: flat.node, node: flat.node }\"></ng-container>\n </span>\n </div>\n }\n\n <div class=\"tree-tail\" [class.drop-tail]=\"dropTail\" (dragover)=\"onTailDragOver($event)\" (drop)=\"onTailDrop($event)\"></div>\n</div>\n\n<ng-template #defaultTemplate let-node>{{ node.name }}</ng-template>\n", styles: [":host{display:block}.tree{display:flex;flex-direction:column;padding:5px;-webkit-user-select:none;user-select:none}.tree-node{position:relative;flex:0 0 auto;display:flex;align-items:center;height:23px;line-height:23px;padding-left:var(--tree-indent, 4px);padding-right:4px;cursor:pointer;white-space:nowrap}.tree-node:hover{background-color:#ffffff0f}.tree-node.selected{background-color:#906543}.tree-node.dragging{opacity:.4}.tree-node.drop-before,.tree-node.drop-after,.tree-node.drop-inside{z-index:1}.tree-icon{flex:0 0 auto;width:16px;margin-right:5px;text-align:center}.tree-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.tree-node.drop-before:before,.tree-node.drop-after:after{content:\"\";position:absolute;left:var(--tree-drop-indent, var(--tree-indent, 4px));right:4px;height:2px;background-color:#d39661;pointer-events:none}.tree-node.drop-before:before{top:-1px}.tree-node.drop-after:after{bottom:-1px}.tree-node.drop-inside{outline:2px solid #d39661;outline-offset:-2px}.tree-tail{flex:1 1 auto;min-height:16px}.tree-tail.drop-tail:before{content:\"\";display:block;height:2px;margin:0 4px;background-color:#d39661}\n"], dependencies: [{ kind: "directive", type: i10$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
9026
+ }
9027
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TreeComponent, decorators: [{
9028
+ type: Component,
9029
+ args: [{ selector: 'lib-tree', standalone: false, template: "<div class=\"tree\" (dragend)=\"onDragEnd()\">\n @for (flat of flatNodes; track flat.node) {\n <div\n class=\"tree-node\"\n [class.selected]=\"isSelected(flat.node)\"\n [class.dragging]=\"isDragging(flat.node)\"\n [class.drop-before]=\"drop?.row === flat.node && drop?.zone === 'before'\"\n [class.drop-after]=\"drop?.row === flat.node && drop?.zone === 'after'\"\n [class.drop-inside]=\"drop?.row === flat.node && drop?.zone === 'inside'\"\n [style.--tree-indent]=\"indentPx(flat.level)\"\n [style.--tree-drop-indent]=\"drop?.row === flat.node ? drop.indent + 'px' : null\"\n [attr.draggable]=\"allowDrag(flat.node)\"\n (click)=\"onRowClick(flat, $event)\"\n (dragstart)=\"onDragStart(flat, $event)\"\n (dragover)=\"onDragOver(flat, $event)\"\n (drop)=\"onDrop(flat, $event)\"\n >\n <i\n class=\"tree-icon fa\"\n [class.fa-folder-open-o]=\"flat.node.isFolder && flat.node.expanded\"\n [class.fa-folder-o]=\"flat.node.isFolder && !flat.node.expanded\"\n [class.fa-file-o]=\"!flat.node.isFolder\"\n aria-hidden=\"true\"\n ></i>\n <span class=\"tree-label\">\n <ng-container *ngTemplateOutlet=\"nodeTemplate || defaultTemplate; context: { $implicit: flat.node, node: flat.node }\"></ng-container>\n </span>\n </div>\n }\n\n <div class=\"tree-tail\" [class.drop-tail]=\"dropTail\" (dragover)=\"onTailDragOver($event)\" (drop)=\"onTailDrop($event)\"></div>\n</div>\n\n<ng-template #defaultTemplate let-node>{{ node.name }}</ng-template>\n", styles: [":host{display:block}.tree{display:flex;flex-direction:column;padding:5px;-webkit-user-select:none;user-select:none}.tree-node{position:relative;flex:0 0 auto;display:flex;align-items:center;height:23px;line-height:23px;padding-left:var(--tree-indent, 4px);padding-right:4px;cursor:pointer;white-space:nowrap}.tree-node:hover{background-color:#ffffff0f}.tree-node.selected{background-color:#906543}.tree-node.dragging{opacity:.4}.tree-node.drop-before,.tree-node.drop-after,.tree-node.drop-inside{z-index:1}.tree-icon{flex:0 0 auto;width:16px;margin-right:5px;text-align:center}.tree-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.tree-node.drop-before:before,.tree-node.drop-after:after{content:\"\";position:absolute;left:var(--tree-drop-indent, var(--tree-indent, 4px));right:4px;height:2px;background-color:#d39661;pointer-events:none}.tree-node.drop-before:before{top:-1px}.tree-node.drop-after:after{bottom:-1px}.tree-node.drop-inside{outline:2px solid #d39661;outline-offset:-2px}.tree-tail{flex:1 1 auto;min-height:16px}.tree-tail.drop-tail:before{content:\"\";display:block;height:2px;margin:0 4px;background-color:#d39661}\n"] }]
9030
+ }], propDecorators: { nodes: [{
9031
+ type: Input
9032
+ }], selectedNodes: [{
9033
+ type: Input
9034
+ }], allowDrag: [{
9035
+ type: Input
9036
+ }], allowDrop: [{
9037
+ type: Input
9038
+ }], selectedNodesChange: [{
9039
+ type: Output
9040
+ }], activate: [{
9041
+ type: Output
9042
+ }], nodesChange: [{
9043
+ type: Output
9044
+ }], nodeTemplate: [{
9045
+ type: ContentChild,
9046
+ args: ['nodeTemplate']
9047
+ }] } });
9048
+
8731
9049
  class DesignerModule {
8732
9050
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DesignerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
8733
9051
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.16", ngImport: i0, type: DesignerModule, declarations: [DesignerComponent,
@@ -8769,7 +9087,8 @@ class DesignerModule {
8769
9087
  FixturePoolCreateFromFileComponent,
8770
9088
  FixturePoolEditUniversesComponent,
8771
9089
  DropzoneComponent,
8772
- SortablejsDirective], imports: [i3.RouterModule, BrowserModule,
9090
+ SortablejsDirective,
9091
+ TreeComponent], imports: [i3.RouterModule, BrowserModule,
8773
9092
  BrowserAnimationsModule,
8774
9093
  FormsModule,
8775
9094
  NgxBootstrapSliderModule,
@@ -8777,7 +9096,7 @@ class DesignerModule {
8777
9096
  AccordionModule,
8778
9097
  PopoverModule,
8779
9098
  TypeaheadModule,
8780
- DropzoneModule, i3$1.ToastrModule, TreeModule], exports: [DesignerComponent] }); }
9099
+ DropzoneModule, i3$1.ToastrModule], exports: [DesignerComponent] }); }
8781
9100
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DesignerModule, providers: [provideHttpClient(withInterceptorsFromDi())], imports: [RouterModule.forRoot([], {}),
8782
9101
  BrowserModule,
8783
9102
  BrowserAnimationsModule,
@@ -8790,8 +9109,7 @@ class DesignerModule {
8790
9109
  DropzoneModule,
8791
9110
  ToastrModule.forRoot({
8792
9111
  newestOnTop: true,
8793
- }),
8794
- TreeModule] }); }
9112
+ })] }); }
8795
9113
  }
8796
9114
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DesignerModule, decorators: [{
8797
9115
  type: NgModule,
@@ -8837,6 +9155,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
8837
9155
  FixturePoolEditUniversesComponent,
8838
9156
  DropzoneComponent,
8839
9157
  SortablejsDirective,
9158
+ TreeComponent,
8840
9159
  ],
8841
9160
  exports: [DesignerComponent],
8842
9161
  imports: [
@@ -8853,7 +9172,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
8853
9172
  ToastrModule.forRoot({
8854
9173
  newestOnTop: true,
8855
9174
  }),
8856
- TreeModule,
8857
9175
  ],
8858
9176
  providers: [provideHttpClient(withInterceptorsFromDi())],
8859
9177
  }]