@rocketshow/designer 1.62.0 → 1.64.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.
@@ -24,9 +24,9 @@ import * as i5 from 'ngx-dropzone-wrapper';
24
24
  import { DropzoneModule } from 'ngx-dropzone-wrapper';
25
25
  import * as i3$2 from '@angular/forms';
26
26
  import { FormsModule } from '@angular/forms';
27
+ import Sortable from 'sortablejs';
27
28
  import * as i12 from 'ngx-bootstrap/popover';
28
29
  import { PopoverModule } from 'ngx-bootstrap/popover';
29
- import Sortable from 'sortablejs';
30
30
  import * as i10 from 'ngx-bootstrap/typeahead';
31
31
  import { TypeaheadModule } from 'ngx-bootstrap/typeahead';
32
32
  import * as i4 from 'ngx-bootstrap-slider';
@@ -1900,7 +1900,7 @@ class FixtureService {
1900
1900
  // no free space left for the needed channels
1901
1901
  return -1;
1902
1902
  }
1903
- addFixture(profile, fixturePool) {
1903
+ addFixture(profile, fixturePool, channelPool) {
1904
1904
  const fixture = new Fixture();
1905
1905
  fixture.uuid = this.uuidService.getUuid();
1906
1906
  fixture.profileUuid = profile.uuid;
@@ -1925,7 +1925,7 @@ class FixtureService {
1925
1925
  }
1926
1926
  const mode = this.getModeByFixture(profile, fixture);
1927
1927
  const channelCount = this.getModeChannelCount(profile, mode);
1928
- const firstChannel = this.getNextFreeDmxChannel(fixturePool, channelCount);
1928
+ const firstChannel = this.getNextFreeDmxChannel(channelPool ?? fixturePool, channelCount);
1929
1929
  if (firstChannel >= 0) {
1930
1930
  fixture.dmxFirstChannel = firstChannel;
1931
1931
  fixturePool.push(fixture);
@@ -2052,6 +2052,14 @@ class ConfigService {
2052
2052
  this.intro = false;
2053
2053
  this.uniqueProjectNames = false;
2054
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;
2055
2063
  this.menuHeightChanged = new Subject();
2056
2064
  }
2057
2065
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
@@ -4985,11 +4993,11 @@ class ProjectImportComponent {
4985
4993
  this.ok();
4986
4994
  }
4987
4995
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectImportComponent, deps: [{ token: i1$1.BsModalRef }, { token: i2.TranslateService }, { token: i3$1.ToastrService }, { token: ProjectLoadService }], target: i0.ɵɵFactoryTarget.Component }); }
4988
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: ProjectImportComponent, isStandalone: false, selector: "lib-project-import", host: { listeners: { "document:keydown.enter": "handleKeyboardEvent($event)" } }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.project.import-title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"bsModalRef.hide()\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <!-- Dropzone -->\n <dropzone [config]=\"dropzoneConfig\" [useDropzoneClass]=\"'false'\" [message]=\"uploadMessage\"\n (error)=\"onUploadError($event)\"></dropzone>\n</div>\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>", styles: [""], dependencies: [{ kind: "component", type: i5.DropzoneComponent, selector: "dropzone", inputs: ["disabled", "config", "message", "placeholder", "useDropzoneClass"], outputs: ["init", "error", "success", "sending", "canceled", "complete", "processing", "drop", "dragStart", "dragEnd", "dragEnter", "dragOver", "dragLeave", "thumbnail", "addedFile", "addedFiles", "removedFile", "uploadProgress", "maxFilesReached", "maxFilesExceeded", "errorMultiple", "successMultiple", "sendingMultiple", "canceledMultiple", "completeMultiple", "processingMultiple", "reset", "queueComplete", "totalUploadProgress"], exportAs: ["ngxDropzone"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] }); }
4996
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: ProjectImportComponent, isStandalone: false, selector: "lib-project-import", host: { listeners: { "document:keydown.enter": "handleKeyboardEvent($event)" } }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.project.import-title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"bsModalRef.hide()\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <!-- Dropzone -->\n <dropzone [config]=\"dropzoneConfig\" [useDropzoneClass]=\"'false'\" [message]=\"uploadMessage\" (error)=\"onUploadError($event)\"></dropzone>\n</div>\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: i5.DropzoneComponent, selector: "dropzone", inputs: ["disabled", "config", "message", "placeholder", "useDropzoneClass"], outputs: ["init", "error", "success", "sending", "canceled", "complete", "processing", "drop", "dragStart", "dragEnd", "dragEnter", "dragOver", "dragLeave", "thumbnail", "addedFile", "addedFiles", "removedFile", "uploadProgress", "maxFilesReached", "maxFilesExceeded", "errorMultiple", "successMultiple", "sendingMultiple", "canceledMultiple", "completeMultiple", "processingMultiple", "reset", "queueComplete", "totalUploadProgress"], exportAs: ["ngxDropzone"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] }); }
4989
4997
  }
4990
4998
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ProjectImportComponent, decorators: [{
4991
4999
  type: Component,
4992
- args: [{ selector: 'lib-project-import', standalone: false, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.project.import-title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"bsModalRef.hide()\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <!-- Dropzone -->\n <dropzone [config]=\"dropzoneConfig\" [useDropzoneClass]=\"'false'\" [message]=\"uploadMessage\"\n (error)=\"onUploadError($event)\"></dropzone>\n</div>\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>" }]
5000
+ args: [{ selector: 'lib-project-import', standalone: false, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.project.import-title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"bsModalRef.hide()\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <!-- Dropzone -->\n <dropzone [config]=\"dropzoneConfig\" [useDropzoneClass]=\"'false'\" [message]=\"uploadMessage\" (error)=\"onUploadError($event)\"></dropzone>\n</div>\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n" }]
4993
5001
  }], ctorParameters: () => [{ type: i1$1.BsModalRef }, { type: i2.TranslateService }, { type: i3$1.ToastrService }, { type: ProjectLoadService }], propDecorators: { handleKeyboardEvent: [{
4994
5002
  type: HostListener,
4995
5003
  args: ['document:keydown.enter', ['$event']]
@@ -5378,6 +5386,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
5378
5386
  type: Output
5379
5387
  }] } });
5380
5388
 
