@twab/visualization 1.15.5 → 1.15.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1246,7 +1246,7 @@ function FramesManager(
1246
1246
  currentTime,
1247
1247
  startAudienceTime,
1248
1248
  useCache,
1249
- shiftFrames
1249
+ shiftFrames,
1250
1250
  ) {
1251
1251
  this.channel = channel;
1252
1252
  this.size = size;
@@ -1286,13 +1286,15 @@ FramesManager.prototype.getSmallBlock = async function (
1286
1286
  time,
1287
1287
  channel,
1288
1288
  split,
1289
- add = false
1289
+ add = false,
1290
1290
  ) {
1291
1291
  const requestId = `smallBlock_${time}`;
1292
1292
  try {
1293
1293
  if (!this.requests[requestId]) {
1294
1294
  this.requests[requestId] = new AbortController();
1295
1295
 
1296
+ console.log(time);
1297
+
1296
1298
  let response = (
1297
1299
  await FramesService.getSmallBlockRequest({
1298
1300
  channel,
@@ -1339,7 +1341,7 @@ FramesManager.prototype.getSmallBlock = async function (
1339
1341
  firstFrameTime - this.currentStep * this.size,
1340
1342
  this.channel,
1341
1343
  1,
1342
- true
1344
+ true,
1343
1345
  );
1344
1346
  }
1345
1347
  }
@@ -1352,7 +1354,7 @@ FramesManager.prototype.getSmallBlock = async function (
1352
1354
  return true
1353
1355
  }
1354
1356
  console.error(
1355
- `Frames Manager (GetSmallBlock): Request blocked by another equal request occurring`
1357
+ `Frames Manager (GetSmallBlock): Request blocked by another equal request occurring`,
1356
1358
  );
1357
1359
  return false
1358
1360
  } catch (err) {
@@ -1370,7 +1372,7 @@ FramesManager.prototype.getBlock = async function (
1370
1372
  direction,
1371
1373
  checkMinBlock,
1372
1374
  substituition = false,
1373
- retry = false
1375
+ retry = false,
1374
1376
  ) {
1375
1377
  const requestId = `block_${direction}_${time}`;
1376
1378
  try {
@@ -1399,7 +1401,7 @@ FramesManager.prototype.getBlock = async function (
1399
1401
  console.error(
1400
1402
  `Frames Manager (GetBlock ${direction}): ${
1401
1403
  response?.message || 'Unknown error'
1402
- }`
1404
+ }`,
1403
1405
  );
1404
1406
 
1405
1407
  setTimeout(() => {
@@ -1422,7 +1424,7 @@ FramesManager.prototype.getBlock = async function (
1422
1424
  return true
1423
1425
  }
1424
1426
  console.error(
1425
- `Frames Manager (GetBlock ${direction}): Request blocked by another equal request occurring`
1427
+ `Frames Manager (GetBlock ${direction}): Request blocked by another equal request occurring`,
1426
1428
  );
1427
1429
  return false
1428
1430
  } catch (err) {
@@ -1435,7 +1437,7 @@ FramesManager.prototype.getBlock = async function (
1435
1437
  direction,
1436
1438
  checkMinBlock,
1437
1439
  substituition,
1438
- (retry || 0) + 1
1440
+ (retry || 0) + 1,
1439
1441
  )
1440
1442
  } else {
1441
1443
  const block_size = 900;
@@ -1458,7 +1460,7 @@ FramesManager.prototype.getBlock = async function (
1458
1460
  number: i,
1459
1461
  time: bloco.inicio + i,
1460
1462
  ...(i === 0 ? { blockStart: true } : {}),
1461
- })
1463
+ }),
1462
1464
  );
1463
1465
 
1464
1466
  this.orderFramesBlock(frames);
@@ -1482,7 +1484,7 @@ FramesManager.prototype.checkMinBlockSize = async function (checkMinBlock) {
1482
1484
  1,
1483
1485
  ENUM.Direction[this.buffer.length === 0 ? 'Self' : 'Previous'],
1484
1486
  checkMinBlock,
1485
- (this.buffer[0]?.number || 0) !== 0
1487
+ (this.buffer[0]?.number || 0) !== 0,
1486
1488
  );
1487
1489
  }
1488
1490
 
@@ -1494,7 +1496,7 @@ FramesManager.prototype.checkMinBlockSize = async function (checkMinBlock) {
1494
1496
  this.channel,
1495
1497
  1,
1496
1498
  ENUM.Direction[this.buffer.length === 0 ? 'Self' : 'Next'],
1497
- checkMinBlock
1499
+ checkMinBlock,
1498
1500
  );
1499
1501
  }
1500
1502
  };
@@ -1576,21 +1578,21 @@ FramesManager.prototype.getPrevImages = function () {
1576
1578
 
1577
1579
  return this.getBufferSlice(
1578
1580
  this.currentPosition - this.size * this.currentStep,
1579
- this.currentPosition
1581
+ this.currentPosition,
1580
1582
  )
1581
1583
  };
1582
1584
 
1583
1585
  FramesManager.prototype.getCurrentImages = function () {
1584
1586
  return this.getBufferSlice(
1585
1587
  this.currentPosition,
1586
- this.currentPosition + this.size * this.currentStep
1588
+ this.currentPosition + this.size * this.currentStep,
1587
1589
  )
1588
1590
  };
1589
1591
 
1590
1592
  FramesManager.prototype.getNextImages = function () {
1591
1593
  return this.getBufferSlice(
1592
1594
  this.currentPosition + this.size * this.currentStep,
1593
- this.currentPosition + this.size * 2 * this.currentStep
1595
+ this.currentPosition + this.size * 2 * this.currentStep,
1594
1596
  )
1595
1597
  };
1596
1598
 
@@ -4236,7 +4238,6 @@ var script = {
4236
4238
  async created() {
4237
4239
  this.changeServer = this.serverOfFrames === 'alternative';
4238
4240
  this.alternativeServer = this.serverOfFrames === 'alternative';
4239
- //console.log('visualization array updated: ', this.visualizationInsArray)
4240
4241
  const settings = [
4241
4242
  {
4242
4243
  framesPerRow: 1,
@@ -4339,7 +4340,6 @@ var script = {
4339
4340
  }
4340
4341
  },
4341
4342
  scrollEvent(e) {
4342
- console.log('scroll event');
4343
4343
  if (this.active) {
4344
4344
  if (Math.sign(e.deltaY) !== Math.sign(this.scrollCounter)) {
4345
4345
  if (Math.sign(e.deltaY) > 0) {
@@ -4750,15 +4750,17 @@ var script = {
4750
4750
  const d = this.getDateParts();
4751
4751
  const limit = Date.UTC(d.year, d.month, d.day, 23, 59, 59) / 1000;
4752
4752
 
4753
- let hour = this.dateInUtc(time * 1000)
4754
- .toTimeString()
4755
- .split(' ')[0]
4756
- .split(':')
4757
- .map(Number);
4753
+ const date = new Date(time * 1000);
4754
+ let hour = [
4755
+ date.getUTCHours(),
4756
+ date.getUTCMinutes(),
4757
+ date.getUTCSeconds(),
4758
+ ];
4758
4759
 
4759
4760
  if (time > limit && time <= limit + this.startAudienceSeconds) {
4760
4761
  hour[0] = 24 + hour[0];
4761
4762
  }
4763
+
4762
4764
  return hour
4763
4765
  .map((part) => (part > 9 ? part.toString() : '0' + part))
4764
4766
  .join(separator)
@@ -5222,14 +5224,8 @@ var script = {
5222
5224
 
5223
5225
  const hours = this.startAudienceTime.match(/.{1,2}/g);
5224
5226
  const d = this.getDateParts();
5225
- const low = Date.UTC(
5226
- d.year,
5227
- d.month,
5228
- d.day,
5229
- hours[0],
5230
- hours[1],
5231
- hours[2],
5232
- );
5227
+ const low =
5228
+ Date.UTC(d.year, d.month, d.day, hours[0], hours[1], hours[2]) / 1000;
5233
5229
 
5234
5230
  this.navigationPending = await new Promise((resolve) => {
5235
5231
  this.fInterface
@@ -5692,7 +5688,6 @@ var script = {
5692
5688
  this.setFrameSelection(value);
5693
5689
  },
5694
5690
  active() {
5695
- console.log('active change');
5696
5691
  // Reset events when switching tabs
5697
5692
  if (this.removeSelectionOnInsert) {
5698
5693
  this.events = {
@@ -5727,7 +5722,6 @@ var script = {
5727
5722
  visualizationInsArray: {
5728
5723
  handler: function (newValue) {
5729
5724
  this.eventsArray = newValue;
5730
- //console.log('visualizationInsArray changed', newValue)
5731
5725
  },
5732
5726
  deep: true,
5733
5727
  },
@@ -6320,11 +6314,11 @@ __vue_render__._withStripped = true;
6320
6314
  /* style */
6321
6315
  const __vue_inject_styles__ = function (inject) {
6322
6316
  if (!inject) return
6323
- inject("data-v-4c8bbfb5_0", { source: "\n.visualization-row[data-v-4c8bbfb5] {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex: 1 1 auto;\n}\n.visualization-col[data-v-4c8bbfb5] {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 5px;\n}\n.visualization-divider[data-v-4c8bbfb5] {\r\n display: block;\r\n flex: 1 1 100%;\r\n height: 0px;\r\n max-height: 0px;\r\n opacity: 1;\r\n transition: inherit;\r\n border-style: solid;\r\n border-width: thin 0 0 0;\r\n border-color: rgba(0, 0, 0, 0.12);\r\n margin: 0;\n}\n.visualization-divider.vertical[data-v-4c8bbfb5] {\r\n align-self: stretch;\r\n border-width: 0 thin 0 0;\r\n display: inline-flex;\r\n height: inherit;\r\n margin-left: -1px;\r\n max-height: 100%;\r\n max-width: 0px;\r\n vertical-align: text-bottom;\r\n width: 0px;\n}\n.visualization-card[data-v-4c8bbfb5] {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 12px;\r\n width: 100%;\r\n transition-property: box-shadow, opacity, -webkit-box-shadow;\r\n overflow-wrap: break-word;\r\n /*box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),\r\n 0 1px 5px 0 rgba(0, 0, 0, 0.12);*/\n}\n.visualization-justify-center[data-v-4c8bbfb5],\r\n*[data-v-4c8bbfb5] .visualization-justify-center {\r\n justify-content: center;\n}\n.visualization-align-center[data-v-4c8bbfb5] {\r\n align-items: center;\n}\n#visualization-container[data-v-4c8bbfb5] {\r\n max-width: 100% !important;\r\n margin: 0 auto !important;\r\n height: 100%;\r\n border-bottom: none;\n}\n#visualization-container > .card[data-v-4c8bbfb5] {\r\n border-radius: 0 !important;\r\n font-size: 12px;\r\n width: 100%;\r\n box-shadow: none;\r\n height: 100%;\n}\n#command-bar[data-v-4c8bbfb5],\r\n#info-bar[data-v-4c8bbfb5] {\r\n background-color: #f5f5f5;\r\n box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),\r\n 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n}\n#command-bar button[data-v-4c8bbfb5] {\r\n width: 42px;\r\n height: 36px;\r\n border: none;\r\n background: none;\n}\n#command-bar button[data-v-4c8bbfb5]:hover {\r\n cursor: pointer;\r\n background: rgba(0, 0, 0, 0.12);\n}\n#command-bar svg[data-v-4c8bbfb5] {\r\n font-size: 16px;\n}\n#command-bar[data-v-4c8bbfb5] {\r\n padding: 0 !important;\n}\n#info-bar[data-v-4c8bbfb5] {\r\n padding: 4px;\r\n font-size: 14px;\r\n position: relative;\n}\n.settings-container[data-v-4c8bbfb5] {\r\n position: absolute;\r\n right: 14px;\r\n top: 50%;\r\n transform: translateY(-50%);\n}\n.settings-container > *[data-v-4c8bbfb5] {\r\n margin: 0 2px;\r\n cursor: pointer;\n}\n#info-bar svg[data-v-4c8bbfb5] {\r\n font-size: 16px;\n}\n#info-bar .divider[data-v-4c8bbfb5] {\r\n margin: 0 8px;\n}\nsvg[data-v-4c8bbfb5]:focus {\r\n border: none;\n}\n.visualization-card[data-v-4c8bbfb5] {\r\n border-left: 8px solid #eee;\n}\n.active-tab[data-v-4c8bbfb5] {\r\n border-left: 8px solid var(--visualization-primary) !important;\r\n border-image-slice: 1;\n}\n[id^='frame-'][data-v-4c8bbfb5] {\r\n padding: 1px;\r\n display: flex;\r\n flex-flow: column;\n}\n.tooltip[data-v-4c8bbfb5] {\r\n display: block !important;\r\n z-index: 10000;\n}\n.tooltip .tooltip-inner[data-v-4c8bbfb5] {\r\n background: var(--visualization-primary);\r\n color: white;\r\n border-radius: 16px;\r\n padding: 5px 10px 4px;\n}\n.tooltip .tooltip-arrow[data-v-4c8bbfb5] {\r\n width: 0;\r\n height: 0;\r\n border-style: solid;\r\n position: absolute;\r\n margin: 5px;\r\n border-color: var(--visualization-primary);\r\n z-index: 1;\n}\n.tooltip[x-placement^='top'][data-v-4c8bbfb5] {\r\n margin-bottom: 5px;\n}\n.tooltip[x-placement^='top'] .tooltip-arrow[data-v-4c8bbfb5] {\r\n border-width: 5px 5px 0 5px;\r\n border-left-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n bottom: -5px;\r\n left: calc(50% - 5px);\r\n margin-top: 0;\r\n margin-bottom: 0;\n}\n.tooltip[x-placement^='bottom'][data-v-4c8bbfb5] {\r\n margin-top: 5px;\n}\n.tooltip[x-placement^='bottom'] .tooltip-arrow[data-v-4c8bbfb5] {\r\n border-width: 0 5px 5px 5px;\r\n border-left-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-top-color: transparent !important;\r\n top: -5px;\r\n left: calc(50% - 5px);\r\n margin-top: 0;\r\n margin-bottom: 0;\n}\n.tooltip[x-placement^='right'][data-v-4c8bbfb5] {\r\n margin-left: 5px;\n}\n.tooltip[x-placement^='right'] .tooltip-arrow[data-v-4c8bbfb5] {\r\n border-width: 5px 5px 5px 0;\r\n border-left-color: transparent !important;\r\n border-top-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n left: -5px;\r\n top: calc(50% - 5px);\r\n margin-left: 0;\r\n margin-right: 0;\n}\n.tooltip[x-placement^='left'][data-v-4c8bbfb5] {\r\n margin-right: 5px;\n}\n.tooltip[x-placement^='left'] .tooltip-arrow[data-v-4c8bbfb5] {\r\n border-width: 5px 0 5px 5px;\r\n border-top-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n right: -5px;\r\n top: calc(50% - 5px);\r\n margin-left: 0;\r\n margin-right: 0;\n}\n.tooltip.popover .popover-inner[data-v-4c8bbfb5] {\r\n background: #f9f9f9;\r\n color: black;\r\n padding: 24px;\r\n border-radius: 5px;\r\n box-shadow: 0 5px 30px rgba(black, 0.1);\n}\n.tooltip.popover .popover-arrow[data-v-4c8bbfb5] {\r\n border-color: #f9f9f9;\n}\n.tooltip[aria-hidden='true'][data-v-4c8bbfb5] {\r\n visibility: hidden;\r\n opacity: 0;\r\n transition: opacity 0.15s, visibility 0.15s;\n}\n.tooltip[aria-hidden='false'][data-v-4c8bbfb5] {\r\n visibility: visible;\r\n opacity: 1;\r\n transition: opacity 0.15s;\n}\n.custom-frametime[data-v-4c8bbfb5] {\r\n font-size: smaller;\n}\r\n\r\n/* DAR CORES AOS TEMPOS DEPENDENDO DO TIPO DE EVENTO */\n.event-A[data-v-4c8bbfb5] {\r\n color: #000; /* black for A */\n}\n.event-B[data-v-4c8bbfb5] {\r\n color: rgb(0, 115, 230); /* blue for B */\n}\n.event-C[data-v-4c8bbfb5] {\r\n color: rgb(0, 179, 0); /* green for C */\n}\n.event-gap[data-v-4c8bbfb5] {\r\n color: #555; /* Default color for gaps */\n}\r\n", map: {"version":3,"sources":["C:\\Workspace\\visualization\\src\\Visualization.vue"],"names":[],"mappings":";AAq5DA;EACA,aAAA;EACA,eAAA;EACA,cAAA;AACA;AAEA;EACA,aAAA;EACA,YAAA;EACA,eAAA;EACA,YAAA;AACA;AAEA;EACA,cAAA;EACA,cAAA;EACA,WAAA;EACA,eAAA;EACA,UAAA;EACA,mBAAA;EACA,mBAAA;EACA,wBAAA;EACA,iCAAA;EACA,SAAA;AACA;AAEA;EACA,mBAAA;EACA,wBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,cAAA;EACA,2BAAA;EACA,UAAA;AACA;AAEA;EACA,aAAA;EACA,YAAA;EACA,eAAA;EACA,aAAA;EACA,WAAA;EACA,4DAAA;EACA,yBAAA;EACA;qCACA;AACA;AAEA;;EAEA,uBAAA;AACA;AAEA;EACA,mBAAA;AACA;AAEA;EACA,0BAAA;EACA,yBAAA;EACA,YAAA;EACA,mBAAA;AACA;AACA;EACA,2BAAA;EACA,eAAA;EACA,WAAA;EACA,gBAAA;EACA,YAAA;AACA;AAEA;;EAEA,yBAAA;EACA;mCACA;AACA;AACA;EACA,WAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;AACA;AAEA;EACA,eAAA;EACA,+BAAA;AACA;AAEA;EACA,eAAA;AACA;AAEA;EACA,qBAAA;AACA;AAEA;EACA,YAAA;EACA,eAAA;EACA,kBAAA;AACA;AAEA;EACA,kBAAA;EACA,WAAA;EACA,QAAA;EACA,2BAAA;AACA;AAEA;EACA,aAAA;EACA,eAAA;AACA;AAEA;EACA,eAAA;AACA;AAEA;EACA,aAAA;AACA;AAEA;EACA,YAAA;AACA;AAEA;EACA,2BAAA;AACA;AAEA;EACA,8DAAA;EACA,qBAAA;AACA;AAEA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;AACA;AAEA;EACA,yBAAA;EACA,cAAA;AACA;AAEA;EACA,wCAAA;EACA,YAAA;EACA,mBAAA;EACA,qBAAA;AACA;AAEA;EACA,QAAA;EACA,SAAA;EACA,mBAAA;EACA,kBAAA;EACA,WAAA;EACA,0CAAA;EACA,UAAA;AACA;AAEA;EACA,kBAAA;AACA;AAEA;EACA,2BAAA;EACA,yCAAA;EACA,0CAAA;EACA,2CAAA;EACA,YAAA;EACA,qBAAA;EACA,aAAA;EACA,gBAAA;AACA;AAEA;EACA,eAAA;AACA;AAEA;EACA,2BAAA;EACA,yCAAA;EACA,0CAAA;EACA,wCAAA;EACA,SAAA;EACA,qBAAA;EACA,aAAA;EACA,gBAAA;AACA;AAEA;EACA,gBAAA;AACA;AAEA;EACA,2BAAA;EACA,yCAAA;EACA,wCAAA;EACA,2CAAA;EACA,UAAA;EACA,oBAAA;EACA,cAAA;EACA,eAAA;AACA;AAEA;EACA,iBAAA;AACA;AAEA;EACA,2BAAA;EACA,wCAAA;EACA,0CAAA;EACA,2CAAA;EACA,WAAA;EACA,oBAAA;EACA,cAAA;EACA,eAAA;AACA;AAEA;EACA,mBAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,uCAAA;AACA;AAEA;EACA,qBAAA;AACA;AAEA;EACA,kBAAA;EACA,UAAA;EACA,2CAAA;AACA;AAEA;EACA,mBAAA;EACA,UAAA;EACA,yBAAA;AACA;AAEA;EACA,kBAAA;AACA;;AAEA,sDAAA;AACA;EACA,WAAA,EAAA,gBAAA;AACA;AACA;EACA,uBAAA,EAAA,eAAA;AACA;AACA;EACA,qBAAA,EAAA,gBAAA;AACA;AACA;EACA,WAAA,EAAA,2BAAA;AACA","file":"Visualization.vue","sourcesContent":["<template>\r\n <div\r\n class=\"visualization-row\"\r\n id=\"visualization-container\"\r\n @click=\"framesClicked\"\r\n @scroll=\"scrollEvent\"\r\n >\r\n <GlobalEvents\r\n v-if=\"!readOnly && canInsertTime && settingsClosed\"\r\n :filter=\"(event) => !event.shiftKey && !event.ctrlKey\"\r\n @keydown.45=\"insertTime\"\r\n />\r\n <!-- <GlobalEvents\r\n v-if=\"!readOnly && active && settingsClosed\"\r\n @keydown.113.prevent=\"toggleEventA\"\r\n @keydown.114.prevent=\"toggleEventC\"\r\n @keydown.116.prevent=\"toggleEventB\" /> -->\r\n <GlobalEvents\r\n v-if=\"active && settingsClosed\"\r\n @keydown.46.prevent=\"clearAllFrameSelections\"\r\n @keydown.left.prevent=\"arrowLeft\"\r\n @keydown.right.prevent=\"arrowRight\"\r\n @keydown.up.prevent=\"arrowUp\"\r\n @keydown.down.prevent=\"arrowDown\"\r\n @keydown.shift.page-down.prevent=\"nextLoopActivate\"\r\n @keydown.page-down.prevent=\"() => next()\"\r\n @keydown.page-up.prevent=\"() => prev()\"\r\n @keydown.shift.page-up.prevent=\"prevLoopActivate\"\r\n @keydown.36.prevent=\"goToFirstFrame\"\r\n @keydown.35.prevent=\"goToLastFrame\"\r\n @keydown.71.prevent=\"dialogs.goTo = true\"\r\n @keydown.73.prevent=\"dialogs.secondsPerFrame = true\"\r\n @keydown.76.prevent=\"dialogs.frames = true\"\r\n @keydown.49.97=\"() => (secondsPerFrame = 1)\"\r\n @keydown.50.98=\"() => (secondsPerFrame = 2)\"\r\n @keydown.51.99=\"() => (secondsPerFrame = 3)\"\r\n @keydown.52.100=\"() => (secondsPerFrame = 4)\"\r\n @keydown.53.101=\"() => (secondsPerFrame = 5)\"\r\n />\r\n <GlobalEvents\r\n v-if=\"prevLoop || nextLoop\"\r\n @keydown=\"breakLoop\"\r\n v-on:click=\"breakLoop\"\r\n />\r\n <settings\r\n v-if=\"active\"\r\n :dialogs-visibility=\"dialogs\"\r\n :playback-rate=\"playbackRate\"\r\n :seconds-per-frame=\"secondsPerFrame\"\r\n :frames-per-row=\"framesPerRow\"\r\n :number-of-rows=\"numberOfRows\"\r\n :max-steps=\"maxSteps\"\r\n :shift-frames=\"shiftFrames\"\r\n :customGridSize=\"customGridSize\"\r\n :stretchFrames=\"stretchFrame\"\r\n @change-playback-rate=\"(value) => (playbackRate = value)\"\r\n @change-go-to=\"changeHour\"\r\n @change-seconds-per-frame=\"(value) => (secondsPerFrame = value)\"\r\n @change-shift-frames=\"(value) => (shiftFrames = value)\"\r\n @set-frames-selection=\"setFrameSelection\"\r\n @set-custom-grid-values=\"setCustomGridValues\"\r\n @close=\"(dialog) => (dialogs[dialog] = false)\"\r\n />\r\n <div\r\n :class=\"{ 'visualization-card': true, 'active-tab': active }\"\r\n style=\"width: 100%; padding: 0\"\r\n >\r\n <command-bar\r\n v-show=\"commandBarShow\"\r\n :read-only=\"readOnly\"\r\n :video-playing=\"videoPlaying\"\r\n :video-paused=\"paused\"\r\n :insert-time=\"canInsertTime\"\r\n @prev-loop-activate=\"prevLoopActivate\"\r\n @next-loop-activate=\"nextLoopActivate\"\r\n @prev=\"prev\"\r\n @next=\"next\"\r\n @go-to=\"dialogs.goTo = true\"\r\n @open-frame-selection=\"dialogs.frames = true\"\r\n @open-frames-per-second=\"dialogs.secondsPerFrame = true\"\r\n @open-blocks=\"openBlocks\"\r\n @open-playback-rate=\"dialogs.playbackRate = true\"\r\n @play-or-pause=\"playOrPause\"\r\n @stop-playing=\"stopPlayingBar\"\r\n @insert-time=\"insertTime\"\r\n @shift-frames=\"dialogs.shiftFrames = true\"\r\n />\r\n <video-progress\r\n v-if=\"videoProgressBar\"\r\n v-show=\"videoPlaying\"\r\n :video-time=\"videoTime\"\r\n />\r\n <info-bar\r\n :playback-rate=\"playbackRate\"\r\n :seconds-per-frame=\"secondsPerFrame\"\r\n :commands-show=\"commandBarShow\"\r\n :cache=\"useCache\"\r\n :block-start-time=\"blockStartTime\"\r\n :video-total-duration=\"videoSliderTotalDuration\"\r\n :alternative-server=\"alternativeServer\"\r\n :frames-per-row=\"framesPerRow\"\r\n :number-of-rows=\"numberOfRows\"\r\n :show-stretch-frame=\"showStretchFrame\"\r\n @toogle-commands-visibility=\"toogleCommandsVisibility\"\r\n @toogle-cache=\"useCache = !useCache\"\r\n @change-server=\"changeServerClick\"\r\n @update-stretch-frames=\"(value) => (stretchFrame = value)\"\r\n />\r\n <div\r\n class=\"visualization-row\"\r\n v-for=\"rowNumber in numberOfRows\"\r\n :id=\"'row-' + rowNumber\"\r\n :key=\"'row-' + rowNumber\"\r\n style=\"padding: 0px 4px\"\r\n >\r\n <div\r\n v-for=\"(frame, frameNumber) in previousFrames\"\r\n :key=\"\r\n numberOfRows +\r\n '-' +\r\n framesPerRow +\r\n '-' +\r\n getIndex(rowNumber, frameNumber, Positions.previous)\r\n \"\r\n style=\"display: none\"\r\n >\r\n <span v-html=\"frame.img\" />\r\n </div>\r\n <div\r\n v-for=\"(frame, frameNumber) in nextFrames\"\r\n :key=\"\r\n numberOfRows +\r\n '-' +\r\n framesPerRow +\r\n '-' +\r\n getIndex(rowNumber, frameNumber, Positions.next)\r\n \"\r\n style=\"display: none\"\r\n >\r\n <span v-html=\"frame.img\" />\r\n </div>\r\n <div\r\n class=\"visualization-col\"\r\n v-for=\"(frame, frameNumber) in frames.slice(\r\n framesPerRow * (rowNumber - 1),\r\n framesPerRow * rowNumber,\r\n )\"\r\n :key=\"'row-' + rowNumber + '-frame-' + frameNumber + '-' + frame.time\"\r\n :id=\"`frame-${getIndex(rowNumber, frameNumber, Positions.current)}`\"\r\n :class=\"{ loaderImg: !!frame.img }\"\r\n @click=\"\r\n frame.time\r\n ? selectFrame(\r\n getIndex(rowNumber, frameNumber, Positions.current),\r\n frame,\r\n )\r\n : null\r\n \"\r\n >\r\n <span\r\n :class=\"giveMeType(frame.time)\"\r\n :id=\"activeFrame ? 'aa' : 0\"\r\n style=\"text-align: center\"\r\n >\r\n <b>\r\n {{\r\n getAudienceTime(\r\n frame.time,\r\n rowNumber,\r\n frameNumber,\r\n Positions.current,\r\n )\r\n }}\r\n </b>\r\n </span>\r\n\r\n <frame\r\n ref=\"frames\"\r\n :frame=\"frame\"\r\n :index=\"getIndex(rowNumber, frameNumber, Positions.current)\"\r\n :grid-settings=\"{ numberOfRows, framesPerRow }\"\r\n :initialTime=\"isEventStart(frame.time)\"\r\n :endTime=\"isEventEnd(frame.time)\"\r\n :checkpointTime=\"false\"\r\n :betweenTime=\"isBetweenEvent(frame.time)\"\r\n :eventType=\"getEventType(frame.time)\"\r\n :active=\"\r\n getIndex(rowNumber, frameNumber, Positions.current) ===\r\n activeFrame\r\n \"\r\n :activeTab=\"active\"\r\n :videoUrl=\"fInterface ? fInterface.getVideoUrl(frame) : ''\"\r\n :videoControls=\"videoControls\"\r\n @startPlaying=\"startPlaying\"\r\n @stopPlaying=\"stopPlaying\"\r\n @playPauseStatus=\"changePlayPause\"\r\n @updateSlider=\"updateSlider\"\r\n :playback-rate=\"playbackRate\"\r\n :aspect-ratio=\"aspectRatio\"\r\n :stretchFrame=\"stretchFrame\"\r\n style=\"margin: 0 auto\"\r\n ></frame>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- <settings\r\n ref=\"settings2\"\r\n :active=\"active\"\r\n @goToTime=\"changeHour\"\r\n @goToBlockInterval=\"changeBlockInterval\"\r\n @setSplitTime=\"setSplitTime\"\r\n @setFrameSelection=\"setFrameSelection\"\r\n @setPlaybackRate=\"\r\n (rate) => {\r\n playbackRate = rate\r\n }\r\n \"\r\n >\r\n </settings> -->\r\n <!-- <v-dialog v-model=\"dialog\" width=\"500\">\r\n <div class=\"card\">\r\n <div class=\"card\"-title class=\"text-h5 grey lighten-2\">\r\n {{ ' Último bloco disponível até: ' }}\r\n <v-btn\r\n @click=\"goToStartBlock\"\r\n class=\"ml-2\"\r\n dark\r\n color=\"success\"\r\n depressed\r\n >\r\n <v-icon left> fa-clock </v-icon>\r\n {{ timeLastBlock }}\r\n </v-btn>\r\n <v-spacer></v-spacer>\r\n <v-btn color=\"error\" fab small class=\"ml-5\" @click=\"dialog = false\">\r\n <v-icon dark> fa fa-xmark </v-icon>\r\n </v-btn>\r\n </div-title>\r\n </div>\r\n </v-dialog>\r\n <Help :media=\"media\" @close=\"media = null\" />\r\n <v-dialog\r\n v-if=\"userMultiTabsGrid\"\r\n v-model=\"userMultiTabsGridsModel\"\r\n persistent\r\n width=\"60%\"\r\n >\r\n <div class=\"card\">\r\n <div class=\"card\"-title class=\"warning text-h5\" primary-title>\r\n <div class=\"row\" class=\"ma-0\" justify=\"center\" align=\"center\">\r\n <v-icon dark left style=\"font-size: 24px !important\">\r\n fa fa-exclamation-triangle\r\n </v-icon>\r\n <div style=\"color: white\">{{ $t('form.alert') }}</div>\r\n </div>\r\n </div-title>\r\n <div class=\"card\"-text class=\"justify-center pa-6 grey lighten-2\">\r\n <h3>\r\n {{ $t('alerts.userMultiTabsGrid') }}\r\n </h3>\r\n </div-text>\r\n <hr class=\"divider\" class=\"grey lighten-1\"></span>\r\n <div class=\"card\"-actions class=\"grey lighten-2 justify-center\">\r\n <v-btn color=\"error\" ml-5 @click=\"userMultiTabsGrid = false\">\r\n <v-icon left color=\"white\">fa fa-times</v-icon>\r\n {{ $t('form.close') }}\r\n </v-btn>\r\n </div-actions>\r\n </div>\r\n </v-dialog> -->\r\n </div>\r\n</template>\r\n<script>\r\nimport Frame from './components/Frame.vue'\r\nimport FramesInterface from './utils/FramesInterface.js'\r\nimport FramesService from './services/FramesService.js'\r\n\r\nimport Commands from './components/Commands.vue'\r\nimport Infos from './components/Infos.vue'\r\nimport VideoProgress from './components/VideoProgress.vue'\r\nimport Settings from './components/Settings.vue'\r\n\r\nconst Positions = Object.freeze({\r\n previous: 0,\r\n current: 1,\r\n next: 2,\r\n})\r\n\r\nexport default {\r\n name: 'visualization-container',\r\n props: {\r\n value: {\r\n type: Boolean,\r\n required: true,\r\n },\r\n date: {\r\n type: String,\r\n required: true,\r\n },\r\n channel: {\r\n type: Number,\r\n required: true,\r\n },\r\n startAudienceTime: {\r\n type: String,\r\n required: true,\r\n },\r\n endAudienceTime: {\r\n type: String,\r\n required: true,\r\n },\r\n videoProgressBar: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n jumpOnInsert: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n removeSelectionOnInsert: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n framesFormat: {\r\n type: [Number, String],\r\n default: 7,\r\n },\r\n maxSize: {\r\n type: Number,\r\n },\r\n videoControls: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n readOnly: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n maxSteps: {\r\n type: Number,\r\n default: 1,\r\n },\r\n customGridSize: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n showStretchFrame: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n visualizationInsArray: {\r\n type: Array,\r\n default: () => [],\r\n },\r\n insertDefaults: {\r\n type: Array,\r\n default: () => [],\r\n },\r\n },\r\n components: {\r\n Frame,\r\n CommandBar: Commands,\r\n InfoBar: Infos,\r\n VideoProgress,\r\n Settings,\r\n // Help,\r\n },\r\n data() {\r\n return {\r\n Positions,\r\n updatingChannel: null,\r\n dialog: false,\r\n timeLastBlock: null,\r\n alternativeServer: false,\r\n useCache: true,\r\n numberOfRows: 1,\r\n framesPerRow: 5,\r\n secondsPerFrame: 1,\r\n fInterface: null,\r\n velocity: 1,\r\n frames: [],\r\n previousFrames: [],\r\n nextFrames: [],\r\n channelCode: 0,\r\n videoPlaying: false,\r\n activeFrame: null,\r\n activeVideo: null,\r\n videoTime: 0,\r\n videoTotalTime: null,\r\n progressVideoDrag: false,\r\n events: {\r\n A: [],\r\n B: [],\r\n C: [],\r\n },\r\n currentEventA: null,\r\n activeEvents: {}, // Track active events by type\r\n eventBlocked: false, // Track if event creation is blocked\r\n canInsertTime: false,\r\n lastHeight: 0,\r\n loopInterval: null,\r\n nextLoop: false,\r\n prevLoop: false,\r\n videoSliderTotalDuration: 900,\r\n blockStartTime: null,\r\n media: null,\r\n changeServer: false,\r\n userMultiTabsGrid: false,\r\n userMultiTabsGridsModel: true,\r\n playbackRate: 1,\r\n paused: false,\r\n commandBarShow: true,\r\n dialogs: {\r\n playbackRate: false,\r\n goTo: false,\r\n secondsPerFrame: false,\r\n frames: false,\r\n shiftFrames: false,\r\n },\r\n lastNext: 0,\r\n lastPrev: 0,\r\n shiftFrames: 0,\r\n aspectRatio: 11 / 9,\r\n showCustomInputs: false,\r\n stretchFrame: false,\r\n scrollCounter: 300,\r\n eventsArray: [],\r\n }\r\n },\r\n async created() {\r\n this.changeServer = this.serverOfFrames === 'alternative'\r\n this.alternativeServer = this.serverOfFrames === 'alternative'\r\n //console.log('visualization array updated: ', this.visualizationInsArray)\r\n const settings = [\r\n {\r\n framesPerRow: 1,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 2,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 3,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 3,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 4,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 4,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 5,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 5,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 6,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 6,\r\n numberOfRows: 2,\r\n },\r\n {\r\n //Custom (item 11)\r\n framesPerRow: 7,\r\n numberOfRows: 3,\r\n },\r\n ]\r\n\r\n // Os valores custom são guardados no local storage e não na base de dados\r\n const customValues = JSON.parse(localStorage.getItem('customGridValues'))\r\n\r\n if (this.framesFormat == '11' && !!customValues) {\r\n this.numberOfRows = customValues[0]\r\n this.framesPerRow = customValues[1]\r\n } else {\r\n const storedOnDb = settings[parseInt(this.framesFormat) - 1]\r\n this.framesPerRow = storedOnDb.framesPerRow\r\n this.numberOfRows = storedOnDb.numberOfRows\r\n }\r\n\r\n document.addEventListener('wheel', this.scrollEvent)\r\n document.addEventListener('keydown', this.handleKeydown)\r\n\r\n await this.createFramesInterface()\r\n this.$nextTick(this.resize)\r\n },\r\n methods: {\r\n handleKeydown(event) {\r\n if (!this.active || !this.settingsClosed || this.readOnly) return\r\n\r\n // Check if the pressed key matches any shortcut in insertDefaults\r\n const matchedConfig = this.insertDefaults.find((config) => {\r\n const shortcut = config.shortcut\r\n\r\n // Parse the shortcut string (e.g., \"Shift+F8\", \"Ctrl+Shift+F3\", \"F2\")\r\n const parts = shortcut.split('+').map((p) => p.trim())\r\n const key = parts[parts.length - 1].toUpperCase()\r\n\r\n // Check modifiers\r\n const requiresShift = parts.some((p) => p.toLowerCase() === 'shift')\r\n const requiresCtrl = parts.some((p) => p.toLowerCase() === 'ctrl')\r\n const requiresAlt = parts.some((p) => p.toLowerCase() === 'alt')\r\n const requiresMeta = parts.some(\r\n (p) => p.toLowerCase() === 'meta' || p.toLowerCase() === 'cmd',\r\n )\r\n\r\n // Match if key and all modifiers match\r\n return (\r\n event.key.toUpperCase() === key &&\r\n event.shiftKey === requiresShift &&\r\n event.ctrlKey === requiresCtrl &&\r\n event.altKey === requiresAlt &&\r\n event.metaKey === requiresMeta\r\n )\r\n })\r\n\r\n if (matchedConfig) {\r\n event.preventDefault()\r\n this.toggleEvent(matchedConfig.event, matchedConfig.shortcut)\r\n }\r\n },\r\n scrollEvent(e) {\r\n console.log('scroll event')\r\n if (this.active) {\r\n if (Math.sign(e.deltaY) !== Math.sign(this.scrollCounter)) {\r\n if (Math.sign(e.deltaY) > 0) {\r\n this.next()\r\n } else {\r\n this.prev()\r\n }\r\n this.scrollCounter = e.deltaY\r\n return\r\n }\r\n\r\n this.scrollCounter += e.deltaY\r\n if (Math.abs(this.scrollCounter) >= 400) {\r\n if (Math.sign(e.deltaY) > 0) {\r\n this.next()\r\n } else {\r\n this.prev()\r\n }\r\n this.scrollCounter = 0\r\n }\r\n }\r\n },\r\n stopVideoPlaying(array) {\r\n for (const frame of array || this.$refs.frames) {\r\n if (\r\n frame.videoStatus === frame.Status.playing ||\r\n frame.videoStatus === frame.Status.paused\r\n ) {\r\n frame.stop(false)\r\n }\r\n }\r\n this.activeVideo = null\r\n },\r\n toogleCommandsVisibility() {\r\n this.commandBarShow = !this.commandBarShow\r\n this.$nextTick(this.resize)\r\n },\r\n framesClicked(e) {\r\n if (e.target.parentNode.id != 'insert') {\r\n this.active = true\r\n }\r\n },\r\n async goToStartBlock() {\r\n try {\r\n const d = new Date()\r\n let timestamp = Date.UTC(\r\n d.getFullYear(),\r\n d.getMonth(),\r\n d.getDate(),\r\n d.getHours(),\r\n d.getMinutes(),\r\n d.getSeconds(),\r\n )\r\n\r\n const response = (\r\n await FramesService.getNextAvailableBlock({\r\n channel: this.channel,\r\n time: timestamp / 1000,\r\n })\r\n ).data\r\n\r\n this.dialog = false\r\n this.changeHour(this.convertToAudienceTime(response.data.start, ':'))\r\n } catch (err) {\r\n // console.error(err)\r\n }\r\n },\r\n async checkAvailableBlock() {\r\n try {\r\n const d = new Date()\r\n let timestamp = Date.UTC(\r\n d.getFullYear(),\r\n d.getMonth(),\r\n d.getDate(),\r\n d.getHours(),\r\n d.getMinutes(),\r\n d.getSeconds(),\r\n )\r\n\r\n const response = (\r\n await FramesService.getNextAvailableBlock({\r\n channel: this.channel,\r\n time: timestamp / 1000,\r\n })\r\n ).data\r\n\r\n this.timeLastBlock = this.convertToAudienceTime(response.data.end, ':')\r\n this.dialog = true\r\n if (!response.status) {\r\n this.timeLastBlock = 'N/D'\r\n }\r\n } catch (err) {\r\n // console.error(err)\r\n }\r\n },\r\n updateSlider(videoStartTime, time) {\r\n // * atualizar slider se estiver fora do range definido previamente\r\n if (\r\n time < this.blockStartTime ||\r\n time > this.blockStartTime ||\r\n videoStartTime > this.blockStartTime + this.videoSliderTotalDuration\r\n ) {\r\n this.blockStartTime = videoStartTime\r\n this.videoSliderTotalDuration = 900\r\n }\r\n },\r\n nextLoopActivate() {\r\n this.breakLoop()\r\n this.loopInterval = setInterval(\r\n () => this.next({ ignoreTime: true }),\r\n this.swapImagesDelay,\r\n )\r\n setTimeout(() => {\r\n this.nextLoop = true\r\n }, 0)\r\n },\r\n prevLoopActivate() {\r\n this.breakLoop()\r\n this.loopInterval = setInterval(\r\n () => this.prev({ ignoreTime: true }),\r\n this.swapImagesDelay,\r\n )\r\n setTimeout(() => {\r\n this.prevLoop = true\r\n }, 0)\r\n },\r\n breakLoop() {\r\n clearInterval(this.loopInterval)\r\n this.loopInterval = null\r\n this.nextLoop = false\r\n this.prevLoop = false\r\n },\r\n changePlayPause(status) {\r\n this.paused = !status\r\n },\r\n resize(height = this.lastHeight) {\r\n this.lastHeight = height\r\n if (this.$refs.frames) {\r\n for (let frame of this.$refs.frames) {\r\n frame.resize(height)\r\n }\r\n }\r\n this.$emit('resized')\r\n },\r\n async goToFirstFrame() {\r\n let frames = this.$refs.frames\r\n\r\n let audienceTime = null\r\n if (frames.length > 0) {\r\n let frame = frames[0].frame\r\n audienceTime = this.getAudienceTime(frame.time, 0, 0, 0)\r\n }\r\n if (audienceTime) {\r\n const [hours, minutes, seconds] = audienceTime.split(':')\r\n const totalSeconds =\r\n parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(seconds)\r\n if (totalSeconds >= 9000)\r\n this.changeHour(this.getLastFirtsBlockTime(audienceTime, true))\r\n else this.changeHour(this.getLastFirtsBlockTime('02:30:00', true))\r\n }\r\n },\r\n async goToLastFrame() {\r\n let frames = this.$refs.frames\r\n let audienceTime = null\r\n if (frames.length > 0) {\r\n let frame = frames[0].frame\r\n\r\n audienceTime = this.getAudienceTime(frame.time, 0, 0, 0)\r\n }\r\n if (audienceTime) {\r\n this.changeHour(this.getLastFirtsBlockTime(audienceTime))\r\n }\r\n },\r\n getLastFirtsBlockTime(time, first = false) {\r\n if (time.indexOf(':') !== -1) {\r\n time = time.replace(/:/g, '')\r\n }\r\n let h, m, newTime\r\n const t = time.match(/.{1,2}/g)\r\n if (t[0] && t[1]) {\r\n h = parseInt(t[0])\r\n m = parseInt(t[1])\r\n }\r\n if (h < 26) {\r\n if (m < 15)\r\n if (first) newTime = t[0] + ':00:00'\r\n else newTime = t[0] + ':14:59'\r\n else if (m < 30)\r\n if (first) newTime = t[0] + ':15:00'\r\n else newTime = t[0] + ':29:59'\r\n else if (m < 45)\r\n if (first) newTime = t[0] + ':30:00'\r\n else newTime = t[0] + ':44:59'\r\n else if (first) newTime = t[0] + ':45:00'\r\n else newTime = t[0] + ':59:59'\r\n } else {\r\n if (m < 15)\r\n if (first) newTime = '26:00:00'\r\n else newTime = '26:14:59'\r\n else {\r\n if (first) newTime = '26:15:00'\r\n else newTime = '26:29:59'\r\n }\r\n }\r\n return newTime\r\n },\r\n openBlocks() {\r\n this.$refs.settings2?.openBlocks()\r\n },\r\n playOrPause() {\r\n const array = this.$refs.frames.filter((fc) => !!fc.active)\r\n if (array.length === 1) {\r\n const frame = array[0]\r\n frame.playOrPause(this.playbackRate)\r\n }\r\n },\r\n stopPlayingBar() {\r\n for (let ref of this.$refs.frames) {\r\n if (\r\n ref.videoStatus === ref.Status.playing ||\r\n ref.videoStatus === ref.Status.paused\r\n ) {\r\n ref.stop(false)\r\n }\r\n }\r\n },\r\n async setFrameSelection(selected) {\r\n this.frames = this.loadingArray\r\n const settings = [\r\n {\r\n framesPerRow: 1,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 2,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 3,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 3,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 4,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 4,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 5,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 5,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 6,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 6,\r\n numberOfRows: 2,\r\n },\r\n {\r\n //Custom (item 11)\r\n framesPerRow: 7,\r\n numberOfRows: 3,\r\n },\r\n ]\r\n\r\n const formatSelected = settings[selected - 1]\r\n this.framesPerRow = formatSelected.framesPerRow\r\n this.numberOfRows = formatSelected.numberOfRows\r\n\r\n await this.fInterface.changeSize(this.numberOfRows, this.framesPerRow)\r\n this.getFramesArray()\r\n this.$nextTick(this.resize)\r\n this.$emit('frames-format-changed', selected)\r\n },\r\n async setCustomGridValues(customValues) {\r\n this.numberOfRows = customValues[0]\r\n this.framesPerRow = customValues[1]\r\n localStorage.setItem('customGridValues', JSON.stringify(customValues))\r\n await this.fInterface.changeSize(this.numberOfRows, this.framesPerRow)\r\n this.getFramesArray()\r\n this.$nextTick(this.resize)\r\n this.$emit('frames-format-changed', 11)\r\n },\r\n getFramesArray() {\r\n this.frames = this.fInterface.getFrames(Positions.current)\r\n this.nextFrames = this.fInterface.getFrames(Positions.next)\r\n this.previousFrames = this.fInterface.getFrames(Positions.previous)\r\n\r\n const div = document.createElement('div')\r\n div.innerHTML = this.frames[0].image\r\n const newAspectRatio =\r\n div.getElementsByTagName('img')[0]?.naturalWidth /\r\n div.getElementsByTagName('img')[0]?.naturalHeight\r\n\r\n this.aspectRatio = isNaN(newAspectRatio)\r\n ? this.aspectRatio\r\n : newAspectRatio\r\n\r\n this.$nextTick(this.resize)\r\n\r\n const frame = this.frames.find((f) => f.blockStart)\r\n if (frame && this.alternativeServer) {\r\n this.$emit(\r\n 'new-block',\r\n frame.title?.match(/[0-9]{3}\\/(?:[0-9]+_?)+/)[0],\r\n )\r\n }\r\n },\r\n async createFramesInterface(startTime = this.startAudienceTime) {\r\n this.frames = this.loadingArray\r\n // let ch = this.channel\r\n // let associationTMP = {\r\n // 1735073: 1,\r\n // 1735074: 139,\r\n // 1735075: 3,\r\n // 1735076: 132,\r\n // }\r\n // //\r\n // this.channelCode = associationTMP[ch] ? associationTMP[ch] : ch\r\n\r\n const t = startTime.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const time = Date.UTC(d.year, d.month, d.day, t[0], t[1], t[2]) / 1000\r\n // * iniciar slider\r\n this.blockStartTime = time\r\n this.fInterface = await new FramesInterface(\r\n this.channel,\r\n this.numberOfRows,\r\n this.framesPerRow,\r\n time,\r\n this.startAudienceTime,\r\n this.useCache,\r\n this.shiftFrames,\r\n )\r\n await this.fInterface.init()\r\n\r\n this.getFramesArray()\r\n\r\n const div = document.createElement('div')\r\n div.innerHTML = this.frames[0].image\r\n\r\n if (div.getElementsByTagName('img')[0]) {\r\n this.aspectRatio =\r\n div.getElementsByTagName('img')[0].naturalWidth /\r\n div.getElementsByTagName('img')[0].naturalHeight\r\n }\r\n\r\n this.activeFrame =\r\n this.getIndex(1, 0, Positions.current) + this.framesPerRow\r\n\r\n this.activeVideo = null\r\n },\r\n getIndex(rowNumber, frameIndex, position) {\r\n const baseOffset = this.framesPerRow * this.numberOfRows * position\r\n const rowOffset = (rowNumber - 1) * this.framesPerRow\r\n return baseOffset + rowOffset + frameIndex\r\n },\r\n getAudienceTime(frameTime, rowNumber, frameNumber, position) {\r\n if (!frameTime) {\r\n return 'Loading...'\r\n } else if (\r\n this.getIndex(rowNumber, frameNumber, position) === this.activeVideo\r\n ) {\r\n return this.convertToAudienceTime(this.videoTime)\r\n } else {\r\n return this.convertToAudienceTime(frameTime)\r\n }\r\n },\r\n giveMeType(frameTime) {\r\n if (!frameTime) {\r\n return ''\r\n }\r\n let time = this.convertToAudienceTime(frameTime)\r\n time = time.replace(/:/g, '')\r\n const foundEvent = this.eventsArray.find((event) => {\r\n return time >= event.hour_ini && time <= event.hour_end\r\n })\r\n if (foundEvent) {\r\n return `event-${foundEvent.type}`\r\n } else {\r\n return 'event-gap'\r\n }\r\n },\r\n dateInUtc(miliSeconds) {\r\n var date = new Date(miliSeconds)\r\n var utc = new Date(\r\n date.getUTCFullYear(),\r\n date.getUTCMonth(),\r\n date.getUTCDate(),\r\n date.getUTCHours(),\r\n date.getUTCMinutes(),\r\n date.getUTCSeconds(),\r\n )\r\n return utc\r\n },\r\n convertToAudienceTime(time, separator = ':') {\r\n const d = this.getDateParts()\r\n const limit = Date.UTC(d.year, d.month, d.day, 23, 59, 59) / 1000\r\n\r\n let hour = this.dateInUtc(time * 1000)\r\n .toTimeString()\r\n .split(' ')[0]\r\n .split(':')\r\n .map(Number)\r\n\r\n if (time > limit && time <= limit + this.startAudienceSeconds) {\r\n hour[0] = 24 + hour[0]\r\n }\r\n return hour\r\n .map((part) => (part > 9 ? part.toString() : '0' + part))\r\n .join(separator)\r\n },\r\n getDateParts(date = this.date) {\r\n const data = /(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})/.exec(\r\n date,\r\n )?.groups\r\n if (data) {\r\n return {\r\n year: parseInt(data.year),\r\n month: parseInt(data.month) - 1,\r\n day: parseInt(data.day),\r\n }\r\n }\r\n return {\r\n year: null,\r\n month: null,\r\n day: null,\r\n }\r\n },\r\n selectFrame(index, frame) {\r\n if (this.activeFrame !== index) {\r\n // Stop playing video if clicking on a different frame\r\n const array = this.$refs.frames.filter(\r\n (fc) => fc.videoStatus === fc.Status.playing,\r\n )\r\n if (array.length === 1) {\r\n const frame = array[0]\r\n frame.playOrPause()\r\n }\r\n this.activeVideo = null\r\n this.activeFrame = index\r\n }\r\n },\r\n toggleEvent(eventConfig, shortcutKey) {\r\n const currentFrame = this.$refs.frames?.find(\r\n (f) => f.index === this.activeFrame,\r\n )\r\n if (!currentFrame || !currentFrame.frame.time) return\r\n\r\n const frameTime = currentFrame.frame.time\r\n const eventType = eventConfig.type\r\n\r\n // Check if events are blocked (when type B is active outside Type A)\r\n if (this.eventBlocked && eventType !== 'B' && eventType !== 'C') {\r\n console.warn(\r\n 'Events are blocked. Close the active type B/C event first.',\r\n )\r\n return\r\n }\r\n\r\n if (eventType === 'A') {\r\n this.handleTypeA(frameTime, eventConfig, shortcutKey)\r\n } else if (eventType === 'B') {\r\n this.handleTypeB(frameTime, eventConfig, shortcutKey)\r\n } else if (eventType === 'C') {\r\n this.handleTypeC(frameTime, eventConfig, shortcutKey)\r\n }\r\n\r\n this.canInsertTime = this.hasAnyEvents()\r\n document.getElementById(`frame-${this.activeFrame}`).click()\r\n },\r\n handleTypeA(frameTime, eventConfig, shortcutKey) {\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n // Check if the shortcut matches the one used to start the event\r\n if (this.currentEventA.shortcutKey !== shortcutKey) {\r\n console.warn('Use the same shortcut key to close this event')\r\n return\r\n }\r\n // If clicking on the same frame where it started, remove the selection\r\n if (this.currentEventA.hour_ini === frameTime) {\r\n this.currentEventA = null\r\n delete this.activeEvents.A\r\n } else {\r\n // Close the event A\r\n this.currentEventA.hour_end = frameTime\r\n this.events.A.push({ ...this.currentEventA })\r\n this.currentEventA = null\r\n delete this.activeEvents.A\r\n }\r\n } else {\r\n // Check if clicking on hour_end of a completed event to reopen it\r\n const completedEvent = this.events.A.find(\r\n (e) => e.hour_end === frameTime && e.shortcutKey === shortcutKey,\r\n )\r\n if (completedEvent) {\r\n // Remove from completed events and reopen it\r\n const index = this.events.A.indexOf(completedEvent)\r\n this.events.A.splice(index, 1)\r\n this.currentEventA = {\r\n ...completedEvent,\r\n hour_end: null,\r\n }\r\n this.activeEvents.A = true\r\n return\r\n }\r\n\r\n // Start a new event A\r\n // Type A cannot be nested inside another Type A (already checked by currentEventA)\r\n this.currentEventA = {\r\n ...eventConfig,\r\n hour_ini: frameTime,\r\n hour_end: null,\r\n shortcutKey: shortcutKey,\r\n }\r\n this.activeEvents.A = true\r\n }\r\n },\r\n handleTypeB(frameTime, eventConfig, shortcutKey) {\r\n const lastEventB = this.events.B[this.events.B.length - 1]\r\n const lastEventC = this.events.C[this.events.C.length - 1]\r\n\r\n if (lastEventB && !lastEventB.hour_end) {\r\n // Check if the shortcut matches the one used to start the event\r\n if (lastEventB.shortcutKey !== shortcutKey) {\r\n console.warn('Use the same shortcut key to close this event')\r\n return\r\n }\r\n // If clicking on the same frame where it started, remove the selection\r\n if (lastEventB.hour_ini === frameTime) {\r\n this.events.B.pop() // Remove the last event\r\n delete this.activeEvents.B\r\n this.eventBlocked = false // Unblock events\r\n } else {\r\n // Close the event B\r\n lastEventB.hour_end = frameTime\r\n delete this.activeEvents.B\r\n this.eventBlocked = false // Unblock events\r\n }\r\n } else {\r\n // Check if clicking on hour_end of a completed event to reopen it\r\n const completedEvent = this.events.B.find(\r\n (e) => e.hour_end === frameTime && e.shortcutKey === shortcutKey,\r\n )\r\n if (completedEvent) {\r\n // Remove hour_end to reopen the event\r\n completedEvent.hour_end = null\r\n this.activeEvents.B = true\r\n if (!this.currentEventA) {\r\n this.eventBlocked = true\r\n }\r\n return\r\n }\r\n\r\n // Check if we're outside Type A and already have an unclosed B or C\r\n if (!this.currentEventA) {\r\n if (\r\n (lastEventB && !lastEventB.hour_end) ||\r\n (lastEventC && !lastEventC.hour_end)\r\n ) {\r\n console.warn(\r\n 'Only 1 B/C event allowed outside Type A. Close the current event first.',\r\n )\r\n return\r\n }\r\n // Outside Type A: block other events when B is active\r\n this.eventBlocked = true\r\n }\r\n\r\n // Auto-close any unclosed C event before starting B\r\n if (lastEventC && !lastEventC.hour_end) {\r\n lastEventC.hour_end = frameTime - 1\r\n delete this.activeEvents.C\r\n }\r\n\r\n // Start a new event B\r\n this.events.B.push({\r\n ...eventConfig,\r\n hour_ini: frameTime,\r\n hour_end: null,\r\n shortcutKey: shortcutKey,\r\n })\r\n this.activeEvents.B = true\r\n }\r\n },\r\n handleTypeC(frameTime, eventConfig, shortcutKey) {\r\n const lastEventC = this.events.C[this.events.C.length - 1]\r\n const lastEventB = this.events.B[this.events.B.length - 1]\r\n\r\n if (lastEventC && !lastEventC.hour_end) {\r\n // Check if the shortcut matches the one used to start the event\r\n if (lastEventC.shortcutKey !== shortcutKey) {\r\n console.warn('Use the same shortcut key to close this event')\r\n return\r\n }\r\n // If clicking on the same frame where it started, remove the selection\r\n if (lastEventC.hour_ini === frameTime) {\r\n this.events.C.pop() // Remove the last event\r\n delete this.activeEvents.C\r\n this.eventBlocked = false // Unblock events if it was outside Type A\r\n } else {\r\n // Close the event C\r\n lastEventC.hour_end = frameTime\r\n delete this.activeEvents.C\r\n this.eventBlocked = false // Unblock events if it was outside Type A\r\n }\r\n } else {\r\n // Check if clicking on hour_end of a completed event to reopen it\r\n const completedEvent = this.events.C.find(\r\n (e) => e.hour_end === frameTime && e.shortcutKey === shortcutKey,\r\n )\r\n if (completedEvent) {\r\n // Remove hour_end to reopen the event\r\n completedEvent.hour_end = null\r\n this.activeEvents.C = true\r\n if (!this.currentEventA) {\r\n this.eventBlocked = true\r\n }\r\n return\r\n }\r\n\r\n // Check if we're outside Type A and already have an unclosed B or C\r\n if (!this.currentEventA) {\r\n if (\r\n (lastEventB && !lastEventB.hour_end) ||\r\n (lastEventC && !lastEventC.hour_end)\r\n ) {\r\n console.warn(\r\n 'Only 1 B/C event allowed outside Type A. Close the current event first.',\r\n )\r\n return\r\n }\r\n // Outside Type A: block other events when C is active\r\n this.eventBlocked = true\r\n }\r\n\r\n // Auto-close any unclosed B event before starting C\r\n if (lastEventB && !lastEventB.hour_end) {\r\n lastEventB.hour_end = frameTime - 1\r\n delete this.activeEvents.B\r\n }\r\n\r\n // Start a new event C\r\n this.events.C.push({\r\n ...eventConfig,\r\n hour_ini: frameTime,\r\n hour_end: null,\r\n shortcutKey: shortcutKey,\r\n })\r\n this.activeEvents.C = true\r\n }\r\n },\r\n clearAllFrameSelections() {\r\n // Clear all events\r\n this.events.A = []\r\n this.events.B = []\r\n this.events.C = []\r\n this.currentEventA = null\r\n this.activeEvents = {}\r\n this.eventBlocked = false\r\n this.canInsertTime = false\r\n },\r\n hasAnyEvents() {\r\n return (\r\n this.events.A.length > 0 ||\r\n this.events.B.length > 0 ||\r\n this.events.C.length > 0 ||\r\n !!(this.currentEventA && this.currentEventA.hour_ini)\r\n )\r\n },\r\n isEventStart(frameTime) {\r\n if (!frameTime) return false\r\n\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n allEvents.push(this.currentEventA)\r\n }\r\n\r\n return allEvents.some((e) => e.hour_ini === frameTime)\r\n },\r\n isEventEnd(frameTime) {\r\n if (!frameTime) return false\r\n\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n return allEvents.some((e) => e.hour_end === frameTime)\r\n },\r\n isBetweenEvent(frameTime) {\r\n if (!frameTime) return false\r\n\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n // Only check completed events\r\n return allEvents.some((e) => {\r\n if (!e.hour_end) return false\r\n const start = e.hour_ini\r\n const end = e.hour_end\r\n return frameTime > start && frameTime < end\r\n })\r\n },\r\n isInEventType(frameTime, type) {\r\n if (!frameTime) return false\r\n\r\n let eventsOfType = this.events[type] || []\r\n\r\n // Only include completed events (with both start and end)\r\n return eventsOfType.some((e) => {\r\n if (!e.hour_end) return false // Event not completed yet\r\n const start = e.hour_ini\r\n const end = e.hour_end\r\n return frameTime >= start && frameTime <= end\r\n })\r\n },\r\n getEventType(frameTime) {\r\n if (!frameTime) return null\r\n\r\n // Check for incomplete (currently being marked) events first\r\n if (this.currentEventA && this.currentEventA.hour_ini === frameTime) {\r\n return 'A'\r\n }\r\n\r\n // Check for incomplete B or C events\r\n const lastEventB = this.events.B[this.events.B.length - 1]\r\n if (\r\n lastEventB &&\r\n !lastEventB.hour_end &&\r\n lastEventB.hour_ini === frameTime\r\n ) {\r\n return 'B'\r\n }\r\n\r\n const lastEventC = this.events.C[this.events.C.length - 1]\r\n if (\r\n lastEventC &&\r\n !lastEventC.hour_end &&\r\n lastEventC.hour_ini === frameTime\r\n ) {\r\n return 'C'\r\n }\r\n\r\n // Check in priority order: B, C, then A for completed events\r\n // This ensures nested events show their specific color\r\n if (this.isInEventType(frameTime, 'B')) return 'B'\r\n if (this.isInEventType(frameTime, 'C')) return 'C'\r\n if (this.isInEventType(frameTime, 'A')) return 'A'\r\n\r\n return null\r\n },\r\n //* Navegação\r\n arrowRight() {\r\n if (this.checkLimitRight(false)) {\r\n if (\r\n this.activeFrame ===\r\n this.numberOfRows * this.framesPerRow * 2 - 1\r\n ) {\r\n this.next()\r\n } else {\r\n this.activeFrame++\r\n }\r\n }\r\n },\r\n arrowLeft() {\r\n if (this.checkLimitLeft(false)) {\r\n if (this.activeFrame === this.numberOfRows * this.framesPerRow) {\r\n this.prev({ stayOnLast: true })\r\n } else {\r\n this.activeFrame--\r\n }\r\n }\r\n },\r\n arrowUp() {\r\n const relativePosition =\r\n this.activeFrame - this.numberOfRows * this.framesPerRow\r\n const currentRow = Math.floor(relativePosition / this.framesPerRow)\r\n if (currentRow > 0) {\r\n this.activeFrame -= this.framesPerRow\r\n }\r\n },\r\n\r\n arrowDown() {\r\n const relativePosition =\r\n this.activeFrame - this.numberOfRows * this.framesPerRow\r\n const currentRow = Math.floor(relativePosition / this.framesPerRow)\r\n\r\n if (currentRow < this.numberOfRows - 1) {\r\n this.activeFrame += this.framesPerRow\r\n }\r\n },\r\n checkLimitRight(value) {\r\n const hours = this.endAudienceTime.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const high = Date.UTC(\r\n d.year,\r\n d.month,\r\n d.day,\r\n hours[0],\r\n parseInt(hours[1]) + 30,\r\n hours[2],\r\n )\r\n\r\n if (value) {\r\n return (\r\n high >\r\n (this.fInterface.getCurrentTime() +\r\n this.numberOfRows * this.framesPerRow -\r\n 1) *\r\n 1000\r\n )\r\n } else {\r\n return high > this.fInterface.getCurrentTime() * 1000\r\n }\r\n },\r\n checkLimitLeft(value) {\r\n const hours = this.startAudienceTime.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const low = Date.UTC(d.year, d.month, d.day, hours[0], hours[1], hours[2])\r\n\r\n if (value) {\r\n return low <= (this.fInterface.getCurrentTime() - 1) * 1000\r\n } else {\r\n return (\r\n low <\r\n (this.fInterface.getCurrentTime() +\r\n this.activeFrame -\r\n this.numberOfRows * this.framesPerRow) *\r\n 1000\r\n )\r\n }\r\n },\r\n async next(config = {}) {\r\n if (\r\n (config.ignoreTime ||\r\n Date.now() - this.lastNext > this.swapImagesDelay) &&\r\n this.checkLimitRight(true) &&\r\n !this.navigationPending\r\n ) {\r\n this.navigationPending = true\r\n this.stopVideoPlaying()\r\n\r\n this.navigationPending = await new Promise((resolve) => {\r\n this.fInterface\r\n .loadNextFrames()\r\n .then(() => {\r\n this.activeFrame = this.getIndex(\r\n 1,\r\n this.framesPerRow,\r\n Positions.current,\r\n )\r\n\r\n this.getFramesArray()\r\n this.lastNext = Date.now()\r\n resolve(false)\r\n })\r\n .catch(() => {\r\n resolve(false)\r\n })\r\n })\r\n } else {\r\n //console.log('next blocked')\r\n }\r\n },\r\n async prev(config = {}) {\r\n if (\r\n (config.ignoreTime ||\r\n Date.now() - this.lastPrev > this.swapImagesDelay) &&\r\n this.checkLimitLeft(true) &&\r\n !this.navigationPending\r\n ) {\r\n this.navigationPending = true\r\n this.stopVideoPlaying()\r\n\r\n const hours = this.startAudienceTime.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const low = Date.UTC(\r\n d.year,\r\n d.month,\r\n d.day,\r\n hours[0],\r\n hours[1],\r\n hours[2],\r\n )\r\n\r\n this.navigationPending = await new Promise((resolve) => {\r\n this.fInterface\r\n .loadPrevFrames()\r\n .then(() => {\r\n if (config.stayOnLast) {\r\n this.activeFrame = this.getIndex(\r\n this.numberOfRows,\r\n this.framesPerRow - 1,\r\n Positions.current,\r\n )\r\n } else {\r\n if (\r\n this.fInterface.getCurrentTime() - low <\r\n this.numberOfRows * this.framesPerRow\r\n ) {\r\n this.setStartTime(this.startAudienceTime, false)\r\n }\r\n this.activeFrame = this.getIndex(1, 0, Positions.current)\r\n }\r\n\r\n this.getFramesArray()\r\n this.lastPrev = Date.now()\r\n resolve(false)\r\n })\r\n .catch(() => {\r\n resolve(false)\r\n })\r\n })\r\n } else {\r\n // console.error('prev blocked')\r\n }\r\n },\r\n\r\n hmsToSeconds(s) {\r\n const [hh, mm, ss] = s.match(/.{1,2}/g).map(Number)\r\n return hh * 3600 + mm * 60 + ss\r\n },\r\n\r\n secondsToHms(total) {\r\n const hh = String(Math.floor(total / 3600)).padStart(2, '0')\r\n const mm = String(Math.floor((total % 3600) / 60)).padStart(2, '0')\r\n const ss = String(total % 60).padStart(2, '0')\r\n return `${hh}${mm}${ss}`\r\n },\r\n\r\n async setStartTime(time, flag = true) {\r\n if (time.includes(':')) time = time.replace(/:/g, '')\r\n const d = this.getDateParts()\r\n const startSec = this.hmsToSeconds(this.startAudienceTime)\r\n const endSec = this.hmsToSeconds(this.endAudienceTime)\r\n const setSec = this.hmsToSeconds(time)\r\n\r\n const secondsBefore = this.framesPerRow + 1\r\n const gridSize = this.numberOfRows * this.framesPerRow\r\n const secondsAfter = gridSize - 1\r\n\r\n const desired = setSec - secondsBefore\r\n const maxStart = endSec - secondsAfter\r\n const clamp = (v, min, max) => Math.min(max, Math.max(min, v))\r\n\r\n let targetSec = clamp(desired, startSec, maxStart)\r\n\r\n if (desired < startSec) {\r\n targetSec = desired + this.framesPerRow\r\n } else if (desired > maxStart) {\r\n targetSec = maxStart\r\n } else {\r\n targetSec = desired + 1\r\n }\r\n\r\n targetSec = clamp(targetSec, startSec, endSec)\r\n const dayStartEpoch = Date.UTC(d.year, d.month, d.day, 0, 0, 0) / 1000\r\n const targetEpoch = dayStartEpoch + targetSec\r\n\r\n let finalTime = targetEpoch\r\n if (this.numberOfRows > 1 && flag) {\r\n finalTime = targetEpoch + this.framesPerRow\r\n }\r\n await this.fInterface.changeTime(finalTime, flag)\r\n\r\n this.getFramesArray()\r\n this.activeFrame =\r\n this.getIndex(1, 0, Positions.current) + this.framesPerRow\r\n this.activeVideo = null\r\n return true\r\n },\r\n hourToTimeStamp(time) {\r\n if (time.indexOf(':') !== -1) {\r\n time = time.replace(/:/g, '')\r\n }\r\n const t = time.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const setTime = Date.UTC(d.year, d.month, d.day, t[0], t[1], t[2]) / 1000\r\n\r\n return setTime\r\n },\r\n changeHour(value) {\r\n if (value) {\r\n return new Promise((resolve) => {\r\n setTimeout(async () => {\r\n this.stopVideoPlaying()\r\n\r\n await this.setStartTime(value, true)\r\n resolve()\r\n }, 0)\r\n })\r\n }\r\n },\r\n changeBlockInterval(value) {\r\n this.changeHour(value.ini)\r\n let time_ini, time_end\r\n time_ini = this.hourToTimeStamp(value.ini)\r\n time_end = this.hourToTimeStamp(value.end)\r\n this.videoSliderTotalDuration = time_end - time_ini\r\n this.$refs.frames[0].changeSettings(time_ini)\r\n this.blockStartTime = time_ini\r\n },\r\n //eslint-disable-next-line\r\n async updateVideoTime(index, videoTime) {\r\n this.activeVideo = index\r\n this.videoTime = videoTime\r\n },\r\n //eslint-disable-next-line\r\n updateVideoStatus(currentTime) {\r\n if (!this.progressVideoDrag) {\r\n // ESTA FUNÇÃO PASSOU PARA DENTRO DOS COMMANDS\r\n // this.updateProgress(null, currentTime)\r\n }\r\n },\r\n async startPlaying(frame, totalTime) {\r\n const array = this.$refs.frames.filter(\r\n (fc) => fc.frame.time !== frame.time,\r\n )\r\n this.stopVideoPlaying(array)\r\n\r\n this.videoTotalTime = totalTime\r\n this.videoPlaying = true\r\n },\r\n stopPlaying() {\r\n this.videoTotalTime = null\r\n this.videoPlaying = false\r\n this.paused = false\r\n },\r\n insertTime() {\r\n // Calculate the overall D event (lowest hour_ini to biggest hour_end)\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n allEvents.push(this.currentEventA)\r\n }\r\n\r\n if (allEvents.length === 0) {\r\n console.warn('No events to insert')\r\n return\r\n }\r\n\r\n const allTimes = allEvents.flatMap((e) =>\r\n [e.hour_ini, e.hour_end].filter(Boolean),\r\n )\r\n const lowestHourIni = Math.min(...allTimes)\r\n const biggestHourEnd = Math.max(...allTimes)\r\n\r\n // Get all B and C events (sorted by start time)\r\n const bcEvents = [...this.events.B, ...this.events.C]\r\n .filter((e) => e.hour_end) // Only complete events\r\n .sort((a, b) => a.hour_ini - b.hour_ini)\r\n\r\n // Generate A events to fill the gaps\r\n const generatedAEvents = []\r\n const standaloneBCEvents = []\r\n\r\n // Get all A events (both completed and current)\r\n const allAEvents = [...this.events.A]\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n allAEvents.push({\r\n ...this.currentEventA,\r\n hour_end: this.currentEventA.hour_end || biggestHourEnd,\r\n })\r\n }\r\n\r\n // Separate B/C events into those inside Type A and standalone\r\n for (const bcEvent of bcEvents) {\r\n const isInsideA = allAEvents.some(\r\n (aEvent) =>\r\n bcEvent.hour_ini >= aEvent.hour_ini &&\r\n bcEvent.hour_end <= aEvent.hour_end,\r\n )\r\n if (!isInsideA) {\r\n standaloneBCEvents.push(bcEvent)\r\n }\r\n }\r\n\r\n // For each A event, generate gap-filling A events around B/C events\r\n for (const aEvent of allAEvents) {\r\n const aStart = aEvent.hour_ini\r\n const aEnd = aEvent.hour_end\r\n\r\n // Get B/C events that are within this A event\r\n const bcEventsInA = bcEvents.filter(\r\n (bc) => bc.hour_ini >= aStart && bc.hour_end <= aEnd,\r\n )\r\n\r\n if (bcEventsInA.length === 0) {\r\n // No B or C events inside this A, keep the A event as is\r\n generatedAEvents.push({\r\n ...aEvent,\r\n hour_ini: aStart,\r\n hour_end: aEnd,\r\n })\r\n } else {\r\n let currentTime = aStart\r\n\r\n for (const bcEvent of bcEventsInA) {\r\n // Create A event before this B/C event\r\n if (currentTime < bcEvent.hour_ini) {\r\n generatedAEvents.push({\r\n ...aEvent,\r\n hour_ini: currentTime,\r\n hour_end: bcEvent.hour_ini - 1,\r\n })\r\n }\r\n currentTime = bcEvent.hour_end + 1\r\n }\r\n\r\n // Create final A event after last B/C event\r\n if (currentTime <= aEnd) {\r\n generatedAEvents.push({\r\n ...aEvent,\r\n hour_ini: currentTime,\r\n hour_end: aEnd,\r\n })\r\n }\r\n }\r\n }\r\n\r\n // Create D events for each manually marked A event\r\n // const dEvents = allAEvents.map((aEvent) => ({\r\n // type: 'D',\r\n // hour_ini: this.convertToAudienceTime(aEvent.hour_ini, ''),\r\n // hour_end: this.convertToAudienceTime(aEvent.hour_end, ''),\r\n // timestamp: aEvent.hour_ini, // For sorting\r\n // }))\r\n\r\n // Convert generated A events\r\n const aEventsFormatted = generatedAEvents.map((e) => {\r\n const formatted = {\r\n type: e.type,\r\n hour_ini: this.convertToAudienceTime(e.hour_ini, ''),\r\n hour_end: this.convertToAudienceTime(e.hour_end, ''),\r\n timestamp: e.hour_ini, // For sorting\r\n }\r\n // Add any extra properties from the original event\r\n Object.keys(e).forEach((key) => {\r\n if (!['type', 'hour_ini', 'hour_end', 'timestamp'].includes(key)) {\r\n formatted[key] = e[key]\r\n }\r\n })\r\n return formatted\r\n })\r\n\r\n // Convert B/C events inside Type A\r\n const bcEventsInsideA = bcEvents.filter((bcEvent) =>\r\n allAEvents.some(\r\n (aEvent) =>\r\n bcEvent.hour_ini >= aEvent.hour_ini &&\r\n bcEvent.hour_end <= aEvent.hour_end,\r\n ),\r\n )\r\n\r\n const bcEventsFormatted = bcEventsInsideA.map((e) => {\r\n const formatted = {\r\n type: e.type,\r\n hour_ini: this.convertToAudienceTime(e.hour_ini, ''),\r\n hour_end: this.convertToAudienceTime(e.hour_end, ''),\r\n timestamp: e.hour_ini, // For sorting\r\n }\r\n // Add any extra properties from the original event\r\n Object.keys(e).forEach((key) => {\r\n if (!['type', 'hour_ini', 'hour_end', 'timestamp'].includes(key)) {\r\n formatted[key] = e[key]\r\n }\r\n })\r\n return formatted\r\n })\r\n\r\n // Convert standalone B/C events (outside Type A)\r\n const standaloneBCFormatted = standaloneBCEvents.map((e) => {\r\n const formatted = {\r\n type: e.type,\r\n hour_ini: this.convertToAudienceTime(e.hour_ini, ''),\r\n hour_end: this.convertToAudienceTime(e.hour_end, ''),\r\n timestamp: e.hour_ini, // For sorting\r\n }\r\n // Add any extra properties from the original event\r\n Object.keys(e).forEach((key) => {\r\n if (!['type', 'hour_ini', 'hour_end', 'timestamp'].includes(key)) {\r\n formatted[key] = e[key]\r\n }\r\n })\r\n return formatted\r\n })\r\n\r\n // Combine all events and sort chronologically\r\n // D events come before A events when they have the same timestamp\r\n const allEventsToSend = [\r\n // ...dEvents,\r\n ...aEventsFormatted,\r\n ...bcEventsFormatted,\r\n ...standaloneBCFormatted,\r\n ].sort((a, b) => {\r\n if (a.timestamp === b.timestamp) {\r\n // If same timestamp, D comes before A, A comes before B/C\r\n const typeOrder = { D: 0, A: 1, B: 2, C: 2 }\r\n return typeOrder[a.type] - typeOrder[b.type]\r\n }\r\n return a.timestamp - b.timestamp\r\n })\r\n\r\n // Remove the timestamp field used for sorting\r\n const eventsToSend = allEventsToSend.map(\r\n ({ timestamp, ...event }) => event,\r\n )\r\n\r\n this.$emit('timeToInsert', {\r\n channel: this.channel,\r\n events: eventsToSend,\r\n force: false,\r\n })\r\n\r\n if (this.jumpOnInsert) {\r\n this.changeHour(this.convertToAudienceTime(biggestHourEnd)).then(() => {\r\n this.activeFrame = this.getIndex(2, 1, Positions.current)\r\n })\r\n }\r\n\r\n if (this.removeSelectionOnInsert) {\r\n this.events = {\r\n A: [],\r\n B: [],\r\n C: [],\r\n }\r\n this.currentEventA = null\r\n this.canInsertTime = false\r\n }\r\n },\r\n insertTimeForce() {\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n allEvents.push(this.currentEventA)\r\n }\r\n\r\n if (allEvents.length === 0) {\r\n console.warn('No events to insert')\r\n return\r\n }\r\n\r\n const allTimes = allEvents.flatMap((e) =>\r\n [e.hour_ini, e.hour_end].filter(Boolean),\r\n )\r\n const lowestHourIni = Math.min(...allTimes)\r\n const biggestHourEnd = Math.max(...allTimes)\r\n\r\n const eventsToSend = [\r\n // {\r\n // type: 'D',\r\n // hour_ini: this.convertToAudienceTime(lowestHourIni, ''),\r\n // hour_end: this.convertToAudienceTime(biggestHourEnd, ''),\r\n // },\r\n ...allEvents\r\n .filter((e) => e.hour_end)\r\n .map((e) => ({\r\n type: e.type,\r\n hour_ini: this.convertToAudienceTime(e.hour_ini, ''),\r\n hour_end: this.convertToAudienceTime(e.hour_end, ''),\r\n })),\r\n ]\r\n\r\n this.$emit('timeToInsert', {\r\n channel: this.channel,\r\n events: eventsToSend,\r\n force: true,\r\n })\r\n\r\n if (this.removeSelectionOnInsert) {\r\n this.events = {\r\n A: [],\r\n B: [],\r\n C: [],\r\n }\r\n this.currentEventA = null\r\n this.canInsertTime = false\r\n }\r\n },\r\n async getChannelMedia() {\r\n // this.media = (await ChannelService.show(this.channel)).data.MEDIA\r\n },\r\n async changeServerClick() {\r\n this.changeServer = !this.changeServer\r\n this.alternativeServer = this.changeServer\r\n\r\n sessionStorage.setItem(\r\n 'server',\r\n this.changeServer ? 'alternative' : 'default',\r\n )\r\n\r\n await this.createFramesInterface(\r\n this.convertToAudienceTime(\r\n this.$refs.frames.find((frame) => frame.index === this.activeFrame)\r\n .frame.time,\r\n '',\r\n ),\r\n )\r\n\r\n this.$nextTick(this.resize)\r\n },\r\n },\r\n computed: {\r\n swapImagesDelay() {\r\n return 0\r\n // return this.numberOfRows * this.framesPerRow * 15\r\n },\r\n active: {\r\n get() {\r\n return this.value\r\n },\r\n set(value) {\r\n this.$emit('input', value)\r\n },\r\n },\r\n settingsClosed() {\r\n return !Object.values(this.dialogs).find((v) => v)\r\n },\r\n startAudienceSeconds() {\r\n const t = this.startAudienceTime.match(/.{1,2}/g)\r\n return parseInt(t[0] * 3600 + t[1] * 60 + t[2])\r\n },\r\n loadingArray() {\r\n return Array.from(Array(this.numberOfRows * this.framesPerRow).keys())\r\n },\r\n serverOfFrames() {\r\n return sessionStorage.getItem('server')\r\n },\r\n },\r\n beforeDestroy() {\r\n this.active = false\r\n document.removeEventListener('wheel', this.scrollEvent)\r\n document.removeEventListener('keydown', this.handleKeydown)\r\n sessionStorage.setItem('server', 'default')\r\n },\r\n watch: {\r\n async secondsPerFrame() {\r\n const activeF =\r\n this.frames[this.activeFrame - this.numberOfRows * this.framesPerRow]\r\n\r\n if (activeF) {\r\n this.changeHour(this.getAudienceTime(activeF.time, 0, 0, 0))\r\n this.fInterface.setCurrentStep(this.secondsPerFrame)\r\n await this.fInterface.loadFrames()\r\n this.getFramesArray()\r\n }\r\n },\r\n async shiftFrames() {\r\n this.createFramesInterface()\r\n },\r\n framesFormat(value) {\r\n this.setFrameSelection(value)\r\n },\r\n active() {\r\n console.log('active change')\r\n // Reset events when switching tabs\r\n if (this.removeSelectionOnInsert) {\r\n this.events = {\r\n A: [],\r\n B: [],\r\n C: [],\r\n }\r\n this.currentEventA = null\r\n }\r\n },\r\n useCache() {\r\n this.createFramesInterface()\r\n },\r\n activeFrame(value) {\r\n if (value) {\r\n this.stopPlayingBar()\r\n }\r\n },\r\n channel() {\r\n this.updatingChannel = new Promise((resolve, reject) => {\r\n try {\r\n this.createFramesInterface()\r\n resolve(true)\r\n } catch (err) {\r\n reject(err)\r\n }\r\n })\r\n },\r\n stretchFrame() {\r\n this.$nextTick(this.resize)\r\n },\r\n visualizationInsArray: {\r\n handler: function (newValue) {\r\n this.eventsArray = newValue\r\n //console.log('visualizationInsArray changed', newValue)\r\n },\r\n deep: true,\r\n },\r\n },\r\n}\r\n</script>\r\n<style scoped>\r\n.visualization-row {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex: 1 1 auto;\r\n}\r\n\r\n.visualization-col {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 5px;\r\n}\r\n\r\n.visualization-divider {\r\n display: block;\r\n flex: 1 1 100%;\r\n height: 0px;\r\n max-height: 0px;\r\n opacity: 1;\r\n transition: inherit;\r\n border-style: solid;\r\n border-width: thin 0 0 0;\r\n border-color: rgba(0, 0, 0, 0.12);\r\n margin: 0;\r\n}\r\n\r\n.visualization-divider.vertical {\r\n align-self: stretch;\r\n border-width: 0 thin 0 0;\r\n display: inline-flex;\r\n height: inherit;\r\n margin-left: -1px;\r\n max-height: 100%;\r\n max-width: 0px;\r\n vertical-align: text-bottom;\r\n width: 0px;\r\n}\r\n\r\n.visualization-card {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 12px;\r\n width: 100%;\r\n transition-property: box-shadow, opacity, -webkit-box-shadow;\r\n overflow-wrap: break-word;\r\n /*box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),\r\n 0 1px 5px 0 rgba(0, 0, 0, 0.12);*/\r\n}\r\n\r\n.visualization-justify-center,\r\n* >>> .visualization-justify-center {\r\n justify-content: center;\r\n}\r\n\r\n.visualization-align-center {\r\n align-items: center;\r\n}\r\n\r\n#visualization-container {\r\n max-width: 100% !important;\r\n margin: 0 auto !important;\r\n height: 100%;\r\n border-bottom: none;\r\n}\r\n#visualization-container > .card {\r\n border-radius: 0 !important;\r\n font-size: 12px;\r\n width: 100%;\r\n box-shadow: none;\r\n height: 100%;\r\n}\r\n\r\n#command-bar,\r\n#info-bar {\r\n background-color: #f5f5f5;\r\n box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),\r\n 0 1px 5px 0 rgba(0, 0, 0, 0.12);\r\n}\r\n#command-bar button {\r\n width: 42px;\r\n height: 36px;\r\n border: none;\r\n background: none;\r\n}\r\n\r\n#command-bar button:hover {\r\n cursor: pointer;\r\n background: rgba(0, 0, 0, 0.12);\r\n}\r\n\r\n#command-bar svg {\r\n font-size: 16px;\r\n}\r\n\r\n#command-bar {\r\n padding: 0 !important;\r\n}\r\n\r\n#info-bar {\r\n padding: 4px;\r\n font-size: 14px;\r\n position: relative;\r\n}\r\n\r\n.settings-container {\r\n position: absolute;\r\n right: 14px;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n}\r\n\r\n.settings-container > * {\r\n margin: 0 2px;\r\n cursor: pointer;\r\n}\r\n\r\n#info-bar svg {\r\n font-size: 16px;\r\n}\r\n\r\n#info-bar .divider {\r\n margin: 0 8px;\r\n}\r\n\r\nsvg:focus {\r\n border: none;\r\n}\r\n\r\n.visualization-card {\r\n border-left: 8px solid #eee;\r\n}\r\n\r\n.active-tab {\r\n border-left: 8px solid var(--visualization-primary) !important;\r\n border-image-slice: 1;\r\n}\r\n\r\n[id^='frame-'] {\r\n padding: 1px;\r\n display: flex;\r\n flex-flow: column;\r\n}\r\n\r\n.tooltip {\r\n display: block !important;\r\n z-index: 10000;\r\n}\r\n\r\n.tooltip .tooltip-inner {\r\n background: var(--visualization-primary);\r\n color: white;\r\n border-radius: 16px;\r\n padding: 5px 10px 4px;\r\n}\r\n\r\n.tooltip .tooltip-arrow {\r\n width: 0;\r\n height: 0;\r\n border-style: solid;\r\n position: absolute;\r\n margin: 5px;\r\n border-color: var(--visualization-primary);\r\n z-index: 1;\r\n}\r\n\r\n.tooltip[x-placement^='top'] {\r\n margin-bottom: 5px;\r\n}\r\n\r\n.tooltip[x-placement^='top'] .tooltip-arrow {\r\n border-width: 5px 5px 0 5px;\r\n border-left-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n bottom: -5px;\r\n left: calc(50% - 5px);\r\n margin-top: 0;\r\n margin-bottom: 0;\r\n}\r\n\r\n.tooltip[x-placement^='bottom'] {\r\n margin-top: 5px;\r\n}\r\n\r\n.tooltip[x-placement^='bottom'] .tooltip-arrow {\r\n border-width: 0 5px 5px 5px;\r\n border-left-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-top-color: transparent !important;\r\n top: -5px;\r\n left: calc(50% - 5px);\r\n margin-top: 0;\r\n margin-bottom: 0;\r\n}\r\n\r\n.tooltip[x-placement^='right'] {\r\n margin-left: 5px;\r\n}\r\n\r\n.tooltip[x-placement^='right'] .tooltip-arrow {\r\n border-width: 5px 5px 5px 0;\r\n border-left-color: transparent !important;\r\n border-top-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n left: -5px;\r\n top: calc(50% - 5px);\r\n margin-left: 0;\r\n margin-right: 0;\r\n}\r\n\r\n.tooltip[x-placement^='left'] {\r\n margin-right: 5px;\r\n}\r\n\r\n.tooltip[x-placement^='left'] .tooltip-arrow {\r\n border-width: 5px 0 5px 5px;\r\n border-top-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n right: -5px;\r\n top: calc(50% - 5px);\r\n margin-left: 0;\r\n margin-right: 0;\r\n}\r\n\r\n.tooltip.popover .popover-inner {\r\n background: #f9f9f9;\r\n color: black;\r\n padding: 24px;\r\n border-radius: 5px;\r\n box-shadow: 0 5px 30px rgba(black, 0.1);\r\n}\r\n\r\n.tooltip.popover .popover-arrow {\r\n border-color: #f9f9f9;\r\n}\r\n\r\n.tooltip[aria-hidden='true'] {\r\n visibility: hidden;\r\n opacity: 0;\r\n transition: opacity 0.15s, visibility 0.15s;\r\n}\r\n\r\n.tooltip[aria-hidden='false'] {\r\n visibility: visible;\r\n opacity: 1;\r\n transition: opacity 0.15s;\r\n}\r\n\r\n.custom-frametime {\r\n font-size: smaller;\r\n}\r\n\r\n/* DAR CORES AOS TEMPOS DEPENDENDO DO TIPO DE EVENTO */\r\n.event-A {\r\n color: #000; /* black for A */\r\n}\r\n.event-B {\r\n color: rgb(0, 115, 230); /* blue for B */\r\n}\r\n.event-C {\r\n color: rgb(0, 179, 0); /* green for C */\r\n}\r\n.event-gap {\r\n color: #555; /* Default color for gaps */\r\n}\r\n</style>\r\n"]}, media: undefined });
6317
+ inject("data-v-452faed6_0", { source: "\n.visualization-row[data-v-452faed6] {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex: 1 1 auto;\n}\n.visualization-col[data-v-452faed6] {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 5px;\n}\n.visualization-divider[data-v-452faed6] {\r\n display: block;\r\n flex: 1 1 100%;\r\n height: 0px;\r\n max-height: 0px;\r\n opacity: 1;\r\n transition: inherit;\r\n border-style: solid;\r\n border-width: thin 0 0 0;\r\n border-color: rgba(0, 0, 0, 0.12);\r\n margin: 0;\n}\n.visualization-divider.vertical[data-v-452faed6] {\r\n align-self: stretch;\r\n border-width: 0 thin 0 0;\r\n display: inline-flex;\r\n height: inherit;\r\n margin-left: -1px;\r\n max-height: 100%;\r\n max-width: 0px;\r\n vertical-align: text-bottom;\r\n width: 0px;\n}\n.visualization-card[data-v-452faed6] {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 12px;\r\n width: 100%;\r\n transition-property: box-shadow, opacity, -webkit-box-shadow;\r\n overflow-wrap: break-word;\r\n /*box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),\r\n 0 1px 5px 0 rgba(0, 0, 0, 0.12);*/\n}\n.visualization-justify-center[data-v-452faed6],\r\n*[data-v-452faed6] .visualization-justify-center {\r\n justify-content: center;\n}\n.visualization-align-center[data-v-452faed6] {\r\n align-items: center;\n}\n#visualization-container[data-v-452faed6] {\r\n max-width: 100% !important;\r\n margin: 0 auto !important;\r\n height: 100%;\r\n border-bottom: none;\n}\n#visualization-container > .card[data-v-452faed6] {\r\n border-radius: 0 !important;\r\n font-size: 12px;\r\n width: 100%;\r\n box-shadow: none;\r\n height: 100%;\n}\n#command-bar[data-v-452faed6],\r\n#info-bar[data-v-452faed6] {\r\n background-color: #f5f5f5;\r\n box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),\r\n 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n}\n#command-bar button[data-v-452faed6] {\r\n width: 42px;\r\n height: 36px;\r\n border: none;\r\n background: none;\n}\n#command-bar button[data-v-452faed6]:hover {\r\n cursor: pointer;\r\n background: rgba(0, 0, 0, 0.12);\n}\n#command-bar svg[data-v-452faed6] {\r\n font-size: 16px;\n}\n#command-bar[data-v-452faed6] {\r\n padding: 0 !important;\n}\n#info-bar[data-v-452faed6] {\r\n padding: 4px;\r\n font-size: 14px;\r\n position: relative;\n}\n.settings-container[data-v-452faed6] {\r\n position: absolute;\r\n right: 14px;\r\n top: 50%;\r\n transform: translateY(-50%);\n}\n.settings-container > *[data-v-452faed6] {\r\n margin: 0 2px;\r\n cursor: pointer;\n}\n#info-bar svg[data-v-452faed6] {\r\n font-size: 16px;\n}\n#info-bar .divider[data-v-452faed6] {\r\n margin: 0 8px;\n}\nsvg[data-v-452faed6]:focus {\r\n border: none;\n}\n.visualization-card[data-v-452faed6] {\r\n border-left: 8px solid #eee;\n}\n.active-tab[data-v-452faed6] {\r\n border-left: 8px solid var(--visualization-primary) !important;\r\n border-image-slice: 1;\n}\n[id^='frame-'][data-v-452faed6] {\r\n padding: 1px;\r\n display: flex;\r\n flex-flow: column;\n}\n.tooltip[data-v-452faed6] {\r\n display: block !important;\r\n z-index: 10000;\n}\n.tooltip .tooltip-inner[data-v-452faed6] {\r\n background: var(--visualization-primary);\r\n color: white;\r\n border-radius: 16px;\r\n padding: 5px 10px 4px;\n}\n.tooltip .tooltip-arrow[data-v-452faed6] {\r\n width: 0;\r\n height: 0;\r\n border-style: solid;\r\n position: absolute;\r\n margin: 5px;\r\n border-color: var(--visualization-primary);\r\n z-index: 1;\n}\n.tooltip[x-placement^='top'][data-v-452faed6] {\r\n margin-bottom: 5px;\n}\n.tooltip[x-placement^='top'] .tooltip-arrow[data-v-452faed6] {\r\n border-width: 5px 5px 0 5px;\r\n border-left-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n bottom: -5px;\r\n left: calc(50% - 5px);\r\n margin-top: 0;\r\n margin-bottom: 0;\n}\n.tooltip[x-placement^='bottom'][data-v-452faed6] {\r\n margin-top: 5px;\n}\n.tooltip[x-placement^='bottom'] .tooltip-arrow[data-v-452faed6] {\r\n border-width: 0 5px 5px 5px;\r\n border-left-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-top-color: transparent !important;\r\n top: -5px;\r\n left: calc(50% - 5px);\r\n margin-top: 0;\r\n margin-bottom: 0;\n}\n.tooltip[x-placement^='right'][data-v-452faed6] {\r\n margin-left: 5px;\n}\n.tooltip[x-placement^='right'] .tooltip-arrow[data-v-452faed6] {\r\n border-width: 5px 5px 5px 0;\r\n border-left-color: transparent !important;\r\n border-top-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n left: -5px;\r\n top: calc(50% - 5px);\r\n margin-left: 0;\r\n margin-right: 0;\n}\n.tooltip[x-placement^='left'][data-v-452faed6] {\r\n margin-right: 5px;\n}\n.tooltip[x-placement^='left'] .tooltip-arrow[data-v-452faed6] {\r\n border-width: 5px 0 5px 5px;\r\n border-top-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n right: -5px;\r\n top: calc(50% - 5px);\r\n margin-left: 0;\r\n margin-right: 0;\n}\n.tooltip.popover .popover-inner[data-v-452faed6] {\r\n background: #f9f9f9;\r\n color: black;\r\n padding: 24px;\r\n border-radius: 5px;\r\n box-shadow: 0 5px 30px rgba(black, 0.1);\n}\n.tooltip.popover .popover-arrow[data-v-452faed6] {\r\n border-color: #f9f9f9;\n}\n.tooltip[aria-hidden='true'][data-v-452faed6] {\r\n visibility: hidden;\r\n opacity: 0;\r\n transition: opacity 0.15s, visibility 0.15s;\n}\n.tooltip[aria-hidden='false'][data-v-452faed6] {\r\n visibility: visible;\r\n opacity: 1;\r\n transition: opacity 0.15s;\n}\n.custom-frametime[data-v-452faed6] {\r\n font-size: smaller;\n}\r\n\r\n/* DAR CORES AOS TEMPOS DEPENDENDO DO TIPO DE EVENTO */\n.event-A[data-v-452faed6] {\r\n color: #000; /* black for A */\n}\n.event-B[data-v-452faed6] {\r\n color: rgb(0, 115, 230); /* blue for B */\n}\n.event-C[data-v-452faed6] {\r\n color: rgb(0, 179, 0); /* green for C */\n}\n.event-gap[data-v-452faed6] {\r\n color: #555; /* Default color for gaps */\n}\r\n", map: {"version":3,"sources":["C:\\Workspace\\visualization\\src\\Visualization.vue"],"names":[],"mappings":";AA24DA;EACA,aAAA;EACA,eAAA;EACA,cAAA;AACA;AAEA;EACA,aAAA;EACA,YAAA;EACA,eAAA;EACA,YAAA;AACA;AAEA;EACA,cAAA;EACA,cAAA;EACA,WAAA;EACA,eAAA;EACA,UAAA;EACA,mBAAA;EACA,mBAAA;EACA,wBAAA;EACA,iCAAA;EACA,SAAA;AACA;AAEA;EACA,mBAAA;EACA,wBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,cAAA;EACA,2BAAA;EACA,UAAA;AACA;AAEA;EACA,aAAA;EACA,YAAA;EACA,eAAA;EACA,aAAA;EACA,WAAA;EACA,4DAAA;EACA,yBAAA;EACA;qCACA;AACA;AAEA;;EAEA,uBAAA;AACA;AAEA;EACA,mBAAA;AACA;AAEA;EACA,0BAAA;EACA,yBAAA;EACA,YAAA;EACA,mBAAA;AACA;AACA;EACA,2BAAA;EACA,eAAA;EACA,WAAA;EACA,gBAAA;EACA,YAAA;AACA;AAEA;;EAEA,yBAAA;EACA;mCACA;AACA;AACA;EACA,WAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;AACA;AAEA;EACA,eAAA;EACA,+BAAA;AACA;AAEA;EACA,eAAA;AACA;AAEA;EACA,qBAAA;AACA;AAEA;EACA,YAAA;EACA,eAAA;EACA,kBAAA;AACA;AAEA;EACA,kBAAA;EACA,WAAA;EACA,QAAA;EACA,2BAAA;AACA;AAEA;EACA,aAAA;EACA,eAAA;AACA;AAEA;EACA,eAAA;AACA;AAEA;EACA,aAAA;AACA;AAEA;EACA,YAAA;AACA;AAEA;EACA,2BAAA;AACA;AAEA;EACA,8DAAA;EACA,qBAAA;AACA;AAEA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;AACA;AAEA;EACA,yBAAA;EACA,cAAA;AACA;AAEA;EACA,wCAAA;EACA,YAAA;EACA,mBAAA;EACA,qBAAA;AACA;AAEA;EACA,QAAA;EACA,SAAA;EACA,mBAAA;EACA,kBAAA;EACA,WAAA;EACA,0CAAA;EACA,UAAA;AACA;AAEA;EACA,kBAAA;AACA;AAEA;EACA,2BAAA;EACA,yCAAA;EACA,0CAAA;EACA,2CAAA;EACA,YAAA;EACA,qBAAA;EACA,aAAA;EACA,gBAAA;AACA;AAEA;EACA,eAAA;AACA;AAEA;EACA,2BAAA;EACA,yCAAA;EACA,0CAAA;EACA,wCAAA;EACA,SAAA;EACA,qBAAA;EACA,aAAA;EACA,gBAAA;AACA;AAEA;EACA,gBAAA;AACA;AAEA;EACA,2BAAA;EACA,yCAAA;EACA,wCAAA;EACA,2CAAA;EACA,UAAA;EACA,oBAAA;EACA,cAAA;EACA,eAAA;AACA;AAEA;EACA,iBAAA;AACA;AAEA;EACA,2BAAA;EACA,wCAAA;EACA,0CAAA;EACA,2CAAA;EACA,WAAA;EACA,oBAAA;EACA,cAAA;EACA,eAAA;AACA;AAEA;EACA,mBAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,uCAAA;AACA;AAEA;EACA,qBAAA;AACA;AAEA;EACA,kBAAA;EACA,UAAA;EACA,2CAAA;AACA;AAEA;EACA,mBAAA;EACA,UAAA;EACA,yBAAA;AACA;AAEA;EACA,kBAAA;AACA;;AAEA,sDAAA;AACA;EACA,WAAA,EAAA,gBAAA;AACA;AACA;EACA,uBAAA,EAAA,eAAA;AACA;AACA;EACA,qBAAA,EAAA,gBAAA;AACA;AACA;EACA,WAAA,EAAA,2BAAA;AACA","file":"Visualization.vue","sourcesContent":["<template>\r\n <div\r\n class=\"visualization-row\"\r\n id=\"visualization-container\"\r\n @click=\"framesClicked\"\r\n @scroll=\"scrollEvent\"\r\n >\r\n <GlobalEvents\r\n v-if=\"!readOnly && canInsertTime && settingsClosed\"\r\n :filter=\"(event) => !event.shiftKey && !event.ctrlKey\"\r\n @keydown.45=\"insertTime\"\r\n />\r\n <!-- <GlobalEvents\r\n v-if=\"!readOnly && active && settingsClosed\"\r\n @keydown.113.prevent=\"toggleEventA\"\r\n @keydown.114.prevent=\"toggleEventC\"\r\n @keydown.116.prevent=\"toggleEventB\" /> -->\r\n <GlobalEvents\r\n v-if=\"active && settingsClosed\"\r\n @keydown.46.prevent=\"clearAllFrameSelections\"\r\n @keydown.left.prevent=\"arrowLeft\"\r\n @keydown.right.prevent=\"arrowRight\"\r\n @keydown.up.prevent=\"arrowUp\"\r\n @keydown.down.prevent=\"arrowDown\"\r\n @keydown.shift.page-down.prevent=\"nextLoopActivate\"\r\n @keydown.page-down.prevent=\"() => next()\"\r\n @keydown.page-up.prevent=\"() => prev()\"\r\n @keydown.shift.page-up.prevent=\"prevLoopActivate\"\r\n @keydown.36.prevent=\"goToFirstFrame\"\r\n @keydown.35.prevent=\"goToLastFrame\"\r\n @keydown.71.prevent=\"dialogs.goTo = true\"\r\n @keydown.73.prevent=\"dialogs.secondsPerFrame = true\"\r\n @keydown.76.prevent=\"dialogs.frames = true\"\r\n @keydown.49.97=\"() => (secondsPerFrame = 1)\"\r\n @keydown.50.98=\"() => (secondsPerFrame = 2)\"\r\n @keydown.51.99=\"() => (secondsPerFrame = 3)\"\r\n @keydown.52.100=\"() => (secondsPerFrame = 4)\"\r\n @keydown.53.101=\"() => (secondsPerFrame = 5)\"\r\n />\r\n <GlobalEvents\r\n v-if=\"prevLoop || nextLoop\"\r\n @keydown=\"breakLoop\"\r\n v-on:click=\"breakLoop\"\r\n />\r\n <settings\r\n v-if=\"active\"\r\n :dialogs-visibility=\"dialogs\"\r\n :playback-rate=\"playbackRate\"\r\n :seconds-per-frame=\"secondsPerFrame\"\r\n :frames-per-row=\"framesPerRow\"\r\n :number-of-rows=\"numberOfRows\"\r\n :max-steps=\"maxSteps\"\r\n :shift-frames=\"shiftFrames\"\r\n :customGridSize=\"customGridSize\"\r\n :stretchFrames=\"stretchFrame\"\r\n @change-playback-rate=\"(value) => (playbackRate = value)\"\r\n @change-go-to=\"changeHour\"\r\n @change-seconds-per-frame=\"(value) => (secondsPerFrame = value)\"\r\n @change-shift-frames=\"(value) => (shiftFrames = value)\"\r\n @set-frames-selection=\"setFrameSelection\"\r\n @set-custom-grid-values=\"setCustomGridValues\"\r\n @close=\"(dialog) => (dialogs[dialog] = false)\"\r\n />\r\n <div\r\n :class=\"{ 'visualization-card': true, 'active-tab': active }\"\r\n style=\"width: 100%; padding: 0\"\r\n >\r\n <command-bar\r\n v-show=\"commandBarShow\"\r\n :read-only=\"readOnly\"\r\n :video-playing=\"videoPlaying\"\r\n :video-paused=\"paused\"\r\n :insert-time=\"canInsertTime\"\r\n @prev-loop-activate=\"prevLoopActivate\"\r\n @next-loop-activate=\"nextLoopActivate\"\r\n @prev=\"prev\"\r\n @next=\"next\"\r\n @go-to=\"dialogs.goTo = true\"\r\n @open-frame-selection=\"dialogs.frames = true\"\r\n @open-frames-per-second=\"dialogs.secondsPerFrame = true\"\r\n @open-blocks=\"openBlocks\"\r\n @open-playback-rate=\"dialogs.playbackRate = true\"\r\n @play-or-pause=\"playOrPause\"\r\n @stop-playing=\"stopPlayingBar\"\r\n @insert-time=\"insertTime\"\r\n @shift-frames=\"dialogs.shiftFrames = true\"\r\n />\r\n <video-progress\r\n v-if=\"videoProgressBar\"\r\n v-show=\"videoPlaying\"\r\n :video-time=\"videoTime\"\r\n />\r\n <info-bar\r\n :playback-rate=\"playbackRate\"\r\n :seconds-per-frame=\"secondsPerFrame\"\r\n :commands-show=\"commandBarShow\"\r\n :cache=\"useCache\"\r\n :block-start-time=\"blockStartTime\"\r\n :video-total-duration=\"videoSliderTotalDuration\"\r\n :alternative-server=\"alternativeServer\"\r\n :frames-per-row=\"framesPerRow\"\r\n :number-of-rows=\"numberOfRows\"\r\n :show-stretch-frame=\"showStretchFrame\"\r\n @toogle-commands-visibility=\"toogleCommandsVisibility\"\r\n @toogle-cache=\"useCache = !useCache\"\r\n @change-server=\"changeServerClick\"\r\n @update-stretch-frames=\"(value) => (stretchFrame = value)\"\r\n />\r\n <div\r\n class=\"visualization-row\"\r\n v-for=\"rowNumber in numberOfRows\"\r\n :id=\"'row-' + rowNumber\"\r\n :key=\"'row-' + rowNumber\"\r\n style=\"padding: 0px 4px\"\r\n >\r\n <div\r\n v-for=\"(frame, frameNumber) in previousFrames\"\r\n :key=\"\r\n numberOfRows +\r\n '-' +\r\n framesPerRow +\r\n '-' +\r\n getIndex(rowNumber, frameNumber, Positions.previous)\r\n \"\r\n style=\"display: none\"\r\n >\r\n <span v-html=\"frame.img\" />\r\n </div>\r\n <div\r\n v-for=\"(frame, frameNumber) in nextFrames\"\r\n :key=\"\r\n numberOfRows +\r\n '-' +\r\n framesPerRow +\r\n '-' +\r\n getIndex(rowNumber, frameNumber, Positions.next)\r\n \"\r\n style=\"display: none\"\r\n >\r\n <span v-html=\"frame.img\" />\r\n </div>\r\n <div\r\n class=\"visualization-col\"\r\n v-for=\"(frame, frameNumber) in frames.slice(\r\n framesPerRow * (rowNumber - 1),\r\n framesPerRow * rowNumber,\r\n )\"\r\n :key=\"'row-' + rowNumber + '-frame-' + frameNumber + '-' + frame.time\"\r\n :id=\"`frame-${getIndex(rowNumber, frameNumber, Positions.current)}`\"\r\n :class=\"{ loaderImg: !!frame.img }\"\r\n @click=\"\r\n frame.time\r\n ? selectFrame(\r\n getIndex(rowNumber, frameNumber, Positions.current),\r\n frame,\r\n )\r\n : null\r\n \"\r\n >\r\n <span\r\n :class=\"giveMeType(frame.time)\"\r\n :id=\"activeFrame ? 'aa' : 0\"\r\n style=\"text-align: center\"\r\n >\r\n <b>\r\n {{\r\n getAudienceTime(\r\n frame.time,\r\n rowNumber,\r\n frameNumber,\r\n Positions.current,\r\n )\r\n }}\r\n </b>\r\n </span>\r\n\r\n <frame\r\n ref=\"frames\"\r\n :frame=\"frame\"\r\n :index=\"getIndex(rowNumber, frameNumber, Positions.current)\"\r\n :grid-settings=\"{ numberOfRows, framesPerRow }\"\r\n :initialTime=\"isEventStart(frame.time)\"\r\n :endTime=\"isEventEnd(frame.time)\"\r\n :checkpointTime=\"false\"\r\n :betweenTime=\"isBetweenEvent(frame.time)\"\r\n :eventType=\"getEventType(frame.time)\"\r\n :active=\"\r\n getIndex(rowNumber, frameNumber, Positions.current) ===\r\n activeFrame\r\n \"\r\n :activeTab=\"active\"\r\n :videoUrl=\"fInterface ? fInterface.getVideoUrl(frame) : ''\"\r\n :videoControls=\"videoControls\"\r\n @startPlaying=\"startPlaying\"\r\n @stopPlaying=\"stopPlaying\"\r\n @playPauseStatus=\"changePlayPause\"\r\n @updateSlider=\"updateSlider\"\r\n :playback-rate=\"playbackRate\"\r\n :aspect-ratio=\"aspectRatio\"\r\n :stretchFrame=\"stretchFrame\"\r\n style=\"margin: 0 auto\"\r\n ></frame>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- <settings\r\n ref=\"settings2\"\r\n :active=\"active\"\r\n @goToTime=\"changeHour\"\r\n @goToBlockInterval=\"changeBlockInterval\"\r\n @setSplitTime=\"setSplitTime\"\r\n @setFrameSelection=\"setFrameSelection\"\r\n @setPlaybackRate=\"\r\n (rate) => {\r\n playbackRate = rate\r\n }\r\n \"\r\n >\r\n </settings> -->\r\n <!-- <v-dialog v-model=\"dialog\" width=\"500\">\r\n <div class=\"card\">\r\n <div class=\"card\"-title class=\"text-h5 grey lighten-2\">\r\n {{ ' Último bloco disponível até: ' }}\r\n <v-btn\r\n @click=\"goToStartBlock\"\r\n class=\"ml-2\"\r\n dark\r\n color=\"success\"\r\n depressed\r\n >\r\n <v-icon left> fa-clock </v-icon>\r\n {{ timeLastBlock }}\r\n </v-btn>\r\n <v-spacer></v-spacer>\r\n <v-btn color=\"error\" fab small class=\"ml-5\" @click=\"dialog = false\">\r\n <v-icon dark> fa fa-xmark </v-icon>\r\n </v-btn>\r\n </div-title>\r\n </div>\r\n </v-dialog>\r\n <Help :media=\"media\" @close=\"media = null\" />\r\n <v-dialog\r\n v-if=\"userMultiTabsGrid\"\r\n v-model=\"userMultiTabsGridsModel\"\r\n persistent\r\n width=\"60%\"\r\n >\r\n <div class=\"card\">\r\n <div class=\"card\"-title class=\"warning text-h5\" primary-title>\r\n <div class=\"row\" class=\"ma-0\" justify=\"center\" align=\"center\">\r\n <v-icon dark left style=\"font-size: 24px !important\">\r\n fa fa-exclamation-triangle\r\n </v-icon>\r\n <div style=\"color: white\">{{ $t('form.alert') }}</div>\r\n </div>\r\n </div-title>\r\n <div class=\"card\"-text class=\"justify-center pa-6 grey lighten-2\">\r\n <h3>\r\n {{ $t('alerts.userMultiTabsGrid') }}\r\n </h3>\r\n </div-text>\r\n <hr class=\"divider\" class=\"grey lighten-1\"></span>\r\n <div class=\"card\"-actions class=\"grey lighten-2 justify-center\">\r\n <v-btn color=\"error\" ml-5 @click=\"userMultiTabsGrid = false\">\r\n <v-icon left color=\"white\">fa fa-times</v-icon>\r\n {{ $t('form.close') }}\r\n </v-btn>\r\n </div-actions>\r\n </div>\r\n </v-dialog> -->\r\n </div>\r\n</template>\r\n<script>\r\nimport Frame from './components/Frame.vue'\r\nimport FramesInterface from './utils/FramesInterface.js'\r\nimport FramesService from './services/FramesService.js'\r\n\r\nimport Commands from './components/Commands.vue'\r\nimport Infos from './components/Infos.vue'\r\nimport VideoProgress from './components/VideoProgress.vue'\r\nimport Settings from './components/Settings.vue'\r\n\r\nconst Positions = Object.freeze({\r\n previous: 0,\r\n current: 1,\r\n next: 2,\r\n})\r\n\r\nexport default {\r\n name: 'visualization-container',\r\n props: {\r\n value: {\r\n type: Boolean,\r\n required: true,\r\n },\r\n date: {\r\n type: String,\r\n required: true,\r\n },\r\n channel: {\r\n type: Number,\r\n required: true,\r\n },\r\n startAudienceTime: {\r\n type: String,\r\n required: true,\r\n },\r\n endAudienceTime: {\r\n type: String,\r\n required: true,\r\n },\r\n videoProgressBar: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n jumpOnInsert: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n removeSelectionOnInsert: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n framesFormat: {\r\n type: [Number, String],\r\n default: 7,\r\n },\r\n maxSize: {\r\n type: Number,\r\n },\r\n videoControls: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n readOnly: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n maxSteps: {\r\n type: Number,\r\n default: 1,\r\n },\r\n customGridSize: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n showStretchFrame: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n visualizationInsArray: {\r\n type: Array,\r\n default: () => [],\r\n },\r\n insertDefaults: {\r\n type: Array,\r\n default: () => [],\r\n },\r\n },\r\n components: {\r\n Frame,\r\n CommandBar: Commands,\r\n InfoBar: Infos,\r\n VideoProgress,\r\n Settings,\r\n // Help,\r\n },\r\n data() {\r\n return {\r\n Positions,\r\n updatingChannel: null,\r\n dialog: false,\r\n timeLastBlock: null,\r\n alternativeServer: false,\r\n useCache: true,\r\n numberOfRows: 1,\r\n framesPerRow: 5,\r\n secondsPerFrame: 1,\r\n fInterface: null,\r\n velocity: 1,\r\n frames: [],\r\n previousFrames: [],\r\n nextFrames: [],\r\n channelCode: 0,\r\n videoPlaying: false,\r\n activeFrame: null,\r\n activeVideo: null,\r\n videoTime: 0,\r\n videoTotalTime: null,\r\n progressVideoDrag: false,\r\n events: {\r\n A: [],\r\n B: [],\r\n C: [],\r\n },\r\n currentEventA: null,\r\n activeEvents: {}, // Track active events by type\r\n eventBlocked: false, // Track if event creation is blocked\r\n canInsertTime: false,\r\n lastHeight: 0,\r\n loopInterval: null,\r\n nextLoop: false,\r\n prevLoop: false,\r\n videoSliderTotalDuration: 900,\r\n blockStartTime: null,\r\n media: null,\r\n changeServer: false,\r\n userMultiTabsGrid: false,\r\n userMultiTabsGridsModel: true,\r\n playbackRate: 1,\r\n paused: false,\r\n commandBarShow: true,\r\n dialogs: {\r\n playbackRate: false,\r\n goTo: false,\r\n secondsPerFrame: false,\r\n frames: false,\r\n shiftFrames: false,\r\n },\r\n lastNext: 0,\r\n lastPrev: 0,\r\n shiftFrames: 0,\r\n aspectRatio: 11 / 9,\r\n showCustomInputs: false,\r\n stretchFrame: false,\r\n scrollCounter: 300,\r\n eventsArray: [],\r\n }\r\n },\r\n async created() {\r\n this.changeServer = this.serverOfFrames === 'alternative'\r\n this.alternativeServer = this.serverOfFrames === 'alternative'\r\n const settings = [\r\n {\r\n framesPerRow: 1,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 2,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 3,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 3,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 4,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 4,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 5,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 5,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 6,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 6,\r\n numberOfRows: 2,\r\n },\r\n {\r\n //Custom (item 11)\r\n framesPerRow: 7,\r\n numberOfRows: 3,\r\n },\r\n ]\r\n\r\n // Os valores custom são guardados no local storage e não na base de dados\r\n const customValues = JSON.parse(localStorage.getItem('customGridValues'))\r\n\r\n if (this.framesFormat == '11' && !!customValues) {\r\n this.numberOfRows = customValues[0]\r\n this.framesPerRow = customValues[1]\r\n } else {\r\n const storedOnDb = settings[parseInt(this.framesFormat) - 1]\r\n this.framesPerRow = storedOnDb.framesPerRow\r\n this.numberOfRows = storedOnDb.numberOfRows\r\n }\r\n\r\n document.addEventListener('wheel', this.scrollEvent)\r\n document.addEventListener('keydown', this.handleKeydown)\r\n\r\n await this.createFramesInterface()\r\n this.$nextTick(this.resize)\r\n },\r\n methods: {\r\n handleKeydown(event) {\r\n if (!this.active || !this.settingsClosed || this.readOnly) return\r\n\r\n // Check if the pressed key matches any shortcut in insertDefaults\r\n const matchedConfig = this.insertDefaults.find((config) => {\r\n const shortcut = config.shortcut\r\n\r\n // Parse the shortcut string (e.g., \"Shift+F8\", \"Ctrl+Shift+F3\", \"F2\")\r\n const parts = shortcut.split('+').map((p) => p.trim())\r\n const key = parts[parts.length - 1].toUpperCase()\r\n\r\n // Check modifiers\r\n const requiresShift = parts.some((p) => p.toLowerCase() === 'shift')\r\n const requiresCtrl = parts.some((p) => p.toLowerCase() === 'ctrl')\r\n const requiresAlt = parts.some((p) => p.toLowerCase() === 'alt')\r\n const requiresMeta = parts.some(\r\n (p) => p.toLowerCase() === 'meta' || p.toLowerCase() === 'cmd',\r\n )\r\n\r\n // Match if key and all modifiers match\r\n return (\r\n event.key.toUpperCase() === key &&\r\n event.shiftKey === requiresShift &&\r\n event.ctrlKey === requiresCtrl &&\r\n event.altKey === requiresAlt &&\r\n event.metaKey === requiresMeta\r\n )\r\n })\r\n\r\n if (matchedConfig) {\r\n event.preventDefault()\r\n this.toggleEvent(matchedConfig.event, matchedConfig.shortcut)\r\n }\r\n },\r\n scrollEvent(e) {\r\n if (this.active) {\r\n if (Math.sign(e.deltaY) !== Math.sign(this.scrollCounter)) {\r\n if (Math.sign(e.deltaY) > 0) {\r\n this.next()\r\n } else {\r\n this.prev()\r\n }\r\n this.scrollCounter = e.deltaY\r\n return\r\n }\r\n\r\n this.scrollCounter += e.deltaY\r\n if (Math.abs(this.scrollCounter) >= 400) {\r\n if (Math.sign(e.deltaY) > 0) {\r\n this.next()\r\n } else {\r\n this.prev()\r\n }\r\n this.scrollCounter = 0\r\n }\r\n }\r\n },\r\n stopVideoPlaying(array) {\r\n for (const frame of array || this.$refs.frames) {\r\n if (\r\n frame.videoStatus === frame.Status.playing ||\r\n frame.videoStatus === frame.Status.paused\r\n ) {\r\n frame.stop(false)\r\n }\r\n }\r\n this.activeVideo = null\r\n },\r\n toogleCommandsVisibility() {\r\n this.commandBarShow = !this.commandBarShow\r\n this.$nextTick(this.resize)\r\n },\r\n framesClicked(e) {\r\n if (e.target.parentNode.id != 'insert') {\r\n this.active = true\r\n }\r\n },\r\n async goToStartBlock() {\r\n try {\r\n const d = new Date()\r\n let timestamp = Date.UTC(\r\n d.getFullYear(),\r\n d.getMonth(),\r\n d.getDate(),\r\n d.getHours(),\r\n d.getMinutes(),\r\n d.getSeconds(),\r\n )\r\n\r\n const response = (\r\n await FramesService.getNextAvailableBlock({\r\n channel: this.channel,\r\n time: timestamp / 1000,\r\n })\r\n ).data\r\n\r\n this.dialog = false\r\n this.changeHour(this.convertToAudienceTime(response.data.start, ':'))\r\n } catch (err) {\r\n // console.error(err)\r\n }\r\n },\r\n async checkAvailableBlock() {\r\n try {\r\n const d = new Date()\r\n let timestamp = Date.UTC(\r\n d.getFullYear(),\r\n d.getMonth(),\r\n d.getDate(),\r\n d.getHours(),\r\n d.getMinutes(),\r\n d.getSeconds(),\r\n )\r\n\r\n const response = (\r\n await FramesService.getNextAvailableBlock({\r\n channel: this.channel,\r\n time: timestamp / 1000,\r\n })\r\n ).data\r\n\r\n this.timeLastBlock = this.convertToAudienceTime(response.data.end, ':')\r\n this.dialog = true\r\n if (!response.status) {\r\n this.timeLastBlock = 'N/D'\r\n }\r\n } catch (err) {\r\n // console.error(err)\r\n }\r\n },\r\n updateSlider(videoStartTime, time) {\r\n // * atualizar slider se estiver fora do range definido previamente\r\n if (\r\n time < this.blockStartTime ||\r\n time > this.blockStartTime ||\r\n videoStartTime > this.blockStartTime + this.videoSliderTotalDuration\r\n ) {\r\n this.blockStartTime = videoStartTime\r\n this.videoSliderTotalDuration = 900\r\n }\r\n },\r\n nextLoopActivate() {\r\n this.breakLoop()\r\n this.loopInterval = setInterval(\r\n () => this.next({ ignoreTime: true }),\r\n this.swapImagesDelay,\r\n )\r\n setTimeout(() => {\r\n this.nextLoop = true\r\n }, 0)\r\n },\r\n prevLoopActivate() {\r\n this.breakLoop()\r\n this.loopInterval = setInterval(\r\n () => this.prev({ ignoreTime: true }),\r\n this.swapImagesDelay,\r\n )\r\n setTimeout(() => {\r\n this.prevLoop = true\r\n }, 0)\r\n },\r\n breakLoop() {\r\n clearInterval(this.loopInterval)\r\n this.loopInterval = null\r\n this.nextLoop = false\r\n this.prevLoop = false\r\n },\r\n changePlayPause(status) {\r\n this.paused = !status\r\n },\r\n resize(height = this.lastHeight) {\r\n this.lastHeight = height\r\n if (this.$refs.frames) {\r\n for (let frame of this.$refs.frames) {\r\n frame.resize(height)\r\n }\r\n }\r\n this.$emit('resized')\r\n },\r\n async goToFirstFrame() {\r\n let frames = this.$refs.frames\r\n\r\n let audienceTime = null\r\n if (frames.length > 0) {\r\n let frame = frames[0].frame\r\n audienceTime = this.getAudienceTime(frame.time, 0, 0, 0)\r\n }\r\n if (audienceTime) {\r\n const [hours, minutes, seconds] = audienceTime.split(':')\r\n const totalSeconds =\r\n parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(seconds)\r\n if (totalSeconds >= 9000)\r\n this.changeHour(this.getLastFirtsBlockTime(audienceTime, true))\r\n else this.changeHour(this.getLastFirtsBlockTime('02:30:00', true))\r\n }\r\n },\r\n async goToLastFrame() {\r\n let frames = this.$refs.frames\r\n let audienceTime = null\r\n if (frames.length > 0) {\r\n let frame = frames[0].frame\r\n\r\n audienceTime = this.getAudienceTime(frame.time, 0, 0, 0)\r\n }\r\n if (audienceTime) {\r\n this.changeHour(this.getLastFirtsBlockTime(audienceTime))\r\n }\r\n },\r\n getLastFirtsBlockTime(time, first = false) {\r\n if (time.indexOf(':') !== -1) {\r\n time = time.replace(/:/g, '')\r\n }\r\n let h, m, newTime\r\n const t = time.match(/.{1,2}/g)\r\n if (t[0] && t[1]) {\r\n h = parseInt(t[0])\r\n m = parseInt(t[1])\r\n }\r\n if (h < 26) {\r\n if (m < 15)\r\n if (first) newTime = t[0] + ':00:00'\r\n else newTime = t[0] + ':14:59'\r\n else if (m < 30)\r\n if (first) newTime = t[0] + ':15:00'\r\n else newTime = t[0] + ':29:59'\r\n else if (m < 45)\r\n if (first) newTime = t[0] + ':30:00'\r\n else newTime = t[0] + ':44:59'\r\n else if (first) newTime = t[0] + ':45:00'\r\n else newTime = t[0] + ':59:59'\r\n } else {\r\n if (m < 15)\r\n if (first) newTime = '26:00:00'\r\n else newTime = '26:14:59'\r\n else {\r\n if (first) newTime = '26:15:00'\r\n else newTime = '26:29:59'\r\n }\r\n }\r\n return newTime\r\n },\r\n openBlocks() {\r\n this.$refs.settings2?.openBlocks()\r\n },\r\n playOrPause() {\r\n const array = this.$refs.frames.filter((fc) => !!fc.active)\r\n if (array.length === 1) {\r\n const frame = array[0]\r\n frame.playOrPause(this.playbackRate)\r\n }\r\n },\r\n stopPlayingBar() {\r\n for (let ref of this.$refs.frames) {\r\n if (\r\n ref.videoStatus === ref.Status.playing ||\r\n ref.videoStatus === ref.Status.paused\r\n ) {\r\n ref.stop(false)\r\n }\r\n }\r\n },\r\n async setFrameSelection(selected) {\r\n this.frames = this.loadingArray\r\n const settings = [\r\n {\r\n framesPerRow: 1,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 2,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 3,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 3,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 4,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 4,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 5,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 5,\r\n numberOfRows: 2,\r\n },\r\n {\r\n framesPerRow: 6,\r\n numberOfRows: 1,\r\n },\r\n {\r\n framesPerRow: 6,\r\n numberOfRows: 2,\r\n },\r\n {\r\n //Custom (item 11)\r\n framesPerRow: 7,\r\n numberOfRows: 3,\r\n },\r\n ]\r\n\r\n const formatSelected = settings[selected - 1]\r\n this.framesPerRow = formatSelected.framesPerRow\r\n this.numberOfRows = formatSelected.numberOfRows\r\n\r\n await this.fInterface.changeSize(this.numberOfRows, this.framesPerRow)\r\n this.getFramesArray()\r\n this.$nextTick(this.resize)\r\n this.$emit('frames-format-changed', selected)\r\n },\r\n async setCustomGridValues(customValues) {\r\n this.numberOfRows = customValues[0]\r\n this.framesPerRow = customValues[1]\r\n localStorage.setItem('customGridValues', JSON.stringify(customValues))\r\n await this.fInterface.changeSize(this.numberOfRows, this.framesPerRow)\r\n this.getFramesArray()\r\n this.$nextTick(this.resize)\r\n this.$emit('frames-format-changed', 11)\r\n },\r\n getFramesArray() {\r\n this.frames = this.fInterface.getFrames(Positions.current)\r\n this.nextFrames = this.fInterface.getFrames(Positions.next)\r\n this.previousFrames = this.fInterface.getFrames(Positions.previous)\r\n\r\n const div = document.createElement('div')\r\n div.innerHTML = this.frames[0].image\r\n const newAspectRatio =\r\n div.getElementsByTagName('img')[0]?.naturalWidth /\r\n div.getElementsByTagName('img')[0]?.naturalHeight\r\n\r\n this.aspectRatio = isNaN(newAspectRatio)\r\n ? this.aspectRatio\r\n : newAspectRatio\r\n\r\n this.$nextTick(this.resize)\r\n\r\n const frame = this.frames.find((f) => f.blockStart)\r\n if (frame && this.alternativeServer) {\r\n this.$emit(\r\n 'new-block',\r\n frame.title?.match(/[0-9]{3}\\/(?:[0-9]+_?)+/)[0],\r\n )\r\n }\r\n },\r\n async createFramesInterface(startTime = this.startAudienceTime) {\r\n this.frames = this.loadingArray\r\n // let ch = this.channel\r\n // let associationTMP = {\r\n // 1735073: 1,\r\n // 1735074: 139,\r\n // 1735075: 3,\r\n // 1735076: 132,\r\n // }\r\n // //\r\n // this.channelCode = associationTMP[ch] ? associationTMP[ch] : ch\r\n\r\n const t = startTime.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const time = Date.UTC(d.year, d.month, d.day, t[0], t[1], t[2]) / 1000\r\n // * iniciar slider\r\n this.blockStartTime = time\r\n this.fInterface = await new FramesInterface(\r\n this.channel,\r\n this.numberOfRows,\r\n this.framesPerRow,\r\n time,\r\n this.startAudienceTime,\r\n this.useCache,\r\n this.shiftFrames,\r\n )\r\n await this.fInterface.init()\r\n\r\n this.getFramesArray()\r\n\r\n const div = document.createElement('div')\r\n div.innerHTML = this.frames[0].image\r\n\r\n if (div.getElementsByTagName('img')[0]) {\r\n this.aspectRatio =\r\n div.getElementsByTagName('img')[0].naturalWidth /\r\n div.getElementsByTagName('img')[0].naturalHeight\r\n }\r\n\r\n this.activeFrame =\r\n this.getIndex(1, 0, Positions.current) + this.framesPerRow\r\n\r\n this.activeVideo = null\r\n },\r\n getIndex(rowNumber, frameIndex, position) {\r\n const baseOffset = this.framesPerRow * this.numberOfRows * position\r\n const rowOffset = (rowNumber - 1) * this.framesPerRow\r\n return baseOffset + rowOffset + frameIndex\r\n },\r\n getAudienceTime(frameTime, rowNumber, frameNumber, position) {\r\n if (!frameTime) {\r\n return 'Loading...'\r\n } else if (\r\n this.getIndex(rowNumber, frameNumber, position) === this.activeVideo\r\n ) {\r\n return this.convertToAudienceTime(this.videoTime)\r\n } else {\r\n return this.convertToAudienceTime(frameTime)\r\n }\r\n },\r\n giveMeType(frameTime) {\r\n if (!frameTime) {\r\n return ''\r\n }\r\n let time = this.convertToAudienceTime(frameTime)\r\n time = time.replace(/:/g, '')\r\n const foundEvent = this.eventsArray.find((event) => {\r\n return time >= event.hour_ini && time <= event.hour_end\r\n })\r\n if (foundEvent) {\r\n return `event-${foundEvent.type}`\r\n } else {\r\n return 'event-gap'\r\n }\r\n },\r\n dateInUtc(miliSeconds) {\r\n var date = new Date(miliSeconds)\r\n var utc = new Date(\r\n date.getUTCFullYear(),\r\n date.getUTCMonth(),\r\n date.getUTCDate(),\r\n date.getUTCHours(),\r\n date.getUTCMinutes(),\r\n date.getUTCSeconds(),\r\n )\r\n return utc\r\n },\r\n convertToAudienceTime(time, separator = ':') {\r\n const d = this.getDateParts()\r\n const limit = Date.UTC(d.year, d.month, d.day, 23, 59, 59) / 1000\r\n\r\n const date = new Date(time * 1000)\r\n let hour = [\r\n date.getUTCHours(),\r\n date.getUTCMinutes(),\r\n date.getUTCSeconds(),\r\n ]\r\n\r\n if (time > limit && time <= limit + this.startAudienceSeconds) {\r\n hour[0] = 24 + hour[0]\r\n }\r\n\r\n return hour\r\n .map((part) => (part > 9 ? part.toString() : '0' + part))\r\n .join(separator)\r\n },\r\n getDateParts(date = this.date) {\r\n const data = /(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})/.exec(\r\n date,\r\n )?.groups\r\n if (data) {\r\n return {\r\n year: parseInt(data.year),\r\n month: parseInt(data.month) - 1,\r\n day: parseInt(data.day),\r\n }\r\n }\r\n return {\r\n year: null,\r\n month: null,\r\n day: null,\r\n }\r\n },\r\n selectFrame(index, frame) {\r\n if (this.activeFrame !== index) {\r\n // Stop playing video if clicking on a different frame\r\n const array = this.$refs.frames.filter(\r\n (fc) => fc.videoStatus === fc.Status.playing,\r\n )\r\n if (array.length === 1) {\r\n const frame = array[0]\r\n frame.playOrPause()\r\n }\r\n this.activeVideo = null\r\n this.activeFrame = index\r\n }\r\n },\r\n toggleEvent(eventConfig, shortcutKey) {\r\n const currentFrame = this.$refs.frames?.find(\r\n (f) => f.index === this.activeFrame,\r\n )\r\n if (!currentFrame || !currentFrame.frame.time) return\r\n\r\n const frameTime = currentFrame.frame.time\r\n const eventType = eventConfig.type\r\n\r\n // Check if events are blocked (when type B is active outside Type A)\r\n if (this.eventBlocked && eventType !== 'B' && eventType !== 'C') {\r\n console.warn(\r\n 'Events are blocked. Close the active type B/C event first.',\r\n )\r\n return\r\n }\r\n\r\n if (eventType === 'A') {\r\n this.handleTypeA(frameTime, eventConfig, shortcutKey)\r\n } else if (eventType === 'B') {\r\n this.handleTypeB(frameTime, eventConfig, shortcutKey)\r\n } else if (eventType === 'C') {\r\n this.handleTypeC(frameTime, eventConfig, shortcutKey)\r\n }\r\n\r\n this.canInsertTime = this.hasAnyEvents()\r\n document.getElementById(`frame-${this.activeFrame}`).click()\r\n },\r\n handleTypeA(frameTime, eventConfig, shortcutKey) {\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n // Check if the shortcut matches the one used to start the event\r\n if (this.currentEventA.shortcutKey !== shortcutKey) {\r\n console.warn('Use the same shortcut key to close this event')\r\n return\r\n }\r\n // If clicking on the same frame where it started, remove the selection\r\n if (this.currentEventA.hour_ini === frameTime) {\r\n this.currentEventA = null\r\n delete this.activeEvents.A\r\n } else {\r\n // Close the event A\r\n this.currentEventA.hour_end = frameTime\r\n this.events.A.push({ ...this.currentEventA })\r\n this.currentEventA = null\r\n delete this.activeEvents.A\r\n }\r\n } else {\r\n // Check if clicking on hour_end of a completed event to reopen it\r\n const completedEvent = this.events.A.find(\r\n (e) => e.hour_end === frameTime && e.shortcutKey === shortcutKey,\r\n )\r\n if (completedEvent) {\r\n // Remove from completed events and reopen it\r\n const index = this.events.A.indexOf(completedEvent)\r\n this.events.A.splice(index, 1)\r\n this.currentEventA = {\r\n ...completedEvent,\r\n hour_end: null,\r\n }\r\n this.activeEvents.A = true\r\n return\r\n }\r\n\r\n // Start a new event A\r\n // Type A cannot be nested inside another Type A (already checked by currentEventA)\r\n this.currentEventA = {\r\n ...eventConfig,\r\n hour_ini: frameTime,\r\n hour_end: null,\r\n shortcutKey: shortcutKey,\r\n }\r\n this.activeEvents.A = true\r\n }\r\n },\r\n handleTypeB(frameTime, eventConfig, shortcutKey) {\r\n const lastEventB = this.events.B[this.events.B.length - 1]\r\n const lastEventC = this.events.C[this.events.C.length - 1]\r\n\r\n if (lastEventB && !lastEventB.hour_end) {\r\n // Check if the shortcut matches the one used to start the event\r\n if (lastEventB.shortcutKey !== shortcutKey) {\r\n console.warn('Use the same shortcut key to close this event')\r\n return\r\n }\r\n // If clicking on the same frame where it started, remove the selection\r\n if (lastEventB.hour_ini === frameTime) {\r\n this.events.B.pop() // Remove the last event\r\n delete this.activeEvents.B\r\n this.eventBlocked = false // Unblock events\r\n } else {\r\n // Close the event B\r\n lastEventB.hour_end = frameTime\r\n delete this.activeEvents.B\r\n this.eventBlocked = false // Unblock events\r\n }\r\n } else {\r\n // Check if clicking on hour_end of a completed event to reopen it\r\n const completedEvent = this.events.B.find(\r\n (e) => e.hour_end === frameTime && e.shortcutKey === shortcutKey,\r\n )\r\n if (completedEvent) {\r\n // Remove hour_end to reopen the event\r\n completedEvent.hour_end = null\r\n this.activeEvents.B = true\r\n if (!this.currentEventA) {\r\n this.eventBlocked = true\r\n }\r\n return\r\n }\r\n\r\n // Check if we're outside Type A and already have an unclosed B or C\r\n if (!this.currentEventA) {\r\n if (\r\n (lastEventB && !lastEventB.hour_end) ||\r\n (lastEventC && !lastEventC.hour_end)\r\n ) {\r\n console.warn(\r\n 'Only 1 B/C event allowed outside Type A. Close the current event first.',\r\n )\r\n return\r\n }\r\n // Outside Type A: block other events when B is active\r\n this.eventBlocked = true\r\n }\r\n\r\n // Auto-close any unclosed C event before starting B\r\n if (lastEventC && !lastEventC.hour_end) {\r\n lastEventC.hour_end = frameTime - 1\r\n delete this.activeEvents.C\r\n }\r\n\r\n // Start a new event B\r\n this.events.B.push({\r\n ...eventConfig,\r\n hour_ini: frameTime,\r\n hour_end: null,\r\n shortcutKey: shortcutKey,\r\n })\r\n this.activeEvents.B = true\r\n }\r\n },\r\n handleTypeC(frameTime, eventConfig, shortcutKey) {\r\n const lastEventC = this.events.C[this.events.C.length - 1]\r\n const lastEventB = this.events.B[this.events.B.length - 1]\r\n\r\n if (lastEventC && !lastEventC.hour_end) {\r\n // Check if the shortcut matches the one used to start the event\r\n if (lastEventC.shortcutKey !== shortcutKey) {\r\n console.warn('Use the same shortcut key to close this event')\r\n return\r\n }\r\n // If clicking on the same frame where it started, remove the selection\r\n if (lastEventC.hour_ini === frameTime) {\r\n this.events.C.pop() // Remove the last event\r\n delete this.activeEvents.C\r\n this.eventBlocked = false // Unblock events if it was outside Type A\r\n } else {\r\n // Close the event C\r\n lastEventC.hour_end = frameTime\r\n delete this.activeEvents.C\r\n this.eventBlocked = false // Unblock events if it was outside Type A\r\n }\r\n } else {\r\n // Check if clicking on hour_end of a completed event to reopen it\r\n const completedEvent = this.events.C.find(\r\n (e) => e.hour_end === frameTime && e.shortcutKey === shortcutKey,\r\n )\r\n if (completedEvent) {\r\n // Remove hour_end to reopen the event\r\n completedEvent.hour_end = null\r\n this.activeEvents.C = true\r\n if (!this.currentEventA) {\r\n this.eventBlocked = true\r\n }\r\n return\r\n }\r\n\r\n // Check if we're outside Type A and already have an unclosed B or C\r\n if (!this.currentEventA) {\r\n if (\r\n (lastEventB && !lastEventB.hour_end) ||\r\n (lastEventC && !lastEventC.hour_end)\r\n ) {\r\n console.warn(\r\n 'Only 1 B/C event allowed outside Type A. Close the current event first.',\r\n )\r\n return\r\n }\r\n // Outside Type A: block other events when C is active\r\n this.eventBlocked = true\r\n }\r\n\r\n // Auto-close any unclosed B event before starting C\r\n if (lastEventB && !lastEventB.hour_end) {\r\n lastEventB.hour_end = frameTime - 1\r\n delete this.activeEvents.B\r\n }\r\n\r\n // Start a new event C\r\n this.events.C.push({\r\n ...eventConfig,\r\n hour_ini: frameTime,\r\n hour_end: null,\r\n shortcutKey: shortcutKey,\r\n })\r\n this.activeEvents.C = true\r\n }\r\n },\r\n clearAllFrameSelections() {\r\n // Clear all events\r\n this.events.A = []\r\n this.events.B = []\r\n this.events.C = []\r\n this.currentEventA = null\r\n this.activeEvents = {}\r\n this.eventBlocked = false\r\n this.canInsertTime = false\r\n },\r\n hasAnyEvents() {\r\n return (\r\n this.events.A.length > 0 ||\r\n this.events.B.length > 0 ||\r\n this.events.C.length > 0 ||\r\n !!(this.currentEventA && this.currentEventA.hour_ini)\r\n )\r\n },\r\n isEventStart(frameTime) {\r\n if (!frameTime) return false\r\n\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n allEvents.push(this.currentEventA)\r\n }\r\n\r\n return allEvents.some((e) => e.hour_ini === frameTime)\r\n },\r\n isEventEnd(frameTime) {\r\n if (!frameTime) return false\r\n\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n return allEvents.some((e) => e.hour_end === frameTime)\r\n },\r\n isBetweenEvent(frameTime) {\r\n if (!frameTime) return false\r\n\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n // Only check completed events\r\n return allEvents.some((e) => {\r\n if (!e.hour_end) return false\r\n const start = e.hour_ini\r\n const end = e.hour_end\r\n return frameTime > start && frameTime < end\r\n })\r\n },\r\n isInEventType(frameTime, type) {\r\n if (!frameTime) return false\r\n\r\n let eventsOfType = this.events[type] || []\r\n\r\n // Only include completed events (with both start and end)\r\n return eventsOfType.some((e) => {\r\n if (!e.hour_end) return false // Event not completed yet\r\n const start = e.hour_ini\r\n const end = e.hour_end\r\n return frameTime >= start && frameTime <= end\r\n })\r\n },\r\n getEventType(frameTime) {\r\n if (!frameTime) return null\r\n\r\n // Check for incomplete (currently being marked) events first\r\n if (this.currentEventA && this.currentEventA.hour_ini === frameTime) {\r\n return 'A'\r\n }\r\n\r\n // Check for incomplete B or C events\r\n const lastEventB = this.events.B[this.events.B.length - 1]\r\n if (\r\n lastEventB &&\r\n !lastEventB.hour_end &&\r\n lastEventB.hour_ini === frameTime\r\n ) {\r\n return 'B'\r\n }\r\n\r\n const lastEventC = this.events.C[this.events.C.length - 1]\r\n if (\r\n lastEventC &&\r\n !lastEventC.hour_end &&\r\n lastEventC.hour_ini === frameTime\r\n ) {\r\n return 'C'\r\n }\r\n\r\n // Check in priority order: B, C, then A for completed events\r\n // This ensures nested events show their specific color\r\n if (this.isInEventType(frameTime, 'B')) return 'B'\r\n if (this.isInEventType(frameTime, 'C')) return 'C'\r\n if (this.isInEventType(frameTime, 'A')) return 'A'\r\n\r\n return null\r\n },\r\n //* Navegação\r\n arrowRight() {\r\n if (this.checkLimitRight(false)) {\r\n if (\r\n this.activeFrame ===\r\n this.numberOfRows * this.framesPerRow * 2 - 1\r\n ) {\r\n this.next()\r\n } else {\r\n this.activeFrame++\r\n }\r\n }\r\n },\r\n arrowLeft() {\r\n if (this.checkLimitLeft(false)) {\r\n if (this.activeFrame === this.numberOfRows * this.framesPerRow) {\r\n this.prev({ stayOnLast: true })\r\n } else {\r\n this.activeFrame--\r\n }\r\n }\r\n },\r\n arrowUp() {\r\n const relativePosition =\r\n this.activeFrame - this.numberOfRows * this.framesPerRow\r\n const currentRow = Math.floor(relativePosition / this.framesPerRow)\r\n if (currentRow > 0) {\r\n this.activeFrame -= this.framesPerRow\r\n }\r\n },\r\n\r\n arrowDown() {\r\n const relativePosition =\r\n this.activeFrame - this.numberOfRows * this.framesPerRow\r\n const currentRow = Math.floor(relativePosition / this.framesPerRow)\r\n\r\n if (currentRow < this.numberOfRows - 1) {\r\n this.activeFrame += this.framesPerRow\r\n }\r\n },\r\n checkLimitRight(value) {\r\n const hours = this.endAudienceTime.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const high = Date.UTC(\r\n d.year,\r\n d.month,\r\n d.day,\r\n hours[0],\r\n parseInt(hours[1]) + 30,\r\n hours[2],\r\n )\r\n\r\n if (value) {\r\n return (\r\n high >\r\n (this.fInterface.getCurrentTime() +\r\n this.numberOfRows * this.framesPerRow -\r\n 1) *\r\n 1000\r\n )\r\n } else {\r\n return high > this.fInterface.getCurrentTime() * 1000\r\n }\r\n },\r\n checkLimitLeft(value) {\r\n const hours = this.startAudienceTime.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const low = Date.UTC(d.year, d.month, d.day, hours[0], hours[1], hours[2])\r\n\r\n if (value) {\r\n return low <= (this.fInterface.getCurrentTime() - 1) * 1000\r\n } else {\r\n return (\r\n low <\r\n (this.fInterface.getCurrentTime() +\r\n this.activeFrame -\r\n this.numberOfRows * this.framesPerRow) *\r\n 1000\r\n )\r\n }\r\n },\r\n async next(config = {}) {\r\n if (\r\n (config.ignoreTime ||\r\n Date.now() - this.lastNext > this.swapImagesDelay) &&\r\n this.checkLimitRight(true) &&\r\n !this.navigationPending\r\n ) {\r\n this.navigationPending = true\r\n this.stopVideoPlaying()\r\n\r\n this.navigationPending = await new Promise((resolve) => {\r\n this.fInterface\r\n .loadNextFrames()\r\n .then(() => {\r\n this.activeFrame = this.getIndex(\r\n 1,\r\n this.framesPerRow,\r\n Positions.current,\r\n )\r\n\r\n this.getFramesArray()\r\n this.lastNext = Date.now()\r\n resolve(false)\r\n })\r\n .catch(() => {\r\n resolve(false)\r\n })\r\n })\r\n }\r\n },\r\n async prev(config = {}) {\r\n if (\r\n (config.ignoreTime ||\r\n Date.now() - this.lastPrev > this.swapImagesDelay) &&\r\n this.checkLimitLeft(true) &&\r\n !this.navigationPending\r\n ) {\r\n this.navigationPending = true\r\n this.stopVideoPlaying()\r\n\r\n const hours = this.startAudienceTime.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const low =\r\n Date.UTC(d.year, d.month, d.day, hours[0], hours[1], hours[2]) / 1000\r\n\r\n this.navigationPending = await new Promise((resolve) => {\r\n this.fInterface\r\n .loadPrevFrames()\r\n .then(() => {\r\n if (config.stayOnLast) {\r\n this.activeFrame = this.getIndex(\r\n this.numberOfRows,\r\n this.framesPerRow - 1,\r\n Positions.current,\r\n )\r\n } else {\r\n if (\r\n this.fInterface.getCurrentTime() - low <\r\n this.numberOfRows * this.framesPerRow\r\n ) {\r\n this.setStartTime(this.startAudienceTime, false)\r\n }\r\n this.activeFrame = this.getIndex(1, 0, Positions.current)\r\n }\r\n\r\n this.getFramesArray()\r\n this.lastPrev = Date.now()\r\n resolve(false)\r\n })\r\n .catch(() => {\r\n resolve(false)\r\n })\r\n })\r\n } else {\r\n // console.error('prev blocked')\r\n }\r\n },\r\n\r\n hmsToSeconds(s) {\r\n const [hh, mm, ss] = s.match(/.{1,2}/g).map(Number)\r\n return hh * 3600 + mm * 60 + ss\r\n },\r\n\r\n secondsToHms(total) {\r\n const hh = String(Math.floor(total / 3600)).padStart(2, '0')\r\n const mm = String(Math.floor((total % 3600) / 60)).padStart(2, '0')\r\n const ss = String(total % 60).padStart(2, '0')\r\n return `${hh}${mm}${ss}`\r\n },\r\n\r\n async setStartTime(time, flag = true) {\r\n if (time.includes(':')) time = time.replace(/:/g, '')\r\n const d = this.getDateParts()\r\n const startSec = this.hmsToSeconds(this.startAudienceTime)\r\n const endSec = this.hmsToSeconds(this.endAudienceTime)\r\n const setSec = this.hmsToSeconds(time)\r\n\r\n const secondsBefore = this.framesPerRow + 1\r\n const gridSize = this.numberOfRows * this.framesPerRow\r\n const secondsAfter = gridSize - 1\r\n\r\n const desired = setSec - secondsBefore\r\n const maxStart = endSec - secondsAfter\r\n const clamp = (v, min, max) => Math.min(max, Math.max(min, v))\r\n\r\n let targetSec = clamp(desired, startSec, maxStart)\r\n\r\n if (desired < startSec) {\r\n targetSec = desired + this.framesPerRow\r\n } else if (desired > maxStart) {\r\n targetSec = maxStart\r\n } else {\r\n targetSec = desired + 1\r\n }\r\n\r\n targetSec = clamp(targetSec, startSec, endSec)\r\n const dayStartEpoch = Date.UTC(d.year, d.month, d.day, 0, 0, 0) / 1000\r\n const targetEpoch = dayStartEpoch + targetSec\r\n\r\n let finalTime = targetEpoch\r\n if (this.numberOfRows > 1 && flag) {\r\n finalTime = targetEpoch + this.framesPerRow\r\n }\r\n await this.fInterface.changeTime(finalTime, flag)\r\n\r\n this.getFramesArray()\r\n this.activeFrame =\r\n this.getIndex(1, 0, Positions.current) + this.framesPerRow\r\n this.activeVideo = null\r\n return true\r\n },\r\n hourToTimeStamp(time) {\r\n if (time.indexOf(':') !== -1) {\r\n time = time.replace(/:/g, '')\r\n }\r\n const t = time.match(/.{1,2}/g)\r\n const d = this.getDateParts()\r\n const setTime = Date.UTC(d.year, d.month, d.day, t[0], t[1], t[2]) / 1000\r\n\r\n return setTime\r\n },\r\n changeHour(value) {\r\n if (value) {\r\n return new Promise((resolve) => {\r\n setTimeout(async () => {\r\n this.stopVideoPlaying()\r\n\r\n await this.setStartTime(value, true)\r\n resolve()\r\n }, 0)\r\n })\r\n }\r\n },\r\n changeBlockInterval(value) {\r\n this.changeHour(value.ini)\r\n let time_ini, time_end\r\n time_ini = this.hourToTimeStamp(value.ini)\r\n time_end = this.hourToTimeStamp(value.end)\r\n this.videoSliderTotalDuration = time_end - time_ini\r\n this.$refs.frames[0].changeSettings(time_ini)\r\n this.blockStartTime = time_ini\r\n },\r\n //eslint-disable-next-line\r\n async updateVideoTime(index, videoTime) {\r\n this.activeVideo = index\r\n this.videoTime = videoTime\r\n },\r\n //eslint-disable-next-line\r\n updateVideoStatus(currentTime) {\r\n if (!this.progressVideoDrag) {\r\n // ESTA FUNÇÃO PASSOU PARA DENTRO DOS COMMANDS\r\n // this.updateProgress(null, currentTime)\r\n }\r\n },\r\n async startPlaying(frame, totalTime) {\r\n const array = this.$refs.frames.filter(\r\n (fc) => fc.frame.time !== frame.time,\r\n )\r\n this.stopVideoPlaying(array)\r\n\r\n this.videoTotalTime = totalTime\r\n this.videoPlaying = true\r\n },\r\n stopPlaying() {\r\n this.videoTotalTime = null\r\n this.videoPlaying = false\r\n this.paused = false\r\n },\r\n insertTime() {\r\n // Calculate the overall D event (lowest hour_ini to biggest hour_end)\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n allEvents.push(this.currentEventA)\r\n }\r\n\r\n if (allEvents.length === 0) {\r\n console.warn('No events to insert')\r\n return\r\n }\r\n\r\n const allTimes = allEvents.flatMap((e) =>\r\n [e.hour_ini, e.hour_end].filter(Boolean),\r\n )\r\n const lowestHourIni = Math.min(...allTimes)\r\n const biggestHourEnd = Math.max(...allTimes)\r\n\r\n // Get all B and C events (sorted by start time)\r\n const bcEvents = [...this.events.B, ...this.events.C]\r\n .filter((e) => e.hour_end) // Only complete events\r\n .sort((a, b) => a.hour_ini - b.hour_ini)\r\n\r\n // Generate A events to fill the gaps\r\n const generatedAEvents = []\r\n const standaloneBCEvents = []\r\n\r\n // Get all A events (both completed and current)\r\n const allAEvents = [...this.events.A]\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n allAEvents.push({\r\n ...this.currentEventA,\r\n hour_end: this.currentEventA.hour_end || biggestHourEnd,\r\n })\r\n }\r\n\r\n // Separate B/C events into those inside Type A and standalone\r\n for (const bcEvent of bcEvents) {\r\n const isInsideA = allAEvents.some(\r\n (aEvent) =>\r\n bcEvent.hour_ini >= aEvent.hour_ini &&\r\n bcEvent.hour_end <= aEvent.hour_end,\r\n )\r\n if (!isInsideA) {\r\n standaloneBCEvents.push(bcEvent)\r\n }\r\n }\r\n\r\n // For each A event, generate gap-filling A events around B/C events\r\n for (const aEvent of allAEvents) {\r\n const aStart = aEvent.hour_ini\r\n const aEnd = aEvent.hour_end\r\n\r\n // Get B/C events that are within this A event\r\n const bcEventsInA = bcEvents.filter(\r\n (bc) => bc.hour_ini >= aStart && bc.hour_end <= aEnd,\r\n )\r\n\r\n if (bcEventsInA.length === 0) {\r\n // No B or C events inside this A, keep the A event as is\r\n generatedAEvents.push({\r\n ...aEvent,\r\n hour_ini: aStart,\r\n hour_end: aEnd,\r\n })\r\n } else {\r\n let currentTime = aStart\r\n\r\n for (const bcEvent of bcEventsInA) {\r\n // Create A event before this B/C event\r\n if (currentTime < bcEvent.hour_ini) {\r\n generatedAEvents.push({\r\n ...aEvent,\r\n hour_ini: currentTime,\r\n hour_end: bcEvent.hour_ini - 1,\r\n })\r\n }\r\n currentTime = bcEvent.hour_end + 1\r\n }\r\n\r\n // Create final A event after last B/C event\r\n if (currentTime <= aEnd) {\r\n generatedAEvents.push({\r\n ...aEvent,\r\n hour_ini: currentTime,\r\n hour_end: aEnd,\r\n })\r\n }\r\n }\r\n }\r\n\r\n // Create D events for each manually marked A event\r\n // const dEvents = allAEvents.map((aEvent) => ({\r\n // type: 'D',\r\n // hour_ini: this.convertToAudienceTime(aEvent.hour_ini, ''),\r\n // hour_end: this.convertToAudienceTime(aEvent.hour_end, ''),\r\n // timestamp: aEvent.hour_ini, // For sorting\r\n // }))\r\n\r\n // Convert generated A events\r\n const aEventsFormatted = generatedAEvents.map((e) => {\r\n const formatted = {\r\n type: e.type,\r\n hour_ini: this.convertToAudienceTime(e.hour_ini, ''),\r\n hour_end: this.convertToAudienceTime(e.hour_end, ''),\r\n timestamp: e.hour_ini, // For sorting\r\n }\r\n // Add any extra properties from the original event\r\n Object.keys(e).forEach((key) => {\r\n if (!['type', 'hour_ini', 'hour_end', 'timestamp'].includes(key)) {\r\n formatted[key] = e[key]\r\n }\r\n })\r\n return formatted\r\n })\r\n\r\n // Convert B/C events inside Type A\r\n const bcEventsInsideA = bcEvents.filter((bcEvent) =>\r\n allAEvents.some(\r\n (aEvent) =>\r\n bcEvent.hour_ini >= aEvent.hour_ini &&\r\n bcEvent.hour_end <= aEvent.hour_end,\r\n ),\r\n )\r\n\r\n const bcEventsFormatted = bcEventsInsideA.map((e) => {\r\n const formatted = {\r\n type: e.type,\r\n hour_ini: this.convertToAudienceTime(e.hour_ini, ''),\r\n hour_end: this.convertToAudienceTime(e.hour_end, ''),\r\n timestamp: e.hour_ini, // For sorting\r\n }\r\n // Add any extra properties from the original event\r\n Object.keys(e).forEach((key) => {\r\n if (!['type', 'hour_ini', 'hour_end', 'timestamp'].includes(key)) {\r\n formatted[key] = e[key]\r\n }\r\n })\r\n return formatted\r\n })\r\n\r\n // Convert standalone B/C events (outside Type A)\r\n const standaloneBCFormatted = standaloneBCEvents.map((e) => {\r\n const formatted = {\r\n type: e.type,\r\n hour_ini: this.convertToAudienceTime(e.hour_ini, ''),\r\n hour_end: this.convertToAudienceTime(e.hour_end, ''),\r\n timestamp: e.hour_ini, // For sorting\r\n }\r\n // Add any extra properties from the original event\r\n Object.keys(e).forEach((key) => {\r\n if (!['type', 'hour_ini', 'hour_end', 'timestamp'].includes(key)) {\r\n formatted[key] = e[key]\r\n }\r\n })\r\n return formatted\r\n })\r\n\r\n // Combine all events and sort chronologically\r\n // D events come before A events when they have the same timestamp\r\n const allEventsToSend = [\r\n // ...dEvents,\r\n ...aEventsFormatted,\r\n ...bcEventsFormatted,\r\n ...standaloneBCFormatted,\r\n ].sort((a, b) => {\r\n if (a.timestamp === b.timestamp) {\r\n // If same timestamp, D comes before A, A comes before B/C\r\n const typeOrder = { D: 0, A: 1, B: 2, C: 2 }\r\n return typeOrder[a.type] - typeOrder[b.type]\r\n }\r\n return a.timestamp - b.timestamp\r\n })\r\n\r\n // Remove the timestamp field used for sorting\r\n const eventsToSend = allEventsToSend.map(\r\n ({ timestamp, ...event }) => event,\r\n )\r\n\r\n this.$emit('timeToInsert', {\r\n channel: this.channel,\r\n events: eventsToSend,\r\n force: false,\r\n })\r\n\r\n if (this.jumpOnInsert) {\r\n this.changeHour(this.convertToAudienceTime(biggestHourEnd)).then(() => {\r\n this.activeFrame = this.getIndex(2, 1, Positions.current)\r\n })\r\n }\r\n\r\n if (this.removeSelectionOnInsert) {\r\n this.events = {\r\n A: [],\r\n B: [],\r\n C: [],\r\n }\r\n this.currentEventA = null\r\n this.canInsertTime = false\r\n }\r\n },\r\n insertTimeForce() {\r\n const allEvents = [...this.events.A, ...this.events.B, ...this.events.C]\r\n\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\r\n allEvents.push(this.currentEventA)\r\n }\r\n\r\n if (allEvents.length === 0) {\r\n console.warn('No events to insert')\r\n return\r\n }\r\n\r\n const allTimes = allEvents.flatMap((e) =>\r\n [e.hour_ini, e.hour_end].filter(Boolean),\r\n )\r\n const lowestHourIni = Math.min(...allTimes)\r\n const biggestHourEnd = Math.max(...allTimes)\r\n\r\n const eventsToSend = [\r\n // {\r\n // type: 'D',\r\n // hour_ini: this.convertToAudienceTime(lowestHourIni, ''),\r\n // hour_end: this.convertToAudienceTime(biggestHourEnd, ''),\r\n // },\r\n ...allEvents\r\n .filter((e) => e.hour_end)\r\n .map((e) => ({\r\n type: e.type,\r\n hour_ini: this.convertToAudienceTime(e.hour_ini, ''),\r\n hour_end: this.convertToAudienceTime(e.hour_end, ''),\r\n })),\r\n ]\r\n\r\n this.$emit('timeToInsert', {\r\n channel: this.channel,\r\n events: eventsToSend,\r\n force: true,\r\n })\r\n\r\n if (this.removeSelectionOnInsert) {\r\n this.events = {\r\n A: [],\r\n B: [],\r\n C: [],\r\n }\r\n this.currentEventA = null\r\n this.canInsertTime = false\r\n }\r\n },\r\n async getChannelMedia() {\r\n // this.media = (await ChannelService.show(this.channel)).data.MEDIA\r\n },\r\n async changeServerClick() {\r\n this.changeServer = !this.changeServer\r\n this.alternativeServer = this.changeServer\r\n\r\n sessionStorage.setItem(\r\n 'server',\r\n this.changeServer ? 'alternative' : 'default',\r\n )\r\n\r\n await this.createFramesInterface(\r\n this.convertToAudienceTime(\r\n this.$refs.frames.find((frame) => frame.index === this.activeFrame)\r\n .frame.time,\r\n '',\r\n ),\r\n )\r\n\r\n this.$nextTick(this.resize)\r\n },\r\n },\r\n computed: {\r\n swapImagesDelay() {\r\n return 0\r\n // return this.numberOfRows * this.framesPerRow * 15\r\n },\r\n active: {\r\n get() {\r\n return this.value\r\n },\r\n set(value) {\r\n this.$emit('input', value)\r\n },\r\n },\r\n settingsClosed() {\r\n return !Object.values(this.dialogs).find((v) => v)\r\n },\r\n startAudienceSeconds() {\r\n const t = this.startAudienceTime.match(/.{1,2}/g)\r\n return parseInt(t[0] * 3600 + t[1] * 60 + t[2])\r\n },\r\n loadingArray() {\r\n return Array.from(Array(this.numberOfRows * this.framesPerRow).keys())\r\n },\r\n serverOfFrames() {\r\n return sessionStorage.getItem('server')\r\n },\r\n },\r\n beforeDestroy() {\r\n this.active = false\r\n document.removeEventListener('wheel', this.scrollEvent)\r\n document.removeEventListener('keydown', this.handleKeydown)\r\n sessionStorage.setItem('server', 'default')\r\n },\r\n watch: {\r\n async secondsPerFrame() {\r\n const activeF =\r\n this.frames[this.activeFrame - this.numberOfRows * this.framesPerRow]\r\n\r\n if (activeF) {\r\n this.changeHour(this.getAudienceTime(activeF.time, 0, 0, 0))\r\n this.fInterface.setCurrentStep(this.secondsPerFrame)\r\n await this.fInterface.loadFrames()\r\n this.getFramesArray()\r\n }\r\n },\r\n async shiftFrames() {\r\n this.createFramesInterface()\r\n },\r\n framesFormat(value) {\r\n this.setFrameSelection(value)\r\n },\r\n active() {\r\n // Reset events when switching tabs\r\n if (this.removeSelectionOnInsert) {\r\n this.events = {\r\n A: [],\r\n B: [],\r\n C: [],\r\n }\r\n this.currentEventA = null\r\n }\r\n },\r\n useCache() {\r\n this.createFramesInterface()\r\n },\r\n activeFrame(value) {\r\n if (value) {\r\n this.stopPlayingBar()\r\n }\r\n },\r\n channel() {\r\n this.updatingChannel = new Promise((resolve, reject) => {\r\n try {\r\n this.createFramesInterface()\r\n resolve(true)\r\n } catch (err) {\r\n reject(err)\r\n }\r\n })\r\n },\r\n stretchFrame() {\r\n this.$nextTick(this.resize)\r\n },\r\n visualizationInsArray: {\r\n handler: function (newValue) {\r\n this.eventsArray = newValue\r\n },\r\n deep: true,\r\n },\r\n },\r\n}\r\n</script>\r\n<style scoped>\r\n.visualization-row {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex: 1 1 auto;\r\n}\r\n\r\n.visualization-col {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 5px;\r\n}\r\n\r\n.visualization-divider {\r\n display: block;\r\n flex: 1 1 100%;\r\n height: 0px;\r\n max-height: 0px;\r\n opacity: 1;\r\n transition: inherit;\r\n border-style: solid;\r\n border-width: thin 0 0 0;\r\n border-color: rgba(0, 0, 0, 0.12);\r\n margin: 0;\r\n}\r\n\r\n.visualization-divider.vertical {\r\n align-self: stretch;\r\n border-width: 0 thin 0 0;\r\n display: inline-flex;\r\n height: inherit;\r\n margin-left: -1px;\r\n max-height: 100%;\r\n max-width: 0px;\r\n vertical-align: text-bottom;\r\n width: 0px;\r\n}\r\n\r\n.visualization-card {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 12px;\r\n width: 100%;\r\n transition-property: box-shadow, opacity, -webkit-box-shadow;\r\n overflow-wrap: break-word;\r\n /*box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),\r\n 0 1px 5px 0 rgba(0, 0, 0, 0.12);*/\r\n}\r\n\r\n.visualization-justify-center,\r\n* >>> .visualization-justify-center {\r\n justify-content: center;\r\n}\r\n\r\n.visualization-align-center {\r\n align-items: center;\r\n}\r\n\r\n#visualization-container {\r\n max-width: 100% !important;\r\n margin: 0 auto !important;\r\n height: 100%;\r\n border-bottom: none;\r\n}\r\n#visualization-container > .card {\r\n border-radius: 0 !important;\r\n font-size: 12px;\r\n width: 100%;\r\n box-shadow: none;\r\n height: 100%;\r\n}\r\n\r\n#command-bar,\r\n#info-bar {\r\n background-color: #f5f5f5;\r\n box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),\r\n 0 1px 5px 0 rgba(0, 0, 0, 0.12);\r\n}\r\n#command-bar button {\r\n width: 42px;\r\n height: 36px;\r\n border: none;\r\n background: none;\r\n}\r\n\r\n#command-bar button:hover {\r\n cursor: pointer;\r\n background: rgba(0, 0, 0, 0.12);\r\n}\r\n\r\n#command-bar svg {\r\n font-size: 16px;\r\n}\r\n\r\n#command-bar {\r\n padding: 0 !important;\r\n}\r\n\r\n#info-bar {\r\n padding: 4px;\r\n font-size: 14px;\r\n position: relative;\r\n}\r\n\r\n.settings-container {\r\n position: absolute;\r\n right: 14px;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n}\r\n\r\n.settings-container > * {\r\n margin: 0 2px;\r\n cursor: pointer;\r\n}\r\n\r\n#info-bar svg {\r\n font-size: 16px;\r\n}\r\n\r\n#info-bar .divider {\r\n margin: 0 8px;\r\n}\r\n\r\nsvg:focus {\r\n border: none;\r\n}\r\n\r\n.visualization-card {\r\n border-left: 8px solid #eee;\r\n}\r\n\r\n.active-tab {\r\n border-left: 8px solid var(--visualization-primary) !important;\r\n border-image-slice: 1;\r\n}\r\n\r\n[id^='frame-'] {\r\n padding: 1px;\r\n display: flex;\r\n flex-flow: column;\r\n}\r\n\r\n.tooltip {\r\n display: block !important;\r\n z-index: 10000;\r\n}\r\n\r\n.tooltip .tooltip-inner {\r\n background: var(--visualization-primary);\r\n color: white;\r\n border-radius: 16px;\r\n padding: 5px 10px 4px;\r\n}\r\n\r\n.tooltip .tooltip-arrow {\r\n width: 0;\r\n height: 0;\r\n border-style: solid;\r\n position: absolute;\r\n margin: 5px;\r\n border-color: var(--visualization-primary);\r\n z-index: 1;\r\n}\r\n\r\n.tooltip[x-placement^='top'] {\r\n margin-bottom: 5px;\r\n}\r\n\r\n.tooltip[x-placement^='top'] .tooltip-arrow {\r\n border-width: 5px 5px 0 5px;\r\n border-left-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n bottom: -5px;\r\n left: calc(50% - 5px);\r\n margin-top: 0;\r\n margin-bottom: 0;\r\n}\r\n\r\n.tooltip[x-placement^='bottom'] {\r\n margin-top: 5px;\r\n}\r\n\r\n.tooltip[x-placement^='bottom'] .tooltip-arrow {\r\n border-width: 0 5px 5px 5px;\r\n border-left-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-top-color: transparent !important;\r\n top: -5px;\r\n left: calc(50% - 5px);\r\n margin-top: 0;\r\n margin-bottom: 0;\r\n}\r\n\r\n.tooltip[x-placement^='right'] {\r\n margin-left: 5px;\r\n}\r\n\r\n.tooltip[x-placement^='right'] .tooltip-arrow {\r\n border-width: 5px 5px 5px 0;\r\n border-left-color: transparent !important;\r\n border-top-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n left: -5px;\r\n top: calc(50% - 5px);\r\n margin-left: 0;\r\n margin-right: 0;\r\n}\r\n\r\n.tooltip[x-placement^='left'] {\r\n margin-right: 5px;\r\n}\r\n\r\n.tooltip[x-placement^='left'] .tooltip-arrow {\r\n border-width: 5px 0 5px 5px;\r\n border-top-color: transparent !important;\r\n border-right-color: transparent !important;\r\n border-bottom-color: transparent !important;\r\n right: -5px;\r\n top: calc(50% - 5px);\r\n margin-left: 0;\r\n margin-right: 0;\r\n}\r\n\r\n.tooltip.popover .popover-inner {\r\n background: #f9f9f9;\r\n color: black;\r\n padding: 24px;\r\n border-radius: 5px;\r\n box-shadow: 0 5px 30px rgba(black, 0.1);\r\n}\r\n\r\n.tooltip.popover .popover-arrow {\r\n border-color: #f9f9f9;\r\n}\r\n\r\n.tooltip[aria-hidden='true'] {\r\n visibility: hidden;\r\n opacity: 0;\r\n transition: opacity 0.15s, visibility 0.15s;\r\n}\r\n\r\n.tooltip[aria-hidden='false'] {\r\n visibility: visible;\r\n opacity: 1;\r\n transition: opacity 0.15s;\r\n}\r\n\r\n.custom-frametime {\r\n font-size: smaller;\r\n}\r\n\r\n/* DAR CORES AOS TEMPOS DEPENDENDO DO TIPO DE EVENTO */\r\n.event-A {\r\n color: #000; /* black for A */\r\n}\r\n.event-B {\r\n color: rgb(0, 115, 230); /* blue for B */\r\n}\r\n.event-C {\r\n color: rgb(0, 179, 0); /* green for C */\r\n}\r\n.event-gap {\r\n color: #555; /* Default color for gaps */\r\n}\r\n</style>\r\n"]}, media: undefined });
6324
6318
 
6325
6319
  };
6326
6320
  /* scoped */
6327
- const __vue_scope_id__ = "data-v-4c8bbfb5";
6321
+ const __vue_scope_id__ = "data-v-452faed6";
6328
6322
  /* module identifier */
6329
6323
  const __vue_module_identifier__ = undefined;
6330
6324
  /* functional template */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twab/visualization",
3
- "version": "1.15.5",
3
+ "version": "1.15.7",
4
4
  "main": "dist/visualization.js",
5
5
  "files": [
6
6
  "dist"