@twab/visualization 1.14.0 → 1.14.1
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 +21 -4
- package/package.json +1 -1
package/dist/visualization.js
CHANGED
|
@@ -4214,12 +4214,13 @@ var script = {
|
|
|
4214
4214
|
showCustomInputs: false,
|
|
4215
4215
|
stretchFrame: false,
|
|
4216
4216
|
scrollCounter: 300,
|
|
4217
|
+
eventsArray: [],
|
|
4217
4218
|
}
|
|
4218
4219
|
},
|
|
4219
4220
|
async created() {
|
|
4220
4221
|
this.changeServer = this.serverOfFrames === 'alternative';
|
|
4221
4222
|
this.alternativeServer = this.serverOfFrames === 'alternative';
|
|
4222
|
-
//
|
|
4223
|
+
//console.log('visualization array updated: ', this.visualizationInsArray)
|
|
4223
4224
|
const settings = [
|
|
4224
4225
|
{
|
|
4225
4226
|
framesPerRow: 1,
|
|
@@ -4681,6 +4682,21 @@ var script = {
|
|
|
4681
4682
|
return this.convertToAudienceTime(frameTime)
|
|
4682
4683
|
}
|
|
4683
4684
|
},
|
|
4685
|
+
giveMeType(frameTime) {
|
|
4686
|
+
if (!frameTime) {
|
|
4687
|
+
return ''
|
|
4688
|
+
}
|
|
4689
|
+
let time = this.convertToAudienceTime(frameTime);
|
|
4690
|
+
time = time.replace(/:/g, '');
|
|
4691
|
+
const foundEvent = this.eventsArray.find((event) => {
|
|
4692
|
+
return time >= event.hour_ini && time <= event.hour_end
|
|
4693
|
+
});
|
|
4694
|
+
if (foundEvent) {
|
|
4695
|
+
return `event-${foundEvent.type}`
|
|
4696
|
+
}else {
|
|
4697
|
+
return 'event-gap'
|
|
4698
|
+
}
|
|
4699
|
+
},
|
|
4684
4700
|
dateInUtc(miliSeconds) {
|
|
4685
4701
|
var date = new Date(miliSeconds);
|
|
4686
4702
|
var utc = new Date(
|
|
@@ -5518,7 +5534,7 @@ var script = {
|
|
|
5518
5534
|
},
|
|
5519
5535
|
visualizationInsArray: {
|
|
5520
5536
|
handler: function (newValue) {
|
|
5521
|
-
|
|
5537
|
+
this.eventsArray = newValue;
|
|
5522
5538
|
//console.log('visualizationInsArray changed', newValue)
|
|
5523
5539
|
},
|
|
5524
5540
|
deep: true,
|
|
@@ -6019,6 +6035,7 @@ var __vue_render__ = function () {
|
|
|
6019
6035
|
_c(
|
|
6020
6036
|
"span",
|
|
6021
6037
|
{
|
|
6038
|
+
class: _vm.giveMeType(frame.time),
|
|
6022
6039
|
staticStyle: { "text-align": "center" },
|
|
6023
6040
|
attrs: { id: _vm.activeFrame ? "aa" : 0 },
|
|
6024
6041
|
},
|
|
@@ -6104,11 +6121,11 @@ __vue_render__._withStripped = true;
|
|
|
6104
6121
|
/* style */
|
|
6105
6122
|
const __vue_inject_styles__ = function (inject) {
|
|
6106
6123
|
if (!inject) return
|
|
6107
|
-
inject("data-v-50023844_0", { source: "\n.visualization-row[data-v-50023844] {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex: 1 1 auto;\n}\n.visualization-col[data-v-50023844] {\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-50023844] {\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-50023844] {\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-50023844] {\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-50023844],\r\n*[data-v-50023844] .visualization-justify-center {\r\n justify-content: center;\n}\n.visualization-align-center[data-v-50023844] {\r\n align-items: center;\n}\n#visualization-container[data-v-50023844] {\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-50023844] {\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-50023844],\r\n#info-bar[data-v-50023844] {\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-50023844] {\r\n width: 42px;\r\n height: 36px;\r\n border: none;\r\n background: none;\n}\n#command-bar button[data-v-50023844]:hover {\r\n cursor: pointer;\r\n background: rgba(0, 0, 0, 0.12);\n}\n#command-bar svg[data-v-50023844] {\r\n font-size: 16px;\n}\n#command-bar[data-v-50023844] {\r\n padding: 0 !important;\n}\n#info-bar[data-v-50023844] {\r\n padding: 4px;\r\n font-size: 14px;\r\n position: relative;\n}\n.settings-container[data-v-50023844] {\r\n position: absolute;\r\n right: 14px;\r\n top: 50%;\r\n transform: translateY(-50%);\n}\n.settings-container > *[data-v-50023844] {\r\n margin: 0 2px;\r\n cursor: pointer;\n}\n#info-bar svg[data-v-50023844] {\r\n font-size: 16px;\n}\n#info-bar .divider[data-v-50023844] {\r\n margin: 0 8px;\n}\nsvg[data-v-50023844]:focus {\r\n border: none;\n}\n.visualization-card[data-v-50023844] {\r\n border-left: 8px solid #eee;\n}\n.active-tab[data-v-50023844] {\r\n border-left: 8px solid var(--visualization-primary) !important;\r\n border-image-slice: 1;\n}\n[id^='frame-'][data-v-50023844] {\r\n padding: 1px;\r\n display: flex;\r\n flex-flow: column;\n}\n.tooltip[data-v-50023844] {\r\n display: block !important;\r\n z-index: 10000;\n}\n.tooltip .tooltip-inner[data-v-50023844] {\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-50023844] {\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-50023844] {\r\n margin-bottom: 5px;\n}\n.tooltip[x-placement^='top'] .tooltip-arrow[data-v-50023844] {\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-50023844] {\r\n margin-top: 5px;\n}\n.tooltip[x-placement^='bottom'] .tooltip-arrow[data-v-50023844] {\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-50023844] {\r\n margin-left: 5px;\n}\n.tooltip[x-placement^='right'] .tooltip-arrow[data-v-50023844] {\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-50023844] {\r\n margin-right: 5px;\n}\n.tooltip[x-placement^='left'] .tooltip-arrow[data-v-50023844] {\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-50023844] {\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-50023844] {\r\n border-color: #f9f9f9;\n}\n.tooltip[aria-hidden='true'][data-v-50023844] {\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-50023844] {\r\n visibility: visible;\r\n opacity: 1;\r\n transition: opacity 0.15s;\n}\n.custom-frametime[data-v-50023844] {\r\n font-size: smaller;\n}\r\n", map: {"version":3,"sources":["C:\\Workspace\\visualization\\src\\Visualization.vue"],"names":[],"mappings":";AA2sDA;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","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=\"active && settingsClosed\"\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 :id=\"activeFrame ? 'aa' : 0\" style=\"text-align: center\">\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 }\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 beforeDestroy() {\r\n document.removeEventListener('wheel', this.scrollEvent)\r\n document.removeEventListener('keydown', this.handleKeydown)\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(\r\n (config) => config.shortcut.toUpperCase() === event.key.toUpperCase()\r\n )\r\n\r\n if (matchedConfig) {\r\n event.preventDefault()\r\n this.toggleEvent(matchedConfig.event)\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 this.aspectRatio =\r\n div.getElementsByTagName('img')[0].naturalWidth /\r\n div.getElementsByTagName('img')[0].naturalHeight\r\n\r\n this.activeFrame = this.getIndex(1, 0, Positions.current)\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 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) {\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)\r\n } else if (eventType === 'B') {\r\n this.handleTypeB(frameTime, eventConfig)\r\n } else if (eventType === 'C') {\r\n this.handleTypeC(frameTime, eventConfig)\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) {\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\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 } else {\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 }\r\n this.activeEvents.A = true\r\n }\r\n },\r\n handleTypeB(frameTime, eventConfig) {\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 // 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 } else {\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 })\r\n this.activeEvents.B = true\r\n }\r\n },\r\n handleTypeC(frameTime, eventConfig) {\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 // Close the event C\r\n lastEventC.hour_end = frameTime\r\n delete this.activeEvents.C\r\n } else {\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 }\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 })\r\n this.activeEvents.C = true\r\n }\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(1, 0, Positions.current)\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.error('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 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 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 async setStartTime(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 // this.frames = this.loadingArray\r\n\r\n await this.fInterface.changeTime(setTime)\r\n\r\n this.getFramesArray()\r\n\r\n this.activeFrame = this.getIndex(1, 0, Positions.current)\r\n\r\n this.activeVideo = null\r\n\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(1, 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 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 //! aqui amigo prica\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</style>\r\n"]}, media: undefined });
|
|
6124
|
+
inject("data-v-83e856c6_0", { source: "\n.visualization-row[data-v-83e856c6] {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex: 1 1 auto;\n}\n.visualization-col[data-v-83e856c6] {\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-83e856c6] {\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-83e856c6] {\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-83e856c6] {\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-83e856c6],\r\n*[data-v-83e856c6] .visualization-justify-center {\r\n justify-content: center;\n}\n.visualization-align-center[data-v-83e856c6] {\r\n align-items: center;\n}\n#visualization-container[data-v-83e856c6] {\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-83e856c6] {\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-83e856c6],\r\n#info-bar[data-v-83e856c6] {\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-83e856c6] {\r\n width: 42px;\r\n height: 36px;\r\n border: none;\r\n background: none;\n}\n#command-bar button[data-v-83e856c6]:hover {\r\n cursor: pointer;\r\n background: rgba(0, 0, 0, 0.12);\n}\n#command-bar svg[data-v-83e856c6] {\r\n font-size: 16px;\n}\n#command-bar[data-v-83e856c6] {\r\n padding: 0 !important;\n}\n#info-bar[data-v-83e856c6] {\r\n padding: 4px;\r\n font-size: 14px;\r\n position: relative;\n}\n.settings-container[data-v-83e856c6] {\r\n position: absolute;\r\n right: 14px;\r\n top: 50%;\r\n transform: translateY(-50%);\n}\n.settings-container > *[data-v-83e856c6] {\r\n margin: 0 2px;\r\n cursor: pointer;\n}\n#info-bar svg[data-v-83e856c6] {\r\n font-size: 16px;\n}\n#info-bar .divider[data-v-83e856c6] {\r\n margin: 0 8px;\n}\nsvg[data-v-83e856c6]:focus {\r\n border: none;\n}\n.visualization-card[data-v-83e856c6] {\r\n border-left: 8px solid #eee;\n}\n.active-tab[data-v-83e856c6] {\r\n border-left: 8px solid var(--visualization-primary) !important;\r\n border-image-slice: 1;\n}\n[id^='frame-'][data-v-83e856c6] {\r\n padding: 1px;\r\n display: flex;\r\n flex-flow: column;\n}\n.tooltip[data-v-83e856c6] {\r\n display: block !important;\r\n z-index: 10000;\n}\n.tooltip .tooltip-inner[data-v-83e856c6] {\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-83e856c6] {\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-83e856c6] {\r\n margin-bottom: 5px;\n}\n.tooltip[x-placement^='top'] .tooltip-arrow[data-v-83e856c6] {\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-83e856c6] {\r\n margin-top: 5px;\n}\n.tooltip[x-placement^='bottom'] .tooltip-arrow[data-v-83e856c6] {\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-83e856c6] {\r\n margin-left: 5px;\n}\n.tooltip[x-placement^='right'] .tooltip-arrow[data-v-83e856c6] {\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-83e856c6] {\r\n margin-right: 5px;\n}\n.tooltip[x-placement^='left'] .tooltip-arrow[data-v-83e856c6] {\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-83e856c6] {\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-83e856c6] {\r\n border-color: #f9f9f9;\n}\n.tooltip[aria-hidden='true'][data-v-83e856c6] {\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-83e856c6] {\r\n visibility: visible;\r\n opacity: 1;\r\n transition: opacity 0.15s;\n}\n.custom-frametime[data-v-83e856c6] {\r\n font-size: smaller;\n}\r\n\r\n/* DAR CORES AOS TEMPOS DEPENDENDO DO TIPO DE EVENTO */\n.event-A[data-v-83e856c6] {\r\n color: #000; /* black for A */\n}\n.event-B[data-v-83e856c6]{\r\n color: rgb(0, 115, 230); /* blue for B */\n}\n.event-C[data-v-83e856c6]{\r\n color: rgb(0, 179, 0); /* green for C */\n}\n.event-gap[data-v-83e856c6] {\r\n color: #555; /* Default color for gaps */\n}\r\n", map: {"version":3,"sources":["C:\\Workspace\\visualization\\src\\Visualization.vue"],"names":[],"mappings":";AA2tDA;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=\"active && settingsClosed\"\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 <span :class=\"giveMeType(frame.time)\" :id=\"activeFrame ? 'aa' : 0\" style=\"text-align: center\">\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 }}\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 beforeDestroy() {\r\n document.removeEventListener('wheel', this.scrollEvent)\r\n document.removeEventListener('keydown', this.handleKeydown)\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(\r\n (config) => config.shortcut.toUpperCase() === event.key.toUpperCase()\r\n )\r\n\r\n if (matchedConfig) {\r\n event.preventDefault()\r\n this.toggleEvent(matchedConfig.event)\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 this.aspectRatio =\r\n div.getElementsByTagName('img')[0].naturalWidth /\r\n div.getElementsByTagName('img')[0].naturalHeight\r\n\r\n this.activeFrame = this.getIndex(1, 0, Positions.current)\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) {\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)\r\n } else if (eventType === 'B') {\r\n this.handleTypeB(frameTime, eventConfig)\r\n } else if (eventType === 'C') {\r\n this.handleTypeC(frameTime, eventConfig)\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) {\r\n if (this.currentEventA && this.currentEventA.hour_ini) {\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 } else {\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 }\r\n this.activeEvents.A = true\r\n }\r\n },\r\n handleTypeB(frameTime, eventConfig) {\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 // 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 } else {\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 })\r\n this.activeEvents.B = true\r\n }\r\n },\r\n handleTypeC(frameTime, eventConfig) {\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 // Close the event C\r\n lastEventC.hour_end = frameTime\r\n delete this.activeEvents.C\r\n } else {\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 }\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 })\r\n this.activeEvents.C = true\r\n }\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(1, 0, Positions.current)\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.error('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 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 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 async setStartTime(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 // this.frames = this.loadingArray\r\n\r\n await this.fInterface.changeTime(setTime)\r\n\r\n this.getFramesArray()\r\n\r\n this.activeFrame = this.getIndex(1, 0, Positions.current)\r\n\r\n this.activeVideo = null\r\n\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(1, 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 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 //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 });
|
|
6108
6125
|
|
|
6109
6126
|
};
|
|
6110
6127
|
/* scoped */
|
|
6111
|
-
const __vue_scope_id__ = "data-v-
|
|
6128
|
+
const __vue_scope_id__ = "data-v-83e856c6";
|
|
6112
6129
|
/* module identifier */
|
|
6113
6130
|
const __vue_module_identifier__ = undefined;
|
|
6114
6131
|
/* functional template */
|