5389
+ class FixturePoolEditUniversesComponent {
5390
+ constructor(bsModalRef, uuidService) {
5391
+ this.bsModalRef = bsModalRef;
5392
+ this.uuidService = uuidService;
5393
+ this.universes = [];
5394
+ this.newUniverseName = '';
5395
+ this.onClose = new Subject();
5396
+ }
5397
+ addUniverse() {
5398
+ const name = (this.newUniverseName || '').trim();
5399
+ if (!name)
5400
+ return;
5401
+ this.universes.push({ uuid: this.uuidService.getUuid(), name });
5402
+ this.newUniverseName = '';
5403
+ }
5404
+ removeUniverse(universe) {
5405
+ const index = this.universes.findIndex((u) => u.uuid === universe.uuid);
5406
+ if (index >= 0) {
5407
+ this.universes.splice(index, 1);
5408
+ }
5409
+ }
5410
+ ok() {
5411
+ this.onClose.next(this.universes);
5412
+ this.bsModalRef.hide();
5413
+ }
5414
+ cancel() {
5415
+ this.onClose.next(undefined);
5416
+ this.bsModalRef.hide();
5417
+ }
5418
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: FixturePoolEditUniversesComponent, deps: [{ token: i1$1.BsModalRef }, { token: UuidService }], target: i0.ɵɵFactoryTarget.Component }); }
5419
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: FixturePoolEditUniversesComponent, isStandalone: false, selector: "lib-fixture-pool-edit-universes", ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.fixture-pool.edit-universes-title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"cancel()\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <!-- Add universe -->\n <div class=\"input-group mb-3\">\n <input\n type=\"text\"\n class=\"form-control\"\n maxlength=\"200\"\n [(ngModel)]=\"newUniverseName\"\n (keydown.enter)=\"addUniverse(); $event.stopPropagation(); $event.preventDefault()\"\n placeholder=\"{{ 'designer.fixture-pool.universe-name' | translate }}\"\n />\n <div class=\"input-group-append\">\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n [disabled]=\"!newUniverseName || !newUniverseName.trim()\"\n (click)=\"addUniverse()\"\n >\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n <!-- Sortable universe list -->\n <ul class=\"list-group\" [sortablejs]=\"universes\">\n @for (universe of universes; track universe.uuid) {\n <li class=\"list-group-item d-flex align-items-center py-1\">\n <div class=\"list-sort-handle mr-2\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"flex-grow-1\">{{ universe.name }}</span>\n <button class=\"btn btn-primary btn-sm\" type=\"button\" (click)=\"removeUniverse(universe)\">\n <i class=\"fa fa-minus\" aria-hidden=\"true\"></i>\n </button>\n </li>\n }\n </ul>\n</div>\n<div class=\"modal-footer\">\n <a class=\"mr-3 my-auto\" href=\"#\" role=\"button\" (click)=\"cancel(); (false)\">\n {{ 'designer.misc.cancel' | translate }}\n </a>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i3$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { 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" }] }); }
5420
+ }
5421
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: FixturePoolEditUniversesComponent, decorators: [{
5422
+ type: Component,
5423
+ args: [{ selector: 'lib-fixture-pool-edit-universes', standalone: false, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.fixture-pool.edit-universes-title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"cancel()\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <!-- Add universe -->\n <div class=\"input-group mb-3\">\n <input\n type=\"text\"\n class=\"form-control\"\n maxlength=\"200\"\n [(ngModel)]=\"newUniverseName\"\n (keydown.enter)=\"addUniverse(); $event.stopPropagation(); $event.preventDefault()\"\n placeholder=\"{{ 'designer.fixture-pool.universe-name' | translate }}\"\n />\n <div class=\"input-group-append\">\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n [disabled]=\"!newUniverseName || !newUniverseName.trim()\"\n (click)=\"addUniverse()\"\n >\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n <!-- Sortable universe list -->\n <ul class=\"list-group\" [sortablejs]=\"universes\">\n @for (universe of universes; track universe.uuid) {\n <li class=\"list-group-item d-flex align-items-center py-1\">\n <div class=\"list-sort-handle mr-2\" style=\"cursor: move; cursor: -webkit-grabbing\">\n <i class=\"fa fa-bars\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"flex-grow-1\">{{ universe.name }}</span>\n <button class=\"btn btn-primary btn-sm\" type=\"button\" (click)=\"removeUniverse(universe)\">\n <i class=\"fa fa-minus\" aria-hidden=\"true\"></i>\n </button>\n </li>\n }\n </ul>\n</div>\n<div class=\"modal-footer\">\n <a class=\"mr-3 my-auto\" href=\"#\" role=\"button\" (click)=\"cancel(); (false)\">\n {{ 'designer.misc.cancel' | translate }}\n </a>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n" }]
5424
+ }], ctorParameters: () => [{ type: i1$1.BsModalRef }, { type: UuidService }] });
5425
+
5381
5426
  class FixturePoolComponent {
5382
5427
  constructor(bsModalRef, fixtureService, uuidService, projectService, previewService, translateService, toastrService, presetService, configService, modalService, livePreviewService) {
5383
5428
  this.bsModalRef = bsModalRef;
@@ -5395,9 +5440,45 @@ class FixturePoolComponent {
5395
5440
  this.dmxChannels = [];
5396
5441
  this.showChannelNumbers = false;
5397
5442
  this.onClose = new Subject();
5443
+ // Universes the user can choose from. In "free edit" mode the user can
5444
+ // extend this list at runtime; otherwise it is provided by the host
5445
+ // application through the ConfigService.
5446
+ this.universes = [];
5398
5447
  this.fixturePool = structuredClone(this.projectService.project.fixtures);
5399
- if (this.fixturePool.length > 0) {
5400
- this.selectFixture(this.fixturePool[0]);
5448
+ // Initialise the universe list from the config. We keep a local copy so
5449
+ // edits done in free-edit mode don't bleed into the config until the user
5450
+ // confirms with OK (see `ok()` below).
5451
+ this.universes = structuredClone(this.configService.universes || []);
5452
+ if (this.universes.length === 0 && this.configService.freeUniverseEdit) {
5453
+ this.universes.push({ uuid: this.uuidService.getUuid(), name: 'Universe 1' });
5454
+ }
5455
+ if (this.universes.length > 0) {
5456
+ this.selectedUniverse = this.universes[0];
5457
+ }
5458
+ const firstVisible = this.currentUniverseFixtures[0] ?? this.fixturePool[0];
5459
+ if (firstVisible) {
5460
+ this.selectFixture(firstVisible);
5461
+ }
5462
+ }
5463
+ get freeUniverseEdit() {
5464
+ return this.configService.freeUniverseEdit;
5465
+ }
5466
+ /** Fixtures belonging to the currently selected universe. */
5467
+ get currentUniverseFixtures() {
5468
+ if (!this.selectedUniverse) {
5469
+ return this.fixturePool;
5470
+ }
5471
+ return this.fixturePool.filter((f) => f.dmxUniverseUuid === this.selectedUniverse.uuid);
5472
+ }
5473
+ /** Fixtures used for channel-map calculations (same as currentUniverseFixtures). */
5474
+ get channelFixtures() {
5475
+ return this.currentUniverseFixtures;
5476
+ }
5477
+ selectUniverse(universe) {
5478
+ this.selectedUniverse = universe;
5479
+ // Deselect the current fixture if it doesn't belong to the new universe.
5480
+ if (this.selectedFixture && this.selectedFixture.dmxUniverseUuid !== universe?.uuid) {
5481
+ this.selectFixture(this.currentUniverseFixtures[0] ?? undefined);
5401
5482
  }
5402
5483
  }
5403
5484
  ngOnInit() {
@@ -5447,8 +5528,9 @@ class FixturePoolComponent {
5447
5528
  // minimal profile passed from the search.
5448
5529
  this.fixtureService.loadProfileByUuid(searchProfile.uuid).subscribe(() => {
5449
5530
  const profile = this.fixtureService.getProfileByUuid(searchProfile.uuid);
5450
- const newFixture = this.fixtureService.addFixture(profile, this.fixturePool);
5531
+ const newFixture = this.fixtureService.addFixture(profile, this.fixturePool, this.currentUniverseFixtures);
5451
5532
  if (newFixture) {
5533
+ newFixture.dmxUniverseUuid = this.selectedUniverse?.uuid;
5452
5534
  this.selectFixture(newFixture);
5453
5535
  }
5454
5536
  });
@@ -5460,7 +5542,7 @@ class FixturePoolComponent {
5460
5542
  fixture.name = originalFixture.name;
5461
5543
  fixture.modeShortName = originalFixture.modeShortName;
5462
5544
  fixture.dmxFirstChannel = originalFixture.dmxFirstChannel;
5463
- fixture.dmxUniverseUuid = originalFixture.dmxUniverseUuid;
5545
+ fixture.dmxUniverseUuid = this.selectedUniverse?.uuid;
5464
5546
  this.fixturePool.push(fixture);
5465
5547
  this.selectFixture(fixture);
5466
5548
  }
@@ -5474,8 +5556,8 @@ class FixturePoolComponent {
5474
5556
  break;
5475
5557
  }
5476
5558
  }
5477
- if (!this.selectedFixture && this.fixturePool && this.fixturePool.length > 0) {
5478
- this.selectFixture((this.selectedFixture = this.fixturePool[0]));
5559
+ if (!this.selectedFixture) {
5560
+ this.selectFixture(this.currentUniverseFixtures[0] ?? undefined);
5479
5561
  }
5480
5562
  // remove unused profiles
5481
5563
  for (let i = 0; i < this.projectService.project.fixtureProfiles.length; i++) {
@@ -5492,7 +5574,7 @@ class FixturePoolComponent {
5492
5574
  }
5493
5575
  }
5494
5576
  channelOccupied(index) {
5495
- for (const fixture of this.fixturePool) {
5577
+ for (const fixture of this.channelFixtures) {
5496
5578
  const profile = this.fixtureService.getProfileByUuid(fixture.profileUuid);
5497
5579
  const mode = this.fixtureService.getModeByFixture(profile, fixture);
5498
5580
  const channelCount = this.fixtureService.getModeChannelCount(profile, mode);
@@ -5503,7 +5585,7 @@ class FixturePoolComponent {
5503
5585
  return false;
5504
5586
  }
5505
5587
  channelOccupiedStart(index) {
5506
- for (const fixture of this.fixturePool) {
5588
+ for (const fixture of this.channelFixtures) {
5507
5589
  if (index === fixture.dmxFirstChannel) {
5508
5590
  return true;
5509
5591
  }
@@ -5511,7 +5593,7 @@ class FixturePoolComponent {
5511
5593
  return false;
5512
5594
  }
5513
5595
  channelOccupiedEnd(index) {
5514
- for (const fixture of this.fixturePool) {
5596
+ for (const fixture of this.channelFixtures) {
5515
5597
  const profile = this.fixtureService.getProfileByUuid(fixture.profileUuid);
5516
5598
  const mode = this.fixtureService.getModeByFixture(profile, fixture);
5517
5599
  const channelCount = this.fixtureService.getModeChannelCount(profile, mode);
@@ -5522,8 +5604,11 @@ class FixturePoolComponent {
5522
5604
  return false;
5523
5605
  }
5524
5606
  channelOverlapped(index) {
5607
+ return this.channelOverlappedInFixtures(index, this.channelFixtures);
5608
+ }
5609
+ channelOverlappedInFixtures(index, fixtures) {
5525
5610
  let occupiedFixture;
5526
- for (const fixture of this.fixturePool) {
5611
+ for (const fixture of fixtures) {
5527
5612
  const profile = this.fixtureService.getProfileByUuid(fixture.profileUuid);
5528
5613
  const mode = this.fixtureService.getModeByFixture(profile, fixture);
5529
5614
  const channelCount = this.fixtureService.getModeChannelCount(profile, mode);
@@ -5548,7 +5633,7 @@ class FixturePoolComponent {
5548
5633
  let newSelectedFixture;
5549
5634
  // find a dragging fixture and select it, but don't change the selection, if the
5550
5635
  // currently selected fixture might also be selected (on overlapped fixtures)
5551
- for (const fixture of this.fixturePool) {
5636
+ for (const fixture of this.channelFixtures) {
5552
5637
  const profile = this.fixtureService.getProfileByUuid(fixture.profileUuid);
5553
5638
  const mode = this.fixtureService.getModeByFixture(profile, fixture);
5554
5639
  const channelCount = this.fixtureService.getModeChannelCount(profile, mode);
@@ -5604,15 +5689,18 @@ class FixturePoolComponent {
5604
5689
  }
5605
5690
  }
5606
5691
  ok() {
5607
- // don't allow OK if overlapping fixtures exist
5608
- for (let i = 0; i < this.dmxChannels.length; i++) {
5609
- if (this.channelOverlapped(i)) {
5610
- const msg = 'designer.fixture-pool.channel-occupied';
5611
- const title = 'designer.fixture-pool.channel-occupied-title';
5612
- this.translateService.get([msg, title]).subscribe((result) => {
5613
- this.toastrService.error(result[msg], result[title]);
5614
- });
5615
- return;
5692
+ // don't allow OK if overlapping fixtures exist within any universe
5693
+ for (const universe of this.universes) {
5694
+ const universeFixtures = this.fixturePool.filter((f) => f.dmxUniverseUuid === universe.uuid);
5695
+ for (let i = 0; i < this.dmxChannels.length; i++) {
5696
+ if (this.channelOverlappedInFixtures(i, universeFixtures)) {
5697
+ const msg = 'designer.fixture-pool.channel-occupied';
5698
+ const title = 'designer.fixture-pool.channel-occupied-title';
5699
+ this.translateService.get([msg, title]).subscribe((result) => {
5700
+ this.toastrService.error(result[msg], result[title]);
5701
+ });
5702
+ return;
5703
+ }
5616
5704
  }
5617
5705
  }
5618
5706
  // remove deleted fixtures/pixel keys from preset fixtures
@@ -5669,6 +5757,11 @@ class FixturePoolComponent {
5669
5757
  }
5670
5758
  }
5671
5759
  this.projectService.project.fixtures = this.fixturePool;
5760
+ // Persist any locally edited universes back to the config when the
5761
+ // host application has opted in to free editing.
5762
+ if (this.configService.freeUniverseEdit) {
5763
+ this.configService.universes = this.universes;
5764
+ }
5672
5765
  this.fixtureService.updateCachedFixtures();
5673
5766
  this.presetService.removeDeletedFixtures();
5674
5767
  this.previewService.updateFixtureSetup();
@@ -5706,10 +5799,45 @@ class FixturePoolComponent {
5706
5799
  });
5707
5800
  });
5708
5801
  }
5802
+ openEditUniverses() {
5803
+ const ref = this.modalService.show(FixturePoolEditUniversesComponent, {
5804
+ keyboard: true,
5805
+ ignoreBackdropClick: false,
5806
+ initialState: { universes: structuredClone(this.universes) },
5807
+ });
5808
+ ref.content.onClose.subscribe((result) => {
5809
+ if (!result)
5810
+ return;
5811
+ // Clear fixture universe assignments for any removed universe
5812
+ for (const universe of this.universes) {
5813
+ if (!result.find((u) => u.uuid === universe.uuid)) {
5814
+ for (const fixture of this.fixturePool) {
5815
+ if (fixture.dmxUniverseUuid === universe.uuid) {
5816
+ fixture.dmxUniverseUuid = undefined;
5817
+ }
5818
+ }
5819
+ }
5820
+ }
5821
+ this.universes = result;
5822
+ // Re-anchor selectedUniverse to the new object reference from result.
5823
+ // The modal works on a structuredClone, so all objects have new references.
5824
+ const stillSelected = this.universes.find((u) => u.uuid === this.selectedUniverse?.uuid);
5825
+ if (stillSelected) {
5826
+ this.selectedUniverse = stillSelected;
5827
+ }
5828
+ else {
5829
+ this.selectedUniverse = this.universes[0] ?? undefined;
5830
+ this.selectFixture(this.currentUniverseFixtures[0] ?? undefined);
5831
+ }
5832
+ });
5833
+ }
5709
5834
  createFixtureFromProfileFile() {
5710
5835
  const bsModalRef = this.modalService.show(FixturePoolCreateFromFileComponent, { keyboard: true, ignoreBackdropClick: false });
5711
5836
  bsModalRef.content.onClose.subscribe((profile) => {
5712
- const newFixture = this.fixtureService.addFixture(profile, this.fixturePool);
5837
+ const newFixture = this.fixtureService.addFixture(profile, this.fixturePool, this.currentUniverseFixtures);
5838
+ if (newFixture) {
5839
+ newFixture.dmxUniverseUuid = this.selectedUniverse?.uuid;
5840
+ }
5713
5841
  this.selectFixture(newFixture);
5714
5842
  });
5715
5843
  }
@@ -5717,11 +5845,11 @@ class FixturePoolComponent {
5717
5845
  this.ok();
5718
5846
  }
5719
5847
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: FixturePoolComponent, deps: [{ token: i1$1.BsModalRef }, { token: FixtureService }, { token: UuidService }, { token: ProjectService }, { token: PreviewService }, { token: i2.TranslateService }, { token: i3$1.ToastrService }, { token: PresetService }, { token: ConfigService }, { token: i1$1.BsModalService }, { token: LivePreviewService }], target: i0.ɵɵFactoryTarget.Component }); }
5720
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: FixturePoolComponent, isStandalone: false, selector: "lib-app-fixture-pool", host: { listeners: { "window:mouseup": "mouseUp($event)", "document:keydown.enter": "handleKeyboardEvent($event)" } }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.fixture-pool.title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"cancel(); (false)\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <div class=\"row\">\n <div class=\"col-6\">\n <!-- Search for profiles -->\n <input\n type=\"text\"\n class=\"form-control input-block mb-3\"\n id=\"searchSet\"\n placeholder=\"{{ 'designer.fixture-pool.search-profiles' | translate }}\"\n (input)=\"searchExpression = $event.target.value; filterProfiles()\"\n />\n\n <!-- Profiles -->\n <div class=\"card border-secondary w-100\" style=\"height: 200px\">\n <div class=\"card-body p-0 w-100\">\n <div class=\"list-group list-group-flush w-100\">\n @if (loadingProfiles) {\n <a class=\"list-group-item flex-column align-items-start d-flex\">\n <p class=\"mb-0 mx-auto\">\n <i class=\"fa fa-spinner fa-pulse fa-fw\"></i>\n </p>\n </a>\n } @for (profile of filteredProfiles | sort : 'uuid'; track profile) {\n <div class=\"list-group-item\">\n <div class=\"row\">\n <div class=\"col-10 col-auto my-auto pl-2\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(profile) }} mr-1\"></span> {{ profile.manufacturerName }} -\n {{ profile.name }}\n </p>\n </div>\n <div class=\"col-2 my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"addFixture(profile); (false)\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div>\n <p class=\"d-inline-block mb-0 mt-2\">\n Powered by the <a href=\"https://open-fixture-library.org/\" target=\"_blank\">Open Fixture Library</a>\n </p>\n <a\n class=\"mt-2 btn btn-secondary btn-sm float-right\"\n href=\"https://open-fixture-library.org/fixture-editor\"\n target=\"_blank\"\n role=\"button\"\n >\n <i class=\"fa fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.add-profile' | translate }}\n </a>\n @if (configService.localProfiles) {\n <button\n type=\"button\"\n class=\"mr-2 mt-2 btn btn-secondary btn-sm float-right\"\n (click)=\"updateProfiles(); (false)\"\n [disabled]=\"updatingProfiles\"\n role=\"button\"\n >\n @if (updatingProfiles) {\n <i class=\"fa fa-spinner fa-pulse fa-fw\" aria-hidden=\"true\"></i>\n } @if (!updatingProfiles) {\n <i class=\"fa fa-download fa-fw\" aria-hidden=\"true\"></i>\n }\n {{ 'designer.fixture-pool.update-profiles' | translate }}\n </button>\n }\n </div>\n </div>\n <div class=\"col-6\">\n <div class=\"row\">\n <!-- Add fixture from local profile -->\n <div class=\"col-6\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"createFixtureFromProfileFile()\" role=\"button\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.create-fixture-from-profile-file' | translate }}\n </button>\n </div>\n\n <!-- Universe -->\n <div class=\"col-6\">\n <div class=\"form-group row\">\n <label class=\"col-lg-4 col-form-label\">{{ 'designer.fixture-pool.universe' | translate }}</label>\n <div class=\"col-lg-8\">\n <select class=\"custom-select\">\n <option>Universe 1</option>\n </select>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Fixtures -->\n <div class=\"card border-secondary\" style=\"height: 235px\">\n <div class=\"card-body p-0\">\n <div class=\"list-group list-group-flush\" [sortablejs]=\"fixturePool\">\n @for (fixture of fixturePool; track fixture) {\n <div\n class=\"list-group-item\"\n [class.active]=\"fixture == selectedFixture\"\n (click)=\"selectFixture(fixture)\"\n style=\"cursor: pointer\"\n >\n <div class=\"row\">\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=\"col-auto flex-grow pl-0 my-auto\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(fixtureService.getProfileByUuid(fixture.profileUuid)) }} mr-1\">\n </span\n >{{ fixture.name }}\n </p>\n </div>\n <div class=\"col-auto my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"removeFixture(fixture); (false)\">\n <i class=\"fa fa-minus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row mt-3\">\n <div class=\"col\">\n <!-- Tab navigation -->\n <ul class=\"nav nav-tabs mb-4\" id=\"myTab\" role=\"tablist\">\n <li class=\"nav-item\">\n <a class=\"nav-link active\" id=\"sets-tab\" data-toggle=\"tab\" href=\"#dmx\" role=\"tab\">\n <i class=\"icon-ola\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.dmx' | translate }}\n </a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link\" id=\"compositions-tab\" data-toggle=\"tab\" href=\"#settings\" role=\"tab\">\n <i class=\"fa fa-cog\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.settings' | translate }}\n </a>\n </li>\n </ul>\n\n <!-- Tab content -->\n <div class=\"tab-content\" id=\"myTabContent\">\n <!-- DMX universe overview-->\n <div class=\"tab-pane show active\" id=\"dmx\" role=\"tabpanel\">\n <!-- Show all channel numbers -->\n <!-- <div class=\"form-group row\">\n <div class=\"col-auto flex-grow\"></div>\n <div class=\"col-lg-3 d-flex\">\n <div class=\"form-check my-auto ml-auto\">\n <input type=\"checkbox\" class=\"form-check-input\" id=\"showChannelNumbers\" [ngModel]=\"showChannelNumbers\"\n (ngModelChange)=\"showChannelNumbers = $event\">\n <label class=\"form-check-label\" for=\"showChannelNumbers\">Show channels</label>\n </div>\n </div>\n </div> -->\n\n <!-- List of DMX channels -->\n <div>\n @for (channel of dmxChannels; track $index; let i = $index) {\n <div\n class=\"dmx-channel\"\n [class.dmx-channel-occupied]=\"channelOccupied(i)\"\n [class.dmx-channel-occupied-start]=\"channelOccupiedStart(i)\"\n [class.dmx-channel-occupied-end]=\"channelOccupiedEnd(i)\"\n [class.dmx-channel-selected]=\"channelSelected(i)\"\n [class.dmx-channel-overlapped]=\"channelOverlapped(i)\"\n (mousedown)=\"channelMouseDown($event)\"\n (mouseover)=\"channelMouseOver($event)\"\n [attr.data-index]=\"i\"\n popover=\"{{ i + 1 }}\"\n placement=\"top\"\n triggers=\"mouseenter:mouseleave\"\n >\n <div class=\"d-flex w-100 dmx-channel-text\" [attr.data-index]=\"i\" [class.dmx-channel-text-visible]=\"showChannelNumbers\">\n <div class=\"m-auto\" style=\"font-size: 11px\" [attr.data-index]=\"i\"><small [attr.data-index]=\"i\">&nbsp;</small></div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Fixture settings -->\n <div class=\"tab-pane\" id=\"settings\" role=\"tabpanel\">\n @if (selectedFixture && selectedFixtureProfile) {\n <div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\" for=\"fixtureName\">{{ 'designer.fixture-pool.fixture-name' | translate }}</label>\n <div class=\"col-lg-9\">\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"selectedFixture.name\" maxlength=\"200\" id=\"fixtureName\" />\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.mode' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.modeShortName\"\n (ngModelChange)=\"selectedFixture.modeShortName = $event\"\n >\n @for (mode of selectedFixtureProfile.modes; track mode) {\n <option [ngValue]=\"mode.shortName || mode.name\">\n {{ mode.name }}\n - {{ fixtureService.getModeChannelCount(selectedFixtureProfile, mode) }}\n {{ 'designer.fixture-pool.channels' | translate }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.first-channel' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.dmxFirstChannel\"\n (ngModelChange)=\"selectedFixture.dmxFirstChannel = $event\"\n >\n @for (channel of dmxChannels; track $index; let i = $index) {\n <option [ngValue]=\"i\">\n {{ i + 1 }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\"></label>\n <div class=\"col-lg-9\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"addCopy(selectedFixture)\">\n {{ 'designer.fixture.add-copy' | translate }}\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n</div>\n<div class=\"modal-footer\">\n <a class=\"mr-3 my-auto\" href=\"#\" role=\"button\" (click)=\"cancel(); (false)\">\n {{ 'designer.misc.cancel' | translate }}\n </a>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n", styles: [".list-group-item{padding:.3rem 1.25rem}.dmx-channel{display:flex;float:left;width:28px;border:1px solid white;margin-left:-1px;margin-bottom:-1px;padding-bottom:2px}.dmx-channel-occupied{background-color:#63462e;border-left:none;border-right:none;cursor:move}.dmx-channel-occupied-start{border-left:1px solid white;border-top-left-radius:8px;border-bottom-left-radius:8px}.dmx-channel-occupied-end{border-right:1px solid white;border-top-right-radius:8px;border-bottom-right-radius:8px}.dmx-channel-selected{background-color:#fd7e14}.dmx-channel-overlapped{background-color:red;border:1px solid red;cursor:move}.card{overflow:auto}\n"], dependencies: [{ kind: "directive", type: i3$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: "directive", type: SortablejsDirective, selector: "[sortablejs]", inputs: ["sortablejs", "sortablejsContainer", "sortablejsOptions"], outputs: ["sortablejsInit"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: ArraySortPipe, name: "sort" }] }); }
5848
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: FixturePoolComponent, isStandalone: false, selector: "lib-app-fixture-pool", host: { listeners: { "window:mouseup": "mouseUp($event)", "document:keydown.enter": "handleKeyboardEvent($event)" } }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.fixture-pool.title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"cancel(); (false)\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <div class=\"row\">\n <div class=\"col-6\">\n <!-- Search for profiles -->\n <input\n type=\"text\"\n class=\"form-control input-block mb-3\"\n id=\"searchSet\"\n placeholder=\"{{ 'designer.fixture-pool.search-profiles' | translate }}\"\n (input)=\"searchExpression = $event.target.value; filterProfiles()\"\n />\n\n <!-- Profiles -->\n <div class=\"card border-secondary w-100\" style=\"height: 200px\">\n <div class=\"card-body p-0 w-100\">\n <div class=\"list-group list-group-flush w-100\">\n @if (loadingProfiles) {\n <a class=\"list-group-item flex-column align-items-start d-flex\">\n <p class=\"mb-0 mx-auto\">\n <i class=\"fa fa-spinner fa-pulse fa-fw\"></i>\n </p>\n </a>\n } @for (profile of filteredProfiles | sort : 'uuid'; track profile) {\n <div class=\"list-group-item\">\n <div class=\"row\">\n <div class=\"col-10 col-auto my-auto pl-2\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(profile) }} mr-1\"></span> {{ profile.manufacturerName }} -\n {{ profile.name }}\n </p>\n </div>\n <div class=\"col-2 my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"addFixture(profile); (false)\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div>\n <p class=\"d-inline-block mb-0 mt-2\">\n Powered by the <a href=\"https://open-fixture-library.org/\" target=\"_blank\">Open Fixture Library</a>\n </p>\n <a\n class=\"mt-2 btn btn-secondary btn-sm float-right\"\n href=\"https://open-fixture-library.org/fixture-editor\"\n target=\"_blank\"\n role=\"button\"\n >\n <i class=\"fa fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.add-profile' | translate }}\n </a>\n @if (configService.localProfiles) {\n <button\n type=\"button\"\n class=\"mr-2 mt-2 btn btn-secondary btn-sm float-right\"\n (click)=\"updateProfiles(); (false)\"\n [disabled]=\"updatingProfiles\"\n role=\"button\"\n >\n @if (updatingProfiles) {\n <i class=\"fa fa-spinner fa-pulse fa-fw\" aria-hidden=\"true\"></i>\n } @if (!updatingProfiles) {\n <i class=\"fa fa-download fa-fw\" aria-hidden=\"true\"></i>\n }\n {{ 'designer.fixture-pool.update-profiles' | translate }}\n </button>\n }\n </div>\n </div>\n <div class=\"col-6\">\n <div class=\"row\">\n <!-- Add fixture from local profile -->\n <div class=\"col-6\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"createFixtureFromProfileFile()\" role=\"button\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.create-fixture-from-profile-file' | translate }}\n </button>\n </div>\n\n <!-- Universe -->\n <div class=\"col-6\">\n <div class=\"form-group row\">\n <label class=\"col-lg-4 col-form-label\">{{ 'designer.fixture-pool.universe' | translate }}</label>\n <div class=\"col-lg-8\">\n <div class=\"input-group\">\n <select\n class=\"custom-select\"\n [disabled]=\"universes.length === 0\"\n [ngModel]=\"selectedUniverse\"\n (ngModelChange)=\"selectUniverse($event)\"\n >\n @for (universe of universes; track universe.uuid) {\n <option [ngValue]=\"universe\">{{ universe.name }}</option>\n }\n </select>\n @if (freeUniverseEdit) {\n <div class=\"input-group-append\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"openEditUniverses()\">\n <i class=\"fa fa-pencil\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Fixtures -->\n <div class=\"card border-secondary\" style=\"height: 235px\">\n <div class=\"card-body p-0\">\n <div class=\"list-group list-group-flush\" [sortablejs]=\"fixturePool\">\n @for (fixture of currentUniverseFixtures; track fixture) {\n <div\n class=\"list-group-item\"\n [class.active]=\"fixture == selectedFixture\"\n (click)=\"selectFixture(fixture)\"\n style=\"cursor: pointer\"\n >\n <div class=\"row\">\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=\"col-auto flex-grow pl-0 my-auto\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(fixtureService.getProfileByUuid(fixture.profileUuid)) }} mr-1\">\n </span\n >{{ fixture.name }}\n </p>\n </div>\n <div class=\"col-auto my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"removeFixture(fixture); (false)\">\n <i class=\"fa fa-minus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row mt-3\">\n <div class=\"col\">\n <!-- Tab navigation -->\n <ul class=\"nav nav-tabs mb-4\" id=\"myTab\" role=\"tablist\">\n <li class=\"nav-item\">\n <a class=\"nav-link active\" id=\"sets-tab\" data-toggle=\"tab\" href=\"#dmx\" role=\"tab\">\n <i class=\"icon-ola\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.dmx' | translate }}\n </a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link\" id=\"compositions-tab\" data-toggle=\"tab\" href=\"#settings\" role=\"tab\">\n <i class=\"fa fa-cog\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.settings' | translate }}\n </a>\n </li>\n </ul>\n\n <!-- Tab content -->\n <div class=\"tab-content\" id=\"myTabContent\">\n <!-- DMX universe overview-->\n <div class=\"tab-pane show active\" id=\"dmx\" role=\"tabpanel\">\n <!-- Show all channel numbers -->\n <!-- <div class=\"form-group row\">\n <div class=\"col-auto flex-grow\"></div>\n <div class=\"col-lg-3 d-flex\">\n <div class=\"form-check my-auto ml-auto\">\n <input type=\"checkbox\" class=\"form-check-input\" id=\"showChannelNumbers\" [ngModel]=\"showChannelNumbers\"\n (ngModelChange)=\"showChannelNumbers = $event\">\n <label class=\"form-check-label\" for=\"showChannelNumbers\">Show channels</label>\n </div>\n </div>\n </div> -->\n\n <!-- List of DMX channels -->\n <div>\n @for (channel of dmxChannels; track $index; let i = $index) {\n <div\n class=\"dmx-channel\"\n [class.dmx-channel-occupied]=\"channelOccupied(i)\"\n [class.dmx-channel-occupied-start]=\"channelOccupiedStart(i)\"\n [class.dmx-channel-occupied-end]=\"channelOccupiedEnd(i)\"\n [class.dmx-channel-selected]=\"channelSelected(i)\"\n [class.dmx-channel-overlapped]=\"channelOverlapped(i)\"\n (mousedown)=\"channelMouseDown($event)\"\n (mouseover)=\"channelMouseOver($event)\"\n [attr.data-index]=\"i\"\n popover=\"{{ i + 1 }}\"\n placement=\"top\"\n triggers=\"mouseenter:mouseleave\"\n >\n <div class=\"d-flex w-100 dmx-channel-text\" [attr.data-index]=\"i\" [class.dmx-channel-text-visible]=\"showChannelNumbers\">\n <div class=\"m-auto\" style=\"font-size: 11px\" [attr.data-index]=\"i\"><small [attr.data-index]=\"i\">&nbsp;</small></div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Fixture settings -->\n <div class=\"tab-pane\" id=\"settings\" role=\"tabpanel\">\n @if (selectedFixture && selectedFixtureProfile) {\n <div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\" for=\"fixtureName\">{{ 'designer.fixture-pool.fixture-name' | translate }}</label>\n <div class=\"col-lg-9\">\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"selectedFixture.name\" maxlength=\"200\" id=\"fixtureName\" />\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.mode' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.modeShortName\"\n (ngModelChange)=\"selectedFixture.modeShortName = $event\"\n >\n @for (mode of selectedFixtureProfile.modes; track mode) {\n <option [ngValue]=\"mode.shortName || mode.name\">\n {{ mode.name }}\n - {{ fixtureService.getModeChannelCount(selectedFixtureProfile, mode) }}\n {{ 'designer.fixture-pool.channels' | translate }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.first-channel' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.dmxFirstChannel\"\n (ngModelChange)=\"selectedFixture.dmxFirstChannel = $event\"\n >\n @for (channel of dmxChannels; track $index; let i = $index) {\n <option [ngValue]=\"i\">\n {{ i + 1 }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\"></label>\n <div class=\"col-lg-9\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"addCopy(selectedFixture)\">\n {{ 'designer.fixture.add-copy' | translate }}\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n</div>\n<div class=\"modal-footer\">\n <a class=\"mr-3 my-auto\" href=\"#\" role=\"button\" (click)=\"cancel(); (false)\">\n {{ 'designer.misc.cancel' | translate }}\n </a>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n", styles: [".list-group-item{padding:.3rem 1.25rem}.dmx-channel{display:flex;float:left;width:28px;border:1px solid white;margin-left:-1px;margin-bottom:-1px;padding-bottom:2px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.dmx-channel-occupied{background-color:#63462e;border-left:none;border-right:none;cursor:move}.dmx-channel-occupied-start{border-left:1px solid white;border-top-left-radius:8px;border-bottom-left-radius:8px}.dmx-channel-occupied-end{border-right:1px solid white;border-top-right-radius:8px;border-bottom-right-radius:8px}.dmx-channel-selected{background-color:#fd7e14}.dmx-channel-overlapped{background-color:red;border:1px solid red;cursor:move}.card{overflow:auto}\n"], dependencies: [{ kind: "directive", type: i3$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: "directive", type: SortablejsDirective, selector: "[sortablejs]", inputs: ["sortablejs", "sortablejsContainer", "sortablejsOptions"], outputs: ["sortablejsInit"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: ArraySortPipe, name: "sort" }] }); }
5721
5849
  }
5722
5850
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: FixturePoolComponent, decorators: [{
5723
5851
  type: Component,
5724
- args: [{ selector: 'lib-app-fixture-pool', standalone: false, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.fixture-pool.title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"cancel(); (false)\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <div class=\"row\">\n <div class=\"col-6\">\n <!-- Search for profiles -->\n <input\n type=\"text\"\n class=\"form-control input-block mb-3\"\n id=\"searchSet\"\n placeholder=\"{{ 'designer.fixture-pool.search-profiles' | translate }}\"\n (input)=\"searchExpression = $event.target.value; filterProfiles()\"\n />\n\n <!-- Profiles -->\n <div class=\"card border-secondary w-100\" style=\"height: 200px\">\n <div class=\"card-body p-0 w-100\">\n <div class=\"list-group list-group-flush w-100\">\n @if (loadingProfiles) {\n <a class=\"list-group-item flex-column align-items-start d-flex\">\n <p class=\"mb-0 mx-auto\">\n <i class=\"fa fa-spinner fa-pulse fa-fw\"></i>\n </p>\n </a>\n } @for (profile of filteredProfiles | sort : 'uuid'; track profile) {\n <div class=\"list-group-item\">\n <div class=\"row\">\n <div class=\"col-10 col-auto my-auto pl-2\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(profile) }} mr-1\"></span> {{ profile.manufacturerName }} -\n {{ profile.name }}\n </p>\n </div>\n <div class=\"col-2 my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"addFixture(profile); (false)\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div>\n <p class=\"d-inline-block mb-0 mt-2\">\n Powered by the <a href=\"https://open-fixture-library.org/\" target=\"_blank\">Open Fixture Library</a>\n </p>\n <a\n class=\"mt-2 btn btn-secondary btn-sm float-right\"\n href=\"https://open-fixture-library.org/fixture-editor\"\n target=\"_blank\"\n role=\"button\"\n >\n <i class=\"fa fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.add-profile' | translate }}\n </a>\n @if (configService.localProfiles) {\n <button\n type=\"button\"\n class=\"mr-2 mt-2 btn btn-secondary btn-sm float-right\"\n (click)=\"updateProfiles(); (false)\"\n [disabled]=\"updatingProfiles\"\n role=\"button\"\n >\n @if (updatingProfiles) {\n <i class=\"fa fa-spinner fa-pulse fa-fw\" aria-hidden=\"true\"></i>\n } @if (!updatingProfiles) {\n <i class=\"fa fa-download fa-fw\" aria-hidden=\"true\"></i>\n }\n {{ 'designer.fixture-pool.update-profiles' | translate }}\n </button>\n }\n </div>\n </div>\n <div class=\"col-6\">\n <div class=\"row\">\n <!-- Add fixture from local profile -->\n <div class=\"col-6\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"createFixtureFromProfileFile()\" role=\"button\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.create-fixture-from-profile-file' | translate }}\n </button>\n </div>\n\n <!-- Universe -->\n <div class=\"col-6\">\n <div class=\"form-group row\">\n <label class=\"col-lg-4 col-form-label\">{{ 'designer.fixture-pool.universe' | translate }}</label>\n <div class=\"col-lg-8\">\n <select class=\"custom-select\">\n <option>Universe 1</option>\n </select>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Fixtures -->\n <div class=\"card border-secondary\" style=\"height: 235px\">\n <div class=\"card-body p-0\">\n <div class=\"list-group list-group-flush\" [sortablejs]=\"fixturePool\">\n @for (fixture of fixturePool; track fixture) {\n <div\n class=\"list-group-item\"\n [class.active]=\"fixture == selectedFixture\"\n (click)=\"selectFixture(fixture)\"\n style=\"cursor: pointer\"\n >\n <div class=\"row\">\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=\"col-auto flex-grow pl-0 my-auto\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(fixtureService.getProfileByUuid(fixture.profileUuid)) }} mr-1\">\n </span\n >{{ fixture.name }}\n </p>\n </div>\n <div class=\"col-auto my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"removeFixture(fixture); (false)\">\n <i class=\"fa fa-minus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row mt-3\">\n <div class=\"col\">\n <!-- Tab navigation -->\n <ul class=\"nav nav-tabs mb-4\" id=\"myTab\" role=\"tablist\">\n <li class=\"nav-item\">\n <a class=\"nav-link active\" id=\"sets-tab\" data-toggle=\"tab\" href=\"#dmx\" role=\"tab\">\n <i class=\"icon-ola\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.dmx' | translate }}\n </a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link\" id=\"compositions-tab\" data-toggle=\"tab\" href=\"#settings\" role=\"tab\">\n <i class=\"fa fa-cog\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.settings' | translate }}\n </a>\n </li>\n </ul>\n\n <!-- Tab content -->\n <div class=\"tab-content\" id=\"myTabContent\">\n <!-- DMX universe overview-->\n <div class=\"tab-pane show active\" id=\"dmx\" role=\"tabpanel\">\n <!-- Show all channel numbers -->\n <!-- <div class=\"form-group row\">\n <div class=\"col-auto flex-grow\"></div>\n <div class=\"col-lg-3 d-flex\">\n <div class=\"form-check my-auto ml-auto\">\n <input type=\"checkbox\" class=\"form-check-input\" id=\"showChannelNumbers\" [ngModel]=\"showChannelNumbers\"\n (ngModelChange)=\"showChannelNumbers = $event\">\n <label class=\"form-check-label\" for=\"showChannelNumbers\">Show channels</label>\n </div>\n </div>\n </div> -->\n\n <!-- List of DMX channels -->\n <div>\n @for (channel of dmxChannels; track $index; let i = $index) {\n <div\n class=\"dmx-channel\"\n [class.dmx-channel-occupied]=\"channelOccupied(i)\"\n [class.dmx-channel-occupied-start]=\"channelOccupiedStart(i)\"\n [class.dmx-channel-occupied-end]=\"channelOccupiedEnd(i)\"\n [class.dmx-channel-selected]=\"channelSelected(i)\"\n [class.dmx-channel-overlapped]=\"channelOverlapped(i)\"\n (mousedown)=\"channelMouseDown($event)\"\n (mouseover)=\"channelMouseOver($event)\"\n [attr.data-index]=\"i\"\n popover=\"{{ i + 1 }}\"\n placement=\"top\"\n triggers=\"mouseenter:mouseleave\"\n >\n <div class=\"d-flex w-100 dmx-channel-text\" [attr.data-index]=\"i\" [class.dmx-channel-text-visible]=\"showChannelNumbers\">\n <div class=\"m-auto\" style=\"font-size: 11px\" [attr.data-index]=\"i\"><small [attr.data-index]=\"i\">&nbsp;</small></div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Fixture settings -->\n <div class=\"tab-pane\" id=\"settings\" role=\"tabpanel\">\n @if (selectedFixture && selectedFixtureProfile) {\n <div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\" for=\"fixtureName\">{{ 'designer.fixture-pool.fixture-name' | translate }}</label>\n <div class=\"col-lg-9\">\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"selectedFixture.name\" maxlength=\"200\" id=\"fixtureName\" />\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.mode' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.modeShortName\"\n (ngModelChange)=\"selectedFixture.modeShortName = $event\"\n >\n @for (mode of selectedFixtureProfile.modes; track mode) {\n <option [ngValue]=\"mode.shortName || mode.name\">\n {{ mode.name }}\n - {{ fixtureService.getModeChannelCount(selectedFixtureProfile, mode) }}\n {{ 'designer.fixture-pool.channels' | translate }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.first-channel' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.dmxFirstChannel\"\n (ngModelChange)=\"selectedFixture.dmxFirstChannel = $event\"\n >\n @for (channel of dmxChannels; track $index; let i = $index) {\n <option [ngValue]=\"i\">\n {{ i + 1 }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\"></label>\n <div class=\"col-lg-9\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"addCopy(selectedFixture)\">\n {{ 'designer.fixture.add-copy' | translate }}\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n</div>\n<div class=\"modal-footer\">\n <a class=\"mr-3 my-auto\" href=\"#\" role=\"button\" (click)=\"cancel(); (false)\">\n {{ 'designer.misc.cancel' | translate }}\n </a>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n", styles: [".list-group-item{padding:.3rem 1.25rem}.dmx-channel{display:flex;float:left;width:28px;border:1px solid white;margin-left:-1px;margin-bottom:-1px;padding-bottom:2px}.dmx-channel-occupied{background-color:#63462e;border-left:none;border-right:none;cursor:move}.dmx-channel-occupied-start{border-left:1px solid white;border-top-left-radius:8px;border-bottom-left-radius:8px}.dmx-channel-occupied-end{border-right:1px solid white;border-top-right-radius:8px;border-bottom-right-radius:8px}.dmx-channel-selected{background-color:#fd7e14}.dmx-channel-overlapped{background-color:red;border:1px solid red;cursor:move}.card{overflow:auto}\n"] }]
5852
+ args: [{ selector: 'lib-app-fixture-pool', standalone: false, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\">{{ 'designer.fixture-pool.title' | translate }}</h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"cancel(); (false)\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <div class=\"row\">\n <div class=\"col-6\">\n <!-- Search for profiles -->\n <input\n type=\"text\"\n class=\"form-control input-block mb-3\"\n id=\"searchSet\"\n placeholder=\"{{ 'designer.fixture-pool.search-profiles' | translate }}\"\n (input)=\"searchExpression = $event.target.value; filterProfiles()\"\n />\n\n <!-- Profiles -->\n <div class=\"card border-secondary w-100\" style=\"height: 200px\">\n <div class=\"card-body p-0 w-100\">\n <div class=\"list-group list-group-flush w-100\">\n @if (loadingProfiles) {\n <a class=\"list-group-item flex-column align-items-start d-flex\">\n <p class=\"mb-0 mx-auto\">\n <i class=\"fa fa-spinner fa-pulse fa-fw\"></i>\n </p>\n </a>\n } @for (profile of filteredProfiles | sort : 'uuid'; track profile) {\n <div class=\"list-group-item\">\n <div class=\"row\">\n <div class=\"col-10 col-auto my-auto pl-2\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(profile) }} mr-1\"></span> {{ profile.manufacturerName }} -\n {{ profile.name }}\n </p>\n </div>\n <div class=\"col-2 my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"addFixture(profile); (false)\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div>\n <p class=\"d-inline-block mb-0 mt-2\">\n Powered by the <a href=\"https://open-fixture-library.org/\" target=\"_blank\">Open Fixture Library</a>\n </p>\n <a\n class=\"mt-2 btn btn-secondary btn-sm float-right\"\n href=\"https://open-fixture-library.org/fixture-editor\"\n target=\"_blank\"\n role=\"button\"\n >\n <i class=\"fa fa-file-o\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.add-profile' | translate }}\n </a>\n @if (configService.localProfiles) {\n <button\n type=\"button\"\n class=\"mr-2 mt-2 btn btn-secondary btn-sm float-right\"\n (click)=\"updateProfiles(); (false)\"\n [disabled]=\"updatingProfiles\"\n role=\"button\"\n >\n @if (updatingProfiles) {\n <i class=\"fa fa-spinner fa-pulse fa-fw\" aria-hidden=\"true\"></i>\n } @if (!updatingProfiles) {\n <i class=\"fa fa-download fa-fw\" aria-hidden=\"true\"></i>\n }\n {{ 'designer.fixture-pool.update-profiles' | translate }}\n </button>\n }\n </div>\n </div>\n <div class=\"col-6\">\n <div class=\"row\">\n <!-- Add fixture from local profile -->\n <div class=\"col-6\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"createFixtureFromProfileFile()\" role=\"button\">\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.create-fixture-from-profile-file' | translate }}\n </button>\n </div>\n\n <!-- Universe -->\n <div class=\"col-6\">\n <div class=\"form-group row\">\n <label class=\"col-lg-4 col-form-label\">{{ 'designer.fixture-pool.universe' | translate }}</label>\n <div class=\"col-lg-8\">\n <div class=\"input-group\">\n <select\n class=\"custom-select\"\n [disabled]=\"universes.length === 0\"\n [ngModel]=\"selectedUniverse\"\n (ngModelChange)=\"selectUniverse($event)\"\n >\n @for (universe of universes; track universe.uuid) {\n <option [ngValue]=\"universe\">{{ universe.name }}</option>\n }\n </select>\n @if (freeUniverseEdit) {\n <div class=\"input-group-append\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"openEditUniverses()\">\n <i class=\"fa fa-pencil\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Fixtures -->\n <div class=\"card border-secondary\" style=\"height: 235px\">\n <div class=\"card-body p-0\">\n <div class=\"list-group list-group-flush\" [sortablejs]=\"fixturePool\">\n @for (fixture of currentUniverseFixtures; track fixture) {\n <div\n class=\"list-group-item\"\n [class.active]=\"fixture == selectedFixture\"\n (click)=\"selectFixture(fixture)\"\n style=\"cursor: pointer\"\n >\n <div class=\"row\">\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=\"col-auto flex-grow pl-0 my-auto\">\n <p class=\"mb-0\">\n <span class=\"icon-{{ fixtureService.getFixtureIconClass(fixtureService.getProfileByUuid(fixture.profileUuid)) }} mr-1\">\n </span\n >{{ fixture.name }}\n </p>\n </div>\n <div class=\"col-auto my-auto\">\n <a class=\"btn btn-primary btn-sm float-right\" href=\"#\" role=\"button\" (click)=\"removeFixture(fixture); (false)\">\n <i class=\"fa fa-minus\" aria-hidden=\"true\"></i>\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row mt-3\">\n <div class=\"col\">\n <!-- Tab navigation -->\n <ul class=\"nav nav-tabs mb-4\" id=\"myTab\" role=\"tablist\">\n <li class=\"nav-item\">\n <a class=\"nav-link active\" id=\"sets-tab\" data-toggle=\"tab\" href=\"#dmx\" role=\"tab\">\n <i class=\"icon-ola\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.dmx' | translate }}\n </a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link\" id=\"compositions-tab\" data-toggle=\"tab\" href=\"#settings\" role=\"tab\">\n <i class=\"fa fa-cog\" aria-hidden=\"true\"></i> {{ 'designer.fixture-pool.settings' | translate }}\n </a>\n </li>\n </ul>\n\n <!-- Tab content -->\n <div class=\"tab-content\" id=\"myTabContent\">\n <!-- DMX universe overview-->\n <div class=\"tab-pane show active\" id=\"dmx\" role=\"tabpanel\">\n <!-- Show all channel numbers -->\n <!-- <div class=\"form-group row\">\n <div class=\"col-auto flex-grow\"></div>\n <div class=\"col-lg-3 d-flex\">\n <div class=\"form-check my-auto ml-auto\">\n <input type=\"checkbox\" class=\"form-check-input\" id=\"showChannelNumbers\" [ngModel]=\"showChannelNumbers\"\n (ngModelChange)=\"showChannelNumbers = $event\">\n <label class=\"form-check-label\" for=\"showChannelNumbers\">Show channels</label>\n </div>\n </div>\n </div> -->\n\n <!-- List of DMX channels -->\n <div>\n @for (channel of dmxChannels; track $index; let i = $index) {\n <div\n class=\"dmx-channel\"\n [class.dmx-channel-occupied]=\"channelOccupied(i)\"\n [class.dmx-channel-occupied-start]=\"channelOccupiedStart(i)\"\n [class.dmx-channel-occupied-end]=\"channelOccupiedEnd(i)\"\n [class.dmx-channel-selected]=\"channelSelected(i)\"\n [class.dmx-channel-overlapped]=\"channelOverlapped(i)\"\n (mousedown)=\"channelMouseDown($event)\"\n (mouseover)=\"channelMouseOver($event)\"\n [attr.data-index]=\"i\"\n popover=\"{{ i + 1 }}\"\n placement=\"top\"\n triggers=\"mouseenter:mouseleave\"\n >\n <div class=\"d-flex w-100 dmx-channel-text\" [attr.data-index]=\"i\" [class.dmx-channel-text-visible]=\"showChannelNumbers\">\n <div class=\"m-auto\" style=\"font-size: 11px\" [attr.data-index]=\"i\"><small [attr.data-index]=\"i\">&nbsp;</small></div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Fixture settings -->\n <div class=\"tab-pane\" id=\"settings\" role=\"tabpanel\">\n @if (selectedFixture && selectedFixtureProfile) {\n <div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\" for=\"fixtureName\">{{ 'designer.fixture-pool.fixture-name' | translate }}</label>\n <div class=\"col-lg-9\">\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"selectedFixture.name\" maxlength=\"200\" id=\"fixtureName\" />\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.mode' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.modeShortName\"\n (ngModelChange)=\"selectedFixture.modeShortName = $event\"\n >\n @for (mode of selectedFixtureProfile.modes; track mode) {\n <option [ngValue]=\"mode.shortName || mode.name\">\n {{ mode.name }}\n - {{ fixtureService.getModeChannelCount(selectedFixtureProfile, mode) }}\n {{ 'designer.fixture-pool.channels' | translate }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\">{{ 'designer.fixture-pool.first-channel' | translate }}</label>\n <div class=\"col-lg-9\">\n <select\n class=\"custom-select\"\n [ngModel]=\"selectedFixture.dmxFirstChannel\"\n (ngModelChange)=\"selectedFixture.dmxFirstChannel = $event\"\n >\n @for (channel of dmxChannels; track $index; let i = $index) {\n <option [ngValue]=\"i\">\n {{ i + 1 }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group row\">\n <label class=\"col-lg-3 col-form-label\"></label>\n <div class=\"col-lg-9\">\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"addCopy(selectedFixture)\">\n {{ 'designer.fixture.add-copy' | translate }}\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n</div>\n<div class=\"modal-footer\">\n <a class=\"mr-3 my-auto\" href=\"#\" role=\"button\" (click)=\"cancel(); (false)\">\n {{ 'designer.misc.cancel' | translate }}\n </a>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"ok()\">{{ 'designer.misc.ok' | translate }}</button>\n</div>\n", styles: [".list-group-item{padding:.3rem 1.25rem}.dmx-channel{display:flex;float:left;width:28px;border:1px solid white;margin-left:-1px;margin-bottom:-1px;padding-bottom:2px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.dmx-channel-occupied{background-color:#63462e;border-left:none;border-right:none;cursor:move}.dmx-channel-occupied-start{border-left:1px solid white;border-top-left-radius:8px;border-bottom-left-radius:8px}.dmx-channel-occupied-end{border-right:1px solid white;border-top-right-radius:8px;border-bottom-right-radius:8px}.dmx-channel-selected{background-color:#fd7e14}.dmx-channel-overlapped{background-color:red;border:1px solid red;cursor:move}.card{overflow:auto}\n"] }]
5725
5853
  }], ctorParameters: () => [{ type: i1$1.BsModalRef }, { type: FixtureService }, { type: UuidService }, { type: ProjectService }, { type: PreviewService }, { type: i2.TranslateService }, { type: i3$1.ToastrService }, { type: PresetService }, { type: ConfigService }, { type: i1$1.BsModalService }, { type: LivePreviewService }], propDecorators: { mouseUp: [{
5726
5854
  type: HostListener,
5727
5855
  args: ['window:mouseup', ['$event']]
@@ -8307,6 +8435,12 @@ class DesignerComponent {
8307
8435
  set dropzoneChunking(value) {
8308
8436
  this.configService.dropzoneChunking = value;
8309
8437
  }
8438
+ set universes(value) {
8439
+ this.configService.universes = value || [];
8440
+ }
8441
+ set freeUniverseEdit(value) {
8442
+ this.configService.freeUniverseEdit = value;
8443
+ }
8310
8444
  constructor(translateService, projectService, configService, fixturePoolService, hotkeyTargetExcludeService, modalService, userService, userEnsureLoginService, projectLoadService, warningDialogService, errorDialogService, fixtureService, timelineService, introService, effectService) {
8311
8445
  this.translateService = translateService;
8312
8446
  this.projectService = projectService;
@@ -8546,7 +8680,7 @@ class DesignerComponent {
8546
8680
  localStorage.setItem('language', language);
8547
8681
  }
8548
8682
  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 }); }
8549
- 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" }, 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 }); }
8683
+ 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 }); }
8550
8684
  }
8551
8685
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DesignerComponent, decorators: [{
8552
8686
  type: Component,
@@ -8575,6 +8709,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
8575
8709
  type: Input
8576
8710
  }], dropzoneChunking: [{
8577
8711
  type: Input
8712
+ }], universes: [{
8713
+ type: Input
8714
+ }], freeUniverseEdit: [{
8715
+ type: Input
8578
8716
  }], previewComponent: [{
8579
8717
  type: ViewChild,
8580
8718
  args: [PreviewComponent]
@@ -8625,6 +8763,7 @@ class DesignerModule {
8625
8763
  SceneSettingsComponent,
8626
8764
  ProjectSaveComponent,
8627
8765
  FixturePoolCreateFromFileComponent,
8766
+ FixturePoolEditUniversesComponent,
8628
8767
  DropzoneComponent,
8629
8768
  SortablejsDirective], imports: [i3.RouterModule, BrowserModule,
8630
8769
  BrowserAnimationsModule,
@@ -8691,6 +8830,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
8691
8830
  SceneSettingsComponent,
8692
8831
  ProjectSaveComponent,
8693
8832
  FixturePoolCreateFromFileComponent,
8833
+ FixturePoolEditUniversesComponent,
8694
8834
  DropzoneComponent,
8695
8835
  SortablejsDirective,
8696
8836
  ],