@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.
- package/dist/visualization.js +27 -33
- package/package.json +1 -1
package/dist/visualization.js
CHANGED
|
@@ -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
|
-
|
|
4754
|
-
|
|
4755
|
-
.
|
|
4756
|
-
.
|
|
4757
|
-
.
|
|
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 =
|
|
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-
|
|
6321
|
+
const __vue_scope_id__ = "data-v-452faed6";
|
|
6328
6322
|
/* module identifier */
|
|
6329
6323
|
const __vue_module_identifier__ = undefined;
|
|
6330
6324
|
/* functional template */
|