microboard-temp 0.2.5 → 0.3.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.
package/dist/esm/index.js CHANGED
@@ -33395,6 +33395,7 @@ class BaseItem extends Mbr {
33395
33395
  transformationRenderBlock = undefined;
33396
33396
  board;
33397
33397
  id;
33398
+ onRemoveCallbacks = [];
33398
33399
  shouldUseCustomRender = false;
33399
33400
  shouldRenderOutsideViewRect = true;
33400
33401
  itemType = "";
@@ -33472,6 +33473,12 @@ class BaseItem extends Mbr {
33472
33473
  break;
33473
33474
  }
33474
33475
  }
33476
+ addOnRemoveCallback(cb) {
33477
+ this.onRemoveCallbacks.push(cb);
33478
+ }
33479
+ onRemove() {
33480
+ this.onRemoveCallbacks.forEach((cb) => cb());
33481
+ }
33475
33482
  render(context) {}
33476
33483
  renderHTML(documentFactory) {
33477
33484
  return documentFactory.createElement("div");
@@ -38728,6 +38735,9 @@ class VideoItem extends BaseItem {
38728
38735
  this.loadCallbacks.push(callback);
38729
38736
  };
38730
38737
  getStorageId() {
38738
+ if (!this.isStorageUrl) {
38739
+ return;
38740
+ }
38731
38741
  return this.url.split("/").pop();
38732
38742
  }
38733
38743
  getIsStorageUrl() {
@@ -39000,6 +39010,13 @@ class VideoItem extends BaseItem {
39000
39010
  linkElem.click();
39001
39011
  }
39002
39012
  }
39013
+ onRemove() {
39014
+ const storageId = this.getStorageId();
39015
+ if (storageId) {
39016
+ deleteMedia([storageId], this.board.getBoardId());
39017
+ }
39018
+ super.onRemove();
39019
+ }
39003
39020
  }
39004
39021
  // src/Items/Image/calculatePosition.ts
39005
39022
  function calculatePosition(boardImage, board) {
@@ -39430,432 +39447,122 @@ var captureFrame = (frameTime, video) => {
39430
39447
  return null;
39431
39448
  }
39432
39449
  };
39433
- // src/Items/Audio/Audio.ts
39434
- class AudioItem extends BaseItem {
39450
+ // src/Items/Placeholder/Placeholder.ts
39451
+ var PlaceholderImg = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
39452
+ <path d="M5 11.1L7 9.1L12.5 14.6L16 11.1L19 14.1V5H5V11.1ZM4 3H20C20.2652 3 20.5196 3.10536 20.7071 3.29289C20.8946 3.48043 21 3.73478 21 4V20C21 20.2652 20.8946 20.5196 20.7071 20.7071C20.5196 20.8946 20.2652 21 20 21H4C3.73478 21 3.48043 20.8946 3.29289 20.7071C3.10536 20.5196 3 20.2652 3 20V4C3 3.73478 3.10536 3.48043 3.29289 3.29289C3.48043 3.10536 3.73478 3 4 3ZM15.5 10C15.1022 10 14.7206 9.84196 14.4393 9.56066C14.158 9.27936 14 8.89782 14 8.5C14 8.10218 14.158 7.72064 14.4393 7.43934C14.7206 7.15804 15.1022 7 15.5 7C15.8978 7 16.2794 7.15804 16.5607 7.43934C16.842 7.72064 17 8.10218 17 8.5C17 8.89782 16.842 9.27936 16.5607 9.56066C16.2794 9.84196 15.8978 10 15.5 10Z" fill="white" fill-opacity="0.6"/>
39453
+ </svg>`;
39454
+
39455
+ class Placeholder extends BaseItem {
39435
39456
  events;
39436
- extension;
39437
- itemType = "Audio";
39457
+ miroData;
39458
+ backgroundColor;
39459
+ icon;
39460
+ itemType = "Placeholder";
39461
+ shapeType = "Rectangle";
39438
39462
  parent = "Board";
39439
39463
  transformation;
39440
- linkTo;
39464
+ path = Shapes[this.shapeType].path.copy();
39465
+ mbr = Shapes[this.shapeType].path.getMbr().copy();
39441
39466
  subject = new Subject;
39442
- loadCallbacks = [];
39443
- beforeLoadCallbacks = [];
39444
39467
  transformationRenderBlock = undefined;
39445
- url = "";
39446
- isPlaying = false;
39447
- currentTime = 0;
39448
- isStorageUrl = true;
39449
- constructor(board, isStorageUrl, url, events, id = "", extension2) {
39468
+ iconImage;
39469
+ constructor(board, events, miroData, id = "", backgroundColor = "#E5E5EA", icon = PlaceholderImg?.toString() || "") {
39450
39470
  super(board, id);
39451
39471
  this.events = events;
39452
- this.extension = extension2;
39453
- this.linkTo = new LinkTo(this.id, events);
39454
- this.board = board;
39455
- this.isStorageUrl = isStorageUrl;
39456
- if (url) {
39457
- this.applyUrl(url);
39458
- }
39459
- this.transformation = new Transformation(id, events);
39460
- this.linkTo.subject.subscribe(() => {
39472
+ this.miroData = miroData;
39473
+ this.backgroundColor = backgroundColor;
39474
+ this.icon = icon;
39475
+ this.transformation = new Transformation(this.id, this.events);
39476
+ this.transformation.subject.subscribe((_subject) => {
39477
+ this.transformPath();
39461
39478
  this.updateMbr();
39462
39479
  this.subject.publish(this);
39463
39480
  });
39464
- this.transformation.subject.subscribe(this.onTransform);
39465
- this.right = this.left + conf.AUDIO_DIMENSIONS.width;
39466
- this.bottom = this.top + conf.AUDIO_DIMENSIONS.height;
39467
- this.shouldUseCustomRender = true;
39468
- }
39469
- setCurrentTime(time) {
39470
- this.currentTime = time;
39471
- }
39472
- getCurrentTime() {
39473
- return this.currentTime;
39474
- }
39475
- getIsStorageUrl() {
39476
- return this.isStorageUrl;
39477
- }
39478
- onTransform = () => {
39479
39481
  this.updateMbr();
39480
- this.subject.publish(this);
39481
- };
39482
- doOnceBeforeOnLoad = (callback) => {
39483
- this.loadCallbacks.push(callback);
39484
- };
39485
- doOnceOnLoad = (callback) => {
39486
- this.loadCallbacks.push(callback);
39487
- };
39488
- setIsPlaying(isPlaying) {
39489
- this.isPlaying = isPlaying;
39490
- this.shouldRenderOutsideViewRect = isPlaying;
39491
- this.subject.publish(this);
39492
- }
39493
- getIsPlaying() {
39494
- return this.isPlaying;
39482
+ this.loadIconImage();
39495
39483
  }
39496
- applyUrl(url) {
39497
- if (this.isStorageUrl) {
39498
- try {
39499
- const newUrl = new URL(url);
39500
- this.url = `${window.location.origin}${newUrl.pathname}`;
39501
- } catch (_) {}
39484
+ emit(operation) {
39485
+ if (this.events) {
39486
+ const command = new PlaceholderCommand([this], operation);
39487
+ command.apply();
39488
+ this.events.emit(operation, command);
39502
39489
  } else {
39503
- this.url = url;
39504
- }
39505
- }
39506
- setUrl(url) {
39507
- this.emit({
39508
- class: "Audio",
39509
- method: "setUrl",
39510
- item: [this.getId()],
39511
- url
39512
- });
39513
- }
39514
- getStorageId() {
39515
- return this.url.split("/").pop();
39516
- }
39517
- getUrl() {
39518
- return this.url;
39519
- }
39520
- onLoad = async () => {
39521
- this.shootBeforeLoadCallbacks();
39522
- this.updateMbr();
39523
- this.subject.publish(this);
39524
- this.shootLoadCallbacks();
39525
- };
39526
- onError = (_error) => {
39527
- this.updateMbr();
39528
- this.subject.publish(this);
39529
- this.shootLoadCallbacks();
39530
- };
39531
- updateMbr() {
39532
- const { translateX, translateY, scaleX, scaleY } = this.transformation.matrix;
39533
- this.left = translateX;
39534
- this.top = translateY;
39535
- this.right = this.left + conf.AUDIO_DIMENSIONS.width * scaleX;
39536
- this.bottom = this.top + conf.AUDIO_DIMENSIONS.height * scaleY;
39537
- }
39538
- render(context) {
39539
- if (this.transformationRenderBlock) {
39540
- return;
39541
- }
39542
- const ctx = context.ctx;
39543
- const radius = 12 * this.transformation.getScale().x;
39544
- ctx.save();
39545
- ctx.globalCompositeOperation = "destination-out";
39546
- ctx.beginPath();
39547
- ctx.moveTo(this.left + radius, this.top);
39548
- ctx.lineTo(this.left + this.getWidth() - radius, this.top);
39549
- ctx.quadraticCurveTo(this.left + this.getWidth(), this.top, this.left + this.getWidth(), this.top + radius);
39550
- ctx.lineTo(this.left + this.getWidth(), this.top + this.getHeight() - radius);
39551
- ctx.quadraticCurveTo(this.left + this.getWidth(), this.top + this.getHeight(), this.left + this.getWidth() - radius, this.top + this.getHeight());
39552
- ctx.lineTo(this.left + radius, this.top + this.getHeight());
39553
- ctx.quadraticCurveTo(this.left, this.top + this.getHeight(), this.left, this.top + this.getHeight() - radius);
39554
- ctx.lineTo(this.left, this.top + radius);
39555
- ctx.quadraticCurveTo(this.left, this.top, this.left + radius, this.top);
39556
- ctx.closePath();
39557
- ctx.fill();
39558
- ctx.restore();
39559
- }
39560
- renderHTML(documentFactory) {
39561
- const div = documentFactory.createElement("audio-item");
39562
- const { translateX, translateY, scaleX, scaleY } = this.transformation.matrix;
39563
- const transform = `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`;
39564
- div.id = this.getId();
39565
- div.style.width = `${conf.AUDIO_DIMENSIONS.width}px`;
39566
- div.style.height = `${conf.AUDIO_DIMENSIONS.height}px`;
39567
- div.style.transformOrigin = "top left";
39568
- div.style.transform = transform;
39569
- div.style.position = "absolute";
39570
- div.setAttribute("audio-url", this.getUrl());
39571
- if (this.extension) {
39572
- div.setAttribute("extension", this.extension);
39573
- }
39574
- if (this.isStorageUrl) {
39575
- div.setAttribute("is-storage-url", "true");
39490
+ this.apply(operation);
39576
39491
  }
39577
- div.setAttribute("data-link-to", "");
39578
- return div;
39579
39492
  }
39580
39493
  serialize() {
39581
39494
  return {
39582
- itemType: "Audio",
39583
- url: this.url,
39495
+ itemType: "Placeholder",
39496
+ backgroundColor: this.backgroundColor,
39497
+ icon: this.icon,
39584
39498
  transformation: this.transformation.serialize(),
39585
- isStorageUrl: this.isStorageUrl,
39586
- extension: this.extension
39499
+ miroData: this.miroData
39587
39500
  };
39588
39501
  }
39589
39502
  deserialize(data) {
39590
- if (data.isStorageUrl) {
39591
- this.isStorageUrl = data.isStorageUrl;
39592
- }
39503
+ this.initPath();
39504
+ this.backgroundColor = data.backgroundColor ?? this.backgroundColor;
39505
+ this.icon = data.icon ?? this.icon;
39506
+ this.miroData = data.miroData;
39593
39507
  if (data.transformation) {
39594
39508
  this.transformation.deserialize(data.transformation);
39509
+ this.transformPath();
39595
39510
  }
39596
- if (data.url) {
39597
- this.setUrl(data.url);
39598
- }
39599
- if (data.extension) {
39600
- this.extension = data.extension;
39601
- }
39511
+ this.subject.publish(this);
39512
+ return this;
39513
+ }
39514
+ setId(id) {
39515
+ this.id = id;
39516
+ this.transformation.setId(id);
39602
39517
  return this;
39603
39518
  }
39519
+ getId() {
39520
+ return this.id;
39521
+ }
39604
39522
  apply(op) {
39605
39523
  switch (op.class) {
39524
+ case "Placeholder":
39525
+ this.applyPlaceholder(op);
39526
+ this.updateMbr();
39527
+ break;
39606
39528
  case "Transformation":
39607
39529
  this.transformation.apply(op);
39608
39530
  break;
39609
- case "LinkTo":
39610
- this.linkTo.apply(op);
39531
+ default:
39532
+ return;
39533
+ }
39534
+ this.subject.publish(this);
39535
+ }
39536
+ applyPlaceholder(op) {
39537
+ switch (op.method) {
39538
+ case "setBackgroundColor":
39539
+ this.applyBackgroundColor(op.backgroundColor);
39611
39540
  break;
39612
- case "Audio":
39613
- if (op.method === "setUrl") {
39614
- this.applyUrl(op.url);
39615
- }
39616
- this.subject.publish(this);
39541
+ case "setIcon":
39542
+ this.applyIcon(op.icon);
39543
+ break;
39544
+ case "setMiroData":
39545
+ this.applyMiroData(op.miroData);
39617
39546
  break;
39618
39547
  }
39619
39548
  }
39620
- emit(operation) {
39621
- if (this.events) {
39622
- const command = new AudioCommand([this], operation);
39623
- command.apply();
39624
- this.events.emit(operation, command);
39625
- } else {
39626
- this.apply(operation);
39627
- }
39549
+ getBackgroundColor() {
39550
+ return this.backgroundColor;
39628
39551
  }
39629
- setId(id) {
39630
- this.id = id;
39631
- this.transformation.setId(id);
39632
- return this;
39552
+ applyBackgroundColor(backgroundColor) {
39553
+ this.backgroundColor = backgroundColor;
39554
+ this.path.setBackgroundColor(backgroundColor);
39633
39555
  }
39634
- getId() {
39635
- return this.id;
39556
+ setBackgroundColor(backgroundColor) {
39557
+ this.emit({
39558
+ class: "Placeholder",
39559
+ method: "setBackgroundColor",
39560
+ item: [this.getId()],
39561
+ backgroundColor
39562
+ });
39636
39563
  }
39637
- shootLoadCallbacks() {
39638
- while (this.loadCallbacks.length > 0) {
39639
- this.loadCallbacks.shift()(this);
39640
- }
39641
- }
39642
- shootBeforeLoadCallbacks() {
39643
- while (this.beforeLoadCallbacks.length > 0) {
39644
- this.beforeLoadCallbacks.shift()(this);
39645
- }
39646
- }
39647
- getPath() {
39648
- const { left, top, right, bottom } = this.getMbr();
39649
- const leftTop = new Point(left, top);
39650
- const rightTop = new Point(right, top);
39651
- const rightBottom = new Point(right, bottom);
39652
- const leftBottom = new Point(left, bottom);
39653
- return new Path([
39654
- new Line(leftTop, rightTop),
39655
- new Line(rightTop, rightBottom),
39656
- new Line(rightBottom, leftBottom),
39657
- new Line(leftBottom, leftTop)
39658
- ], true);
39659
- }
39660
- getSnapAnchorPoints() {
39661
- const mbr = this.getMbr();
39662
- const width2 = mbr.getWidth();
39663
- const height2 = mbr.getHeight();
39664
- return [
39665
- new Point(mbr.left + width2 / 2, mbr.top),
39666
- new Point(mbr.left + width2 / 2, mbr.bottom),
39667
- new Point(mbr.left, mbr.top + height2 / 2),
39668
- new Point(mbr.right, mbr.top + height2 / 2)
39669
- ];
39670
- }
39671
- isClosed() {
39672
- return true;
39673
- }
39674
- getRichText() {
39675
- return null;
39676
- }
39677
- getLinkTo() {
39678
- return;
39679
- }
39680
- getExtension() {
39681
- return this.extension;
39682
- }
39683
- download() {
39684
- if (this.extension) {
39685
- const linkElem = conf.documentFactory.createElement("a");
39686
- linkElem.href = this.url;
39687
- linkElem.setAttribute("download", `${this.board.getBoardId()}.${this.extension}`);
39688
- linkElem.click();
39689
- }
39690
- }
39691
- }
39692
- // src/Items/Audio/AudioHelpers.ts
39693
- var uploadAudioToStorage = async (hash, audioBlob, accessToken, boardId) => {
39694
- return new Promise((resolve2, reject) => {
39695
- fetch(`${window.location.origin}/api/v1/media/audio/${boardId}`, {
39696
- method: "POST",
39697
- headers: {
39698
- "Content-Type": audioBlob.type,
39699
- "x-audio-id": hash,
39700
- Authorization: `Bearer ${accessToken}`
39701
- },
39702
- body: audioBlob
39703
- }).then(async (response) => {
39704
- if (response.status !== 200) {
39705
- return catchErrorResponse(response, "audio");
39706
- }
39707
- return response.json();
39708
- }).then((data) => {
39709
- console.log(data);
39710
- resolve2(data.src);
39711
- }).catch((error) => {
39712
- console.error("Media storage error:", error);
39713
- reject(error);
39714
- });
39715
- });
39716
- };
39717
- var prepareAudio = (file, accessToken, boardId) => {
39718
- return new Promise((resolve2, reject) => {
39719
- const audio = document.createElement("audio");
39720
- audio.src = URL.createObjectURL(file);
39721
- audio.onloadedmetadata = () => {
39722
- fileTosha256(file).then((hash) => {
39723
- uploadAudioToStorage(hash, file, accessToken, boardId).then((url) => {
39724
- resolve2(url);
39725
- }).catch(reject);
39726
- }).catch(() => {
39727
- reject(new Error("Failed to generate hash"));
39728
- });
39729
- };
39730
- audio.onerror = () => {
39731
- reject(new Error("Failed to load audio"));
39732
- };
39733
- });
39734
- };
39735
- var calculateAudioPosition = (board, audioItem) => {
39736
- const cameraMbr = board.camera.getMbr();
39737
- const cameraWidth = cameraMbr.getWidth();
39738
- const translateX = cameraMbr.left + cameraWidth * 0.34;
39739
- const translateY = cameraMbr.getCenter().y - audioItem.getHeight() / 2;
39740
- const scale = cameraWidth * 0.32 / audioItem.getWidth();
39741
- return new Matrix2(translateX, translateY, scale, scale);
39742
- };
39743
- // src/Items/Placeholder/Placeholder.ts
39744
- var PlaceholderImg = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
39745
- <path d="M5 11.1L7 9.1L12.5 14.6L16 11.1L19 14.1V5H5V11.1ZM4 3H20C20.2652 3 20.5196 3.10536 20.7071 3.29289C20.8946 3.48043 21 3.73478 21 4V20C21 20.2652 20.8946 20.5196 20.7071 20.7071C20.5196 20.8946 20.2652 21 20 21H4C3.73478 21 3.48043 20.8946 3.29289 20.7071C3.10536 20.5196 3 20.2652 3 20V4C3 3.73478 3.10536 3.48043 3.29289 3.29289C3.48043 3.10536 3.73478 3 4 3ZM15.5 10C15.1022 10 14.7206 9.84196 14.4393 9.56066C14.158 9.27936 14 8.89782 14 8.5C14 8.10218 14.158 7.72064 14.4393 7.43934C14.7206 7.15804 15.1022 7 15.5 7C15.8978 7 16.2794 7.15804 16.5607 7.43934C16.842 7.72064 17 8.10218 17 8.5C17 8.89782 16.842 9.27936 16.5607 9.56066C16.2794 9.84196 15.8978 10 15.5 10Z" fill="white" fill-opacity="0.6"/>
39746
- </svg>`;
39747
-
39748
- class Placeholder extends BaseItem {
39749
- events;
39750
- miroData;
39751
- backgroundColor;
39752
- icon;
39753
- itemType = "Placeholder";
39754
- shapeType = "Rectangle";
39755
- parent = "Board";
39756
- transformation;
39757
- path = Shapes[this.shapeType].path.copy();
39758
- mbr = Shapes[this.shapeType].path.getMbr().copy();
39759
- subject = new Subject;
39760
- transformationRenderBlock = undefined;
39761
- iconImage;
39762
- constructor(board, events, miroData, id = "", backgroundColor = "#E5E5EA", icon = PlaceholderImg?.toString() || "") {
39763
- super(board, id);
39764
- this.events = events;
39765
- this.miroData = miroData;
39766
- this.backgroundColor = backgroundColor;
39767
- this.icon = icon;
39768
- this.transformation = new Transformation(this.id, this.events);
39769
- this.transformation.subject.subscribe((_subject) => {
39770
- this.transformPath();
39771
- this.updateMbr();
39772
- this.subject.publish(this);
39773
- });
39774
- this.updateMbr();
39775
- this.loadIconImage();
39776
- }
39777
- emit(operation) {
39778
- if (this.events) {
39779
- const command = new PlaceholderCommand([this], operation);
39780
- command.apply();
39781
- this.events.emit(operation, command);
39782
- } else {
39783
- this.apply(operation);
39784
- }
39785
- }
39786
- serialize() {
39787
- return {
39788
- itemType: "Placeholder",
39789
- backgroundColor: this.backgroundColor,
39790
- icon: this.icon,
39791
- transformation: this.transformation.serialize(),
39792
- miroData: this.miroData
39793
- };
39794
- }
39795
- deserialize(data) {
39796
- this.initPath();
39797
- this.backgroundColor = data.backgroundColor ?? this.backgroundColor;
39798
- this.icon = data.icon ?? this.icon;
39799
- this.miroData = data.miroData;
39800
- if (data.transformation) {
39801
- this.transformation.deserialize(data.transformation);
39802
- this.transformPath();
39803
- }
39804
- this.subject.publish(this);
39805
- return this;
39806
- }
39807
- setId(id) {
39808
- this.id = id;
39809
- this.transformation.setId(id);
39810
- return this;
39811
- }
39812
- getId() {
39813
- return this.id;
39814
- }
39815
- apply(op) {
39816
- switch (op.class) {
39817
- case "Placeholder":
39818
- this.applyPlaceholder(op);
39819
- this.updateMbr();
39820
- break;
39821
- case "Transformation":
39822
- this.transformation.apply(op);
39823
- break;
39824
- default:
39825
- return;
39826
- }
39827
- this.subject.publish(this);
39828
- }
39829
- applyPlaceholder(op) {
39830
- switch (op.method) {
39831
- case "setBackgroundColor":
39832
- this.applyBackgroundColor(op.backgroundColor);
39833
- break;
39834
- case "setIcon":
39835
- this.applyIcon(op.icon);
39836
- break;
39837
- case "setMiroData":
39838
- this.applyMiroData(op.miroData);
39839
- break;
39840
- }
39841
- }
39842
- getBackgroundColor() {
39843
- return this.backgroundColor;
39844
- }
39845
- applyBackgroundColor(backgroundColor) {
39846
- this.backgroundColor = backgroundColor;
39847
- this.path.setBackgroundColor(backgroundColor);
39848
- }
39849
- setBackgroundColor(backgroundColor) {
39850
- this.emit({
39851
- class: "Placeholder",
39852
- method: "setBackgroundColor",
39853
- item: [this.getId()],
39854
- backgroundColor
39855
- });
39856
- }
39857
- getIcon() {
39858
- return this.icon;
39564
+ getIcon() {
39565
+ return this.icon;
39859
39566
  }
39860
39567
  applyIcon(icon) {
39861
39568
  this.icon = icon;
@@ -40285,7 +39992,334 @@ class ImageItem extends BaseItem {
40285
39992
  linkElem.setAttribute("download", "");
40286
39993
  linkElem.click();
40287
39994
  }
39995
+ onRemove() {
39996
+ const storageId = this.getStorageId();
39997
+ if (storageId) {
39998
+ deleteMedia([storageId], this.board.getBoardId());
39999
+ }
40000
+ super.onRemove();
40001
+ }
40288
40002
  }
40003
+ // src/Items/Audio/Audio.ts
40004
+ class AudioItem extends BaseItem {
40005
+ events;
40006
+ extension;
40007
+ itemType = "Audio";
40008
+ parent = "Board";
40009
+ transformation;
40010
+ linkTo;
40011
+ subject = new Subject;
40012
+ loadCallbacks = [];
40013
+ beforeLoadCallbacks = [];
40014
+ transformationRenderBlock = undefined;
40015
+ url = "";
40016
+ isPlaying = false;
40017
+ currentTime = 0;
40018
+ isStorageUrl = true;
40019
+ constructor(board, isStorageUrl, url, events, id = "", extension2) {
40020
+ super(board, id);
40021
+ this.events = events;
40022
+ this.extension = extension2;
40023
+ this.linkTo = new LinkTo(this.id, events);
40024
+ this.board = board;
40025
+ this.isStorageUrl = isStorageUrl;
40026
+ if (url) {
40027
+ this.applyUrl(url);
40028
+ }
40029
+ this.transformation = new Transformation(id, events);
40030
+ this.linkTo.subject.subscribe(() => {
40031
+ this.updateMbr();
40032
+ this.subject.publish(this);
40033
+ });
40034
+ this.transformation.subject.subscribe(this.onTransform);
40035
+ this.right = this.left + conf.AUDIO_DIMENSIONS.width;
40036
+ this.bottom = this.top + conf.AUDIO_DIMENSIONS.height;
40037
+ this.shouldUseCustomRender = true;
40038
+ }
40039
+ setCurrentTime(time) {
40040
+ this.currentTime = time;
40041
+ }
40042
+ getCurrentTime() {
40043
+ return this.currentTime;
40044
+ }
40045
+ getIsStorageUrl() {
40046
+ return this.isStorageUrl;
40047
+ }
40048
+ onTransform = () => {
40049
+ this.updateMbr();
40050
+ this.subject.publish(this);
40051
+ };
40052
+ doOnceBeforeOnLoad = (callback) => {
40053
+ this.loadCallbacks.push(callback);
40054
+ };
40055
+ doOnceOnLoad = (callback) => {
40056
+ this.loadCallbacks.push(callback);
40057
+ };
40058
+ setIsPlaying(isPlaying) {
40059
+ this.isPlaying = isPlaying;
40060
+ this.shouldRenderOutsideViewRect = isPlaying;
40061
+ this.subject.publish(this);
40062
+ }
40063
+ getIsPlaying() {
40064
+ return this.isPlaying;
40065
+ }
40066
+ applyUrl(url) {
40067
+ if (this.isStorageUrl) {
40068
+ try {
40069
+ const newUrl = new URL(url);
40070
+ this.url = `${window.location.origin}${newUrl.pathname}`;
40071
+ } catch (_) {}
40072
+ } else {
40073
+ this.url = url;
40074
+ }
40075
+ }
40076
+ setUrl(url) {
40077
+ this.emit({
40078
+ class: "Audio",
40079
+ method: "setUrl",
40080
+ item: [this.getId()],
40081
+ url
40082
+ });
40083
+ }
40084
+ getStorageId() {
40085
+ if (!this.isStorageUrl) {
40086
+ return;
40087
+ }
40088
+ return this.url.split("/").pop();
40089
+ }
40090
+ getUrl() {
40091
+ return this.url;
40092
+ }
40093
+ onLoad = async () => {
40094
+ this.shootBeforeLoadCallbacks();
40095
+ this.updateMbr();
40096
+ this.subject.publish(this);
40097
+ this.shootLoadCallbacks();
40098
+ };
40099
+ onError = (_error) => {
40100
+ this.updateMbr();
40101
+ this.subject.publish(this);
40102
+ this.shootLoadCallbacks();
40103
+ };
40104
+ updateMbr() {
40105
+ const { translateX, translateY, scaleX, scaleY } = this.transformation.matrix;
40106
+ this.left = translateX;
40107
+ this.top = translateY;
40108
+ this.right = this.left + conf.AUDIO_DIMENSIONS.width * scaleX;
40109
+ this.bottom = this.top + conf.AUDIO_DIMENSIONS.height * scaleY;
40110
+ }
40111
+ render(context) {
40112
+ if (this.transformationRenderBlock) {
40113
+ return;
40114
+ }
40115
+ const ctx = context.ctx;
40116
+ const radius = 12 * this.transformation.getScale().x;
40117
+ ctx.save();
40118
+ ctx.globalCompositeOperation = "destination-out";
40119
+ ctx.beginPath();
40120
+ ctx.moveTo(this.left + radius, this.top);
40121
+ ctx.lineTo(this.left + this.getWidth() - radius, this.top);
40122
+ ctx.quadraticCurveTo(this.left + this.getWidth(), this.top, this.left + this.getWidth(), this.top + radius);
40123
+ ctx.lineTo(this.left + this.getWidth(), this.top + this.getHeight() - radius);
40124
+ ctx.quadraticCurveTo(this.left + this.getWidth(), this.top + this.getHeight(), this.left + this.getWidth() - radius, this.top + this.getHeight());
40125
+ ctx.lineTo(this.left + radius, this.top + this.getHeight());
40126
+ ctx.quadraticCurveTo(this.left, this.top + this.getHeight(), this.left, this.top + this.getHeight() - radius);
40127
+ ctx.lineTo(this.left, this.top + radius);
40128
+ ctx.quadraticCurveTo(this.left, this.top, this.left + radius, this.top);
40129
+ ctx.closePath();
40130
+ ctx.fill();
40131
+ ctx.restore();
40132
+ }
40133
+ renderHTML(documentFactory) {
40134
+ const div = documentFactory.createElement("audio-item");
40135
+ const { translateX, translateY, scaleX, scaleY } = this.transformation.matrix;
40136
+ const transform = `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`;
40137
+ div.id = this.getId();
40138
+ div.style.width = `${conf.AUDIO_DIMENSIONS.width}px`;
40139
+ div.style.height = `${conf.AUDIO_DIMENSIONS.height}px`;
40140
+ div.style.transformOrigin = "top left";
40141
+ div.style.transform = transform;
40142
+ div.style.position = "absolute";
40143
+ div.setAttribute("audio-url", this.getUrl());
40144
+ if (this.extension) {
40145
+ div.setAttribute("extension", this.extension);
40146
+ }
40147
+ if (this.isStorageUrl) {
40148
+ div.setAttribute("is-storage-url", "true");
40149
+ }
40150
+ div.setAttribute("data-link-to", "");
40151
+ return div;
40152
+ }
40153
+ serialize() {
40154
+ return {
40155
+ itemType: "Audio",
40156
+ url: this.url,
40157
+ transformation: this.transformation.serialize(),
40158
+ isStorageUrl: this.isStorageUrl,
40159
+ extension: this.extension
40160
+ };
40161
+ }
40162
+ deserialize(data) {
40163
+ if (data.isStorageUrl) {
40164
+ this.isStorageUrl = data.isStorageUrl;
40165
+ }
40166
+ if (data.transformation) {
40167
+ this.transformation.deserialize(data.transformation);
40168
+ }
40169
+ if (data.url) {
40170
+ this.setUrl(data.url);
40171
+ }
40172
+ if (data.extension) {
40173
+ this.extension = data.extension;
40174
+ }
40175
+ return this;
40176
+ }
40177
+ apply(op) {
40178
+ switch (op.class) {
40179
+ case "Transformation":
40180
+ this.transformation.apply(op);
40181
+ break;
40182
+ case "LinkTo":
40183
+ this.linkTo.apply(op);
40184
+ break;
40185
+ case "Audio":
40186
+ if (op.method === "setUrl") {
40187
+ this.applyUrl(op.url);
40188
+ }
40189
+ this.subject.publish(this);
40190
+ break;
40191
+ }
40192
+ }
40193
+ emit(operation) {
40194
+ if (this.events) {
40195
+ const command = new AudioCommand([this], operation);
40196
+ command.apply();
40197
+ this.events.emit(operation, command);
40198
+ } else {
40199
+ this.apply(operation);
40200
+ }
40201
+ }
40202
+ setId(id) {
40203
+ this.id = id;
40204
+ this.transformation.setId(id);
40205
+ return this;
40206
+ }
40207
+ getId() {
40208
+ return this.id;
40209
+ }
40210
+ shootLoadCallbacks() {
40211
+ while (this.loadCallbacks.length > 0) {
40212
+ this.loadCallbacks.shift()(this);
40213
+ }
40214
+ }
40215
+ shootBeforeLoadCallbacks() {
40216
+ while (this.beforeLoadCallbacks.length > 0) {
40217
+ this.beforeLoadCallbacks.shift()(this);
40218
+ }
40219
+ }
40220
+ getPath() {
40221
+ const { left, top, right, bottom } = this.getMbr();
40222
+ const leftTop = new Point(left, top);
40223
+ const rightTop = new Point(right, top);
40224
+ const rightBottom = new Point(right, bottom);
40225
+ const leftBottom = new Point(left, bottom);
40226
+ return new Path([
40227
+ new Line(leftTop, rightTop),
40228
+ new Line(rightTop, rightBottom),
40229
+ new Line(rightBottom, leftBottom),
40230
+ new Line(leftBottom, leftTop)
40231
+ ], true);
40232
+ }
40233
+ getSnapAnchorPoints() {
40234
+ const mbr = this.getMbr();
40235
+ const width2 = mbr.getWidth();
40236
+ const height2 = mbr.getHeight();
40237
+ return [
40238
+ new Point(mbr.left + width2 / 2, mbr.top),
40239
+ new Point(mbr.left + width2 / 2, mbr.bottom),
40240
+ new Point(mbr.left, mbr.top + height2 / 2),
40241
+ new Point(mbr.right, mbr.top + height2 / 2)
40242
+ ];
40243
+ }
40244
+ isClosed() {
40245
+ return true;
40246
+ }
40247
+ getRichText() {
40248
+ return null;
40249
+ }
40250
+ getLinkTo() {
40251
+ return;
40252
+ }
40253
+ getExtension() {
40254
+ return this.extension;
40255
+ }
40256
+ download() {
40257
+ if (this.extension) {
40258
+ const linkElem = conf.documentFactory.createElement("a");
40259
+ linkElem.href = this.url;
40260
+ linkElem.setAttribute("download", `${this.board.getBoardId()}.${this.extension}`);
40261
+ linkElem.click();
40262
+ }
40263
+ }
40264
+ onRemove() {
40265
+ const storageId = this.getStorageId();
40266
+ if (storageId) {
40267
+ deleteMedia([storageId], this.board.getBoardId());
40268
+ }
40269
+ super.onRemove();
40270
+ }
40271
+ }
40272
+ // src/Items/Audio/AudioHelpers.ts
40273
+ var uploadAudioToStorage = async (hash, audioBlob, accessToken, boardId) => {
40274
+ return new Promise((resolve2, reject) => {
40275
+ fetch(`${window.location.origin}/api/v1/media/audio/${boardId}`, {
40276
+ method: "POST",
40277
+ headers: {
40278
+ "Content-Type": audioBlob.type,
40279
+ "x-audio-id": hash,
40280
+ Authorization: `Bearer ${accessToken}`
40281
+ },
40282
+ body: audioBlob
40283
+ }).then(async (response) => {
40284
+ if (response.status !== 200) {
40285
+ return catchErrorResponse(response, "audio");
40286
+ }
40287
+ return response.json();
40288
+ }).then((data) => {
40289
+ console.log(data);
40290
+ resolve2(data.src);
40291
+ }).catch((error) => {
40292
+ console.error("Media storage error:", error);
40293
+ reject(error);
40294
+ });
40295
+ });
40296
+ };
40297
+ var prepareAudio = (file, accessToken, boardId) => {
40298
+ return new Promise((resolve2, reject) => {
40299
+ const audio = document.createElement("audio");
40300
+ audio.src = URL.createObjectURL(file);
40301
+ audio.onloadedmetadata = () => {
40302
+ fileTosha256(file).then((hash) => {
40303
+ uploadAudioToStorage(hash, file, accessToken, boardId).then((url) => {
40304
+ resolve2(url);
40305
+ }).catch(reject);
40306
+ }).catch(() => {
40307
+ reject(new Error("Failed to generate hash"));
40308
+ });
40309
+ };
40310
+ audio.onerror = () => {
40311
+ reject(new Error("Failed to load audio"));
40312
+ };
40313
+ });
40314
+ };
40315
+ var calculateAudioPosition = (board, audioItem) => {
40316
+ const cameraMbr = board.camera.getMbr();
40317
+ const cameraWidth = cameraMbr.getWidth();
40318
+ const translateX = cameraMbr.left + cameraWidth * 0.34;
40319
+ const translateY = cameraMbr.getCenter().y - audioItem.getHeight() / 2;
40320
+ const scale = cameraWidth * 0.32 / audioItem.getWidth();
40321
+ return new Matrix2(translateX, translateY, scale, scale);
40322
+ };
40289
40323
  // src/isSafari.ts
40290
40324
  function isSafari() {
40291
40325
  if (typeof navigator === "undefined") {
@@ -52137,9 +52171,8 @@ class Board {
52137
52171
  if (withConnectors) {
52138
52172
  connectors = this.items.getLinkedConnectorsById(item.getId()).map((connector) => connector.getId());
52139
52173
  }
52140
- const shouldClearStorageUsage = item.itemType === "Image" || item.itemType === "Video" && item.getIsStorageUrl() || item.itemType === "Audio" && item.getIsStorageUrl();
52141
- if (shouldClearStorageUsage) {
52142
- deleteMedia([item.getStorageId()], this.boardId);
52174
+ if ("onRemove" in item) {
52175
+ item.onRemove();
52143
52176
  }
52144
52177
  this.emit({
52145
52178
  class: "Board",