@twab/visualization 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/visualization.js +201 -118
- package/package.json +1 -1
package/dist/visualization.js
CHANGED
|
@@ -340,7 +340,6 @@ var script$5 = {
|
|
|
340
340
|
this.videoOnModal = false;
|
|
341
341
|
this.$refs.imageDetailsDialog.close();
|
|
342
342
|
} else {
|
|
343
|
-
// Open dialog directly with video instead of image
|
|
344
343
|
this.videoOnModal = true;
|
|
345
344
|
this.$refs.imageDetailsDialog.showModal();
|
|
346
345
|
// Start playing the video (it will be paused in playOrPause)
|
|
@@ -873,7 +872,6 @@ var __vue_render__$5 = function () {
|
|
|
873
872
|
staticStyle: { width: "100%", "aspect-ratio": "11/9" },
|
|
874
873
|
attrs: {
|
|
875
874
|
src: _vm.videoUrlString,
|
|
876
|
-
autoplay: "",
|
|
877
875
|
controls: "true",
|
|
878
876
|
width: "200%",
|
|
879
877
|
},
|
|
@@ -892,11 +890,11 @@ __vue_render__$5._withStripped = true;
|
|
|
892
890
|
/* style */
|
|
893
891
|
const __vue_inject_styles__$5 = function (inject) {
|
|
894
892
|
if (!inject) return
|
|
895
|
-
inject("data-v-1e3e4b44_0", { source: "\n.frame-container > div[data-v-1e3e4b44] {\r\n max-height: 100%;\r\n height: 100%; /* Add this */\n}\n.frame-container[data-v-1e3e4b44] {\r\n background-color: black;\r\n display: flex;\r\n justify-content: center;\n}\n.frame-content[data-v-1e3e4b44],\r\n.dummy-frame[data-v-1e3e4b44] {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\n}\n.frame-content video[data-v-1e3e4b44] {\r\n flex-grow: 1;\r\n object-fit: fill;\r\n z-index: 0;\n}\n.overlay[data-v-1e3e4b44] {\r\n width: 100%;\r\n height: 100%;\r\n z-index: 5;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n border: 4px solid transparent;\r\n pointer-events: none;\n}\n.active .overlay[data-v-1e3e4b44] {\r\n border-color: #ffc966;\n}\r\n/* .between-time .overlay {\r\n border-top-color: var(--visualization-secondary);\r\n}\r\n.between-time.active .overlay {\r\n border-color: var(--visualization-secondary);\r\n} */\r\n\r\n/* Event Type A - Blue */\n.event-a .overlay[data-v-1e3e4b44] {\r\n border-top-color: #ff6f00;\n}\n.event-a.active .overlay[data-v-1e3e4b44] {\r\n border-color: #ff6f00;\n}\r\n\r\n/* Event Type B - Green */\n.event-b .overlay[data-v-1e3e4b44] {\r\n border-top-color: #0073e6;\n}\n.event-b.active .overlay[data-v-1e3e4b44] {\r\n border-color: #0073e6;\n}\r\n\r\n/* Event Type C - Orange */\n.event-c .overlay[data-v-1e3e4b44] {\r\n border-top-color: #00b300;\n}\n.event-c.active .overlay[data-v-1e3e4b44] {\r\n border-color: #00b300;\n}\r\n\r\n/* new styles */\n.frame-content[data-v-1e3e4b44] img,\r\n.dummy-frame[data-v-1e3e4b44] img {\r\n width: 100% !important;\r\n height: 100% !important;\r\n object-fit: fill !important;\r\n aspect-ratio: auto !important;\r\n display: block !important;\n}\n.frame-content[data-v-1e3e4b44] div,\r\n.dummy-frame[data-v-1e3e4b44] div {\r\n width: 100% !important;\r\n height: 100% !important;\n}\n.frame-content video[data-v-1e3e4b44] {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: fill;\r\n z-index: 0;\n}\n.overlay[data-v-1e3e4b44] {\r\n width: 100%;\r\n height: 100%;\r\n z-index: 5;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n border: 4px solid transparent;\r\n pointer-events: none;\n}\r\n/* «««««««« */\n.div-frame[data-v-1e3e4b44] img {\r\n width: 90vh !important;\n}\r\n", map: {"version":3,"sources":["C:\\Workspace\\visualization\\src\\components\\Frame.vue"],"names":[],"mappings":";AA4eA;EACA,gBAAA;EACA,YAAA,EAAA,aAAA;AACA;AACA;EACA,uBAAA;EACA,aAAA;EACA,uBAAA;AACA;AACA;;EAEA,aAAA;EACA,mBAAA;EACA,uBAAA;AACA;AACA;EACA,YAAA;EACA,gBAAA;EACA,UAAA;AACA;AACA;EACA,WAAA;EACA,YAAA;EACA,UAAA;EACA,kBAAA;EACA,MAAA;EACA,OAAA;EACA,6BAAA;EACA,oBAAA;AACA;AACA;EACA,qBAAA;AACA;AACA;;;;;GAKA;;AAEA,wBAAA;AACA;EACA,yBAAA;AACA;AACA;EACA,qBAAA;AACA;;AAEA,yBAAA;AACA;EACA,yBAAA;AACA;AACA;EACA,qBAAA;AACA;;AAEA,0BAAA;AACA;EACA,yBAAA;AACA;AACA;EACA,qBAAA;AACA;;AAEA,eAAA;AACA;;EAEA,sBAAA;EACA,uBAAA;EACA,2BAAA;EACA,6BAAA;EACA,yBAAA;AACA;AAEA;;EAEA,sBAAA;EACA,uBAAA;AACA;AAEA;EACA,WAAA;EACA,YAAA;EACA,gBAAA;EACA,UAAA;AACA;AAEA;EACA,WAAA;EACA,YAAA;EACA,UAAA;EACA,kBAAA;EACA,MAAA;EACA,OAAA;EACA,6BAAA;EACA,oBAAA;AACA;AACA,aAAA;AACA;EACA,sBAAA;AACA","file":"Frame.vue","sourcesContent":["<template>\r\n <div\r\n :style=\"{\r\n maxHeight: `${maxHeight}px`,\r\n width: '100%',\r\n position: 'relative',\r\n }\"\r\n :class=\"{\r\n 'frame-container': true,\r\n 'initial-time': initialTime,\r\n 'end-time': endTime,\r\n 'checkpoint-time': checkpointTime,\r\n 'between-time': betweenTime,\r\n 'event-a': eventType === 'A',\r\n 'event-b': eventType === 'B',\r\n 'event-c': eventType === 'C',\r\n active: active,\r\n }\"\r\n >\r\n <GlobalEvents\r\n v-if=\"active && activeTab && parentComponent.settingsClosed\"\r\n @keydown.32.prevent=\"playOrPause\"\r\n @keydown.27.prevent=\"stop\"\r\n @keydown.79=\"toogleDetailFrame\"\r\n @keydown.13.prevent=\"jumpFrameToStart\"\r\n />\r\n <GlobalEvents\r\n v-if=\"showFrame\"\r\n @keydown.27.prevent=\"toogleDetailFrame\"\r\n @keydown.13.prevent=\"toogleDetailFrame\"\r\n />\r\n\r\n <vue-loading\r\n :active=\"isLoading\"\r\n spinner=\"ring\"\r\n color=\"var(--visualization-primary)\"\r\n background-color=\"#cacaca\"\r\n :style=\"{\r\n 'z-index': 0,\r\n minHeight: `${height}px`,\r\n minWidth: `${width}px`,\r\n }\"\r\n :text=\"videoIsLoading ? $t('infoBar.createVideo') : ''\"\r\n />\r\n <div\r\n :style=\"{\r\n backgroundColor: 'black',\r\n width: stretchFrame ? '100%' : 'auto',\r\n height: stretchFrame ? '100%' : 'auto',\r\n }\"\r\n >\r\n <div\r\n v-if=\"!isLoading\"\r\n :class=\"{\r\n 'frame-content': frame.title === -1 || videoStatus !== Status.stopped,\r\n }\"\r\n :style=\"\r\n stretchFrame\r\n ? {\r\n height: `${maxHeight}px`,\r\n width: '100%',\r\n color: 'white',\r\n }\r\n : {\r\n minHeight: `${height}px`,\r\n minWidth: `${width}px`,\r\n maxHeight: `${height}px`,\r\n maxWidth: `${width}px`,\r\n color: 'white',\r\n }\r\n \"\r\n @dblclick=\"playOrPause\"\r\n >\r\n <div\r\n v-if=\"frame.image && videoStatus === Status.stopped\"\r\n :class=\"{ 'dummy-frame': frame.title === -1 }\"\r\n v-html=\"frame.image\"\r\n style=\"width: 100%; height: 100%\"\r\n />\r\n\r\n <video\r\n v-if=\"\r\n !videoOnModal && videoStatus !== Status.stopped && !videoIsLoading\r\n \"\r\n ref=\"videoPlayer\"\r\n :maxHeight=\"`${height}px`\"\r\n :maxWidth=\"`${width}px`\"\r\n :src=\"videoUrlString\"\r\n @timeupdate=\"updateTimeLabel\"\r\n @ended=\"videoEnded\"\r\n @click=\"playOrPause\"\r\n @canplay=\"canPlay\"\r\n @error=\"() => stop(false)\"\r\n :style=\"{ width: '100%', aspectRatio: aspectRatioFrac }\"\r\n ></video>\r\n <div v-if=\"!loading\" class=\"overlay\"></div>\r\n </div>\r\n </div>\r\n\r\n <dialog\r\n ref=\"imageDetailsDialog\"\r\n style=\"\r\n top: 50%;\r\n left: 50%;\r\n transform: translateY(-50%) translateX(-50%);\r\n border: none;\r\n background: grey;\r\n width: 60%;\r\n \"\r\n >\r\n <div\r\n v-if=\"videoStatus == Status.stopped\"\r\n style=\"justify-content: center; display: flex\"\r\n :class=\"{\r\n 'dummy-frame': frame.title === -1,\r\n 'div-frame': frame.title !== -1,\r\n }\"\r\n v-html=\"frame.image\"\r\n @dblclick=\"changeToVideo\"\r\n ></div>\r\n <video\r\n v-if=\"videoOnModal && videoStatus !== Status.stopped && !videoIsLoading\"\r\n ref=\"videoPlayer\"\r\n :src=\"videoUrlString\"\r\n autoplay\r\n @ended=\"videoEnded\"\r\n style=\"width: 100%; aspect-ratio: 11/9\"\r\n controls=\"true\"\r\n width=\"200%\"\r\n ></video>\r\n </dialog>\r\n </div>\r\n</template>\r\n<script>\r\nimport VueLoading from 'vue-loading-twa'\r\n\r\nconst Status = Object.freeze({\r\n stopped: 0,\r\n playing: 1,\r\n paused: 2,\r\n})\r\n\r\nexport default {\r\n components: {\r\n VueLoading,\r\n },\r\n name: 'frame-component',\r\n props: {\r\n frame: {\r\n type: [Object, Number],\r\n required: true,\r\n },\r\n index: {\r\n type: Number,\r\n required: true,\r\n },\r\n loading: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n gridSettings: {\r\n type: Object,\r\n required: true,\r\n },\r\n initialTime: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n endTime: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n checkpointTime: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n betweenTime: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n active: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n activeTab: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n videoUrl: {\r\n type: [String, Promise],\r\n required: true,\r\n },\r\n playbackRate: {\r\n type: Number,\r\n default: 1,\r\n },\r\n videoControls: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n aspectRatio: {\r\n type: Number,\r\n default: 11 / 9,\r\n },\r\n stretchFrame: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n eventType: {\r\n type: String,\r\n default: null,\r\n },\r\n },\r\n data() {\r\n return {\r\n Status,\r\n width: 0,\r\n height: 0,\r\n maxWidth: 0,\r\n fullMaxWidth: 0,\r\n maxHeight: 0,\r\n showFrame: false,\r\n videoStatus: Status.stopped,\r\n videoStartTime: null,\r\n videoCurrentTime: null,\r\n videoUrlString: '',\r\n parentComponent: null,\r\n videoIsLoading: false,\r\n currentBlock: null,\r\n lastTimeEnterPressed: null,\r\n videoOnModal: false,\r\n }\r\n },\r\n created() {\r\n this.parentComponent = this.$parent\r\n },\r\n watch: {\r\n playbackRate(val) {\r\n if (\r\n this.videoStatus === Status.playing ||\r\n this.videoStatus === Status.paused\r\n ) {\r\n this.$refs.videoPlayer.playbackRate = val\r\n }\r\n },\r\n },\r\n computed: {\r\n isLoading() {\r\n return (\r\n ((!this.frame.image || !this.frame.time) && this.frame.title !== -1) ||\r\n this.loading ||\r\n this.videoIsLoading\r\n )\r\n },\r\n aspectRatioFrac() {\r\n var gcd = function (a, b) {\r\n if (b < 0.0000001) return a\r\n\r\n return gcd(b, Math.floor(a % b))\r\n }\r\n\r\n var denominator = Math.pow(10, this.aspectRatio.toString().length - 2)\r\n var numerator = this.aspectRatio * denominator\r\n\r\n var divisor = gcd(numerator, denominator)\r\n\r\n return `${Math.floor(numerator / divisor)}/${Math.floor(\r\n denominator / divisor\r\n )}`\r\n },\r\n },\r\n methods: {\r\n canPlay() {\r\n this.$refs.videoPlayer.play()\r\n },\r\n changeToVideo() {\r\n this.videoOnModal = true\r\n this.playOrPause()\r\n },\r\n jumpFrameToStart() {\r\n if (this.active) {\r\n const currentTime = new Date().getTime()\r\n\r\n if (currentTime - this.lastTimeEnterPressed <= 500) {\r\n this.parentComponent.changeHour(\r\n this.parentComponent.convertToAudienceTime(this.frame.time)\r\n )\r\n }\r\n\r\n this.lastTimeEnterPressed = currentTime\r\n }\r\n },\r\n changeSettings(value) {\r\n this.videoCurrentTime = value\r\n },\r\n toogleDetailFrame() {\r\n this.stop()\r\n if (this.showFrame) {\r\n this.videoOnModal = false\r\n this.$refs.imageDetailsDialog.close()\r\n } else {\r\n // Open dialog directly with video instead of image\r\n this.videoOnModal = true\r\n this.$refs.imageDetailsDialog.showModal()\r\n // Start playing the video (it will be paused in playOrPause)\r\n this.$nextTick(() => {\r\n this.playOrPause()\r\n // Pause immediately after starting\r\n this.$nextTick(() => {\r\n if (this.$refs.videoPlayer && this.videoStatus === Status.playing) {\r\n this.$refs.videoPlayer.pause()\r\n this.videoStatus = Status.paused\r\n this.$emit('playPauseStatus', false)\r\n }\r\n })\r\n })\r\n }\r\n this.showFrame = !this.showFrame\r\n },\r\n getMaxWidth() {\r\n const commandBarSize = document.getElementById('command-bar')\r\n\r\n return commandBarSize?.clientWidth\r\n },\r\n resize(height) {\r\n const commandBarSize = document.getElementById('command-bar')\r\n const infoBarSize = document.getElementById('info-bar')\r\n\r\n this.fullMaxWidth = infoBarSize?.clientWidth\r\n this.maxWidth =\r\n (this.fullMaxWidth - this.gridSettings.framesPerRow * 10) /\r\n this.gridSettings.framesPerRow\r\n\r\n const rowSpacing = 29 * this.gridSettings.numberOfRows\r\n\r\n let heightAvailable =\r\n height -\r\n (commandBarSize?.offsetHeight || 0) -\r\n infoBarSize?.offsetHeight -\r\n rowSpacing -\r\n 2 // bottom padding\r\n\r\n if (this.gridSettings.numberOfRows > 1) {\r\n heightAvailable += 12\r\n }\r\n\r\n this.maxHeight = heightAvailable / this.gridSettings.numberOfRows\r\n const minHeightPerRow = this.gridSettings.numberOfRows > 2 ? 50 : 60\r\n this.maxHeight = Math.max(this.maxHeight, minHeightPerRow)\r\n\r\n if (this.maxWidth / this.aspectRatio > this.maxHeight) {\r\n this.height = this.maxHeight\r\n this.width = this.maxHeight * this.aspectRatio\r\n } else {\r\n this.width = this.maxWidth\r\n this.height = this.maxWidth / this.aspectRatio\r\n }\r\n this.applyImageStyles()\r\n this.maxHeight = this.height\r\n },\r\n applyImageStyles() {\r\n this.$nextTick(() => {\r\n const frameImages = this.$el.querySelectorAll(\r\n '.frame-container img, .dummy-frame img'\r\n )\r\n\r\n frameImages.forEach((img) => {\r\n img.style.width = '100%'\r\n img.style.height = '100%'\r\n img.style.objectFit = 'fill'\r\n img.style.aspectRatio = 'auto'\r\n img.style.display = 'block'\r\n })\r\n })\r\n },\r\n async playOrPause() {\r\n switch (this.videoStatus) {\r\n case Status.stopped:\r\n this.videoIsLoading = true\r\n try {\r\n this.videoUrlString = await this.videoUrl\r\n const blockInfo =\r\n await this.parentComponent.fInterface?.getVideoStartTime(\r\n this.frame\r\n )\r\n\r\n this.videoStartTime = blockInfo.start\r\n this.videoUrlString =\r\n this.parentComponent.fInterface.getVideoRequestByUrl(\r\n blockInfo.url,\r\n this.frame.number + this.parentComponent.shiftFrames\r\n )\r\n } catch (error) {\r\n console.log(error)\r\n }\r\n this.videoIsLoading = false\r\n this.videoStatus = Status.playing\r\n this.$nextTick(() => {\r\n this.$refs.videoPlayer.onloaded = (e) => {\r\n this.$refs.videoPlayer.playbackRate = this.playbackRate\r\n // * atualizar slider\r\n this.$emit('updateSlider', this.videoStartTime, this.frame.time)\r\n //*\r\n this.$emit('startPlaying', this.frame, e.target.duration)\r\n this.$emit('playPauseStatus', true)\r\n }\r\n })\r\n break\r\n case Status.paused:\r\n this.$refs.videoPlayer.play()\r\n this.$refs.videoPlayer.playbackRate = this.playbackRate\r\n this.videoStatus = Status.playing\r\n this.$emit('playPauseStatus', true)\r\n break\r\n case Status.playing:\r\n this.$refs.videoPlayer.pause()\r\n\r\n this.videoStatus = Status.paused\r\n this.$emit('playPauseStatus', false)\r\n break\r\n }\r\n },\r\n stop(jump = true) {\r\n this.videoOnModal = false\r\n if (\r\n this.videoStatus === Status.playing ||\r\n this.videoStatus === Status.paused\r\n ) {\r\n if (jump) {\r\n this.parentComponent.changeHour(\r\n this.parentComponent.convertToAudienceTime(\r\n parseInt(this.videoCurrentTime)\r\n )\r\n )\r\n }\r\n this.videoStatus = Status.stopped\r\n this.videoUrlString = null\r\n this.videoCurrentTime = null\r\n this.videoStartTime = null\r\n }\r\n this.$emit('stopPlaying')\r\n },\r\n //eslint-disable-next-line\r\n videoJumpTo(time) {\r\n this.videoCurrentTime = this.videoStartTime + time\r\n this.$refs.videoPlayer.currentTime = time\r\n },\r\n async videoJumpToTimeStamp(time) {\r\n let frame = {\r\n time: time,\r\n }\r\n if (!time) return\r\n if (\r\n !(\r\n this.currentBlock &&\r\n time >= this.currentBlock.start &&\r\n time <= this.currentBlock.end\r\n )\r\n ) {\r\n this.currentBlock = await this.parentComponent.fInterface?.getBlockInfo(\r\n frame\r\n )\r\n this.videoStartTime = this.currentBlock.start\r\n this.videoUrlString =\r\n this.parentComponent.fInterface.getVideoUrlForTime(\r\n this.videoStartTime\r\n )\r\n }\r\n this.videoCurrentTime = time - this.videoStartTime\r\n this.$refs.videoPlayer.currentTime = this.videoCurrentTime\r\n },\r\n updateTimeLabel(e) {\r\n this.videoCurrentTime = this.videoStartTime + e.target.currentTime\r\n this.parentComponent.updateVideoTime(this.index, this.videoCurrentTime)\r\n this.parentComponent.updateVideoStatus(e.target.currentTime)\r\n },\r\n async videoEnded() {\r\n this.videoStartTime =\r\n await this.parentComponent.fInterface?.getNextVideoStartTime(\r\n this.frame.time - this.parentComponent.shiftFrames\r\n )\r\n this.videoUrlString = this.parentComponent.fInterface.getVideoUrlForTime(\r\n this.videoStartTime\r\n )\r\n // * Ao acabar o vídeo tenho de mandar atualizar slider\r\n // * aqui o video current time tambem vai ser o videoStartTime\r\n this.$emit('updateSlider', this.videoStartTime, this.videoStartTime)\r\n },\r\n },\r\n}\r\n</script>\r\n<style scoped>\r\n.frame-container > div {\r\n max-height: 100%;\r\n height: 100%; /* Add this */\r\n}\r\n.frame-container {\r\n background-color: black;\r\n display: flex;\r\n justify-content: center;\r\n}\r\n.frame-content,\r\n.dummy-frame {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n.frame-content video {\r\n flex-grow: 1;\r\n object-fit: fill;\r\n z-index: 0;\r\n}\r\n.overlay {\r\n width: 100%;\r\n height: 100%;\r\n z-index: 5;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n border: 4px solid transparent;\r\n pointer-events: none;\r\n}\r\n.active .overlay {\r\n border-color: #ffc966;\r\n}\r\n/* .between-time .overlay {\r\n border-top-color: var(--visualization-secondary);\r\n}\r\n.between-time.active .overlay {\r\n border-color: var(--visualization-secondary);\r\n} */\r\n\r\n/* Event Type A - Blue */\r\n.event-a .overlay {\r\n border-top-color: #ff6f00;\r\n}\r\n.event-a.active .overlay {\r\n border-color: #ff6f00;\r\n}\r\n\r\n/* Event Type B - Green */\r\n.event-b .overlay {\r\n border-top-color: #0073e6;\r\n}\r\n.event-b.active .overlay {\r\n border-color: #0073e6;\r\n}\r\n\r\n/* Event Type C - Orange */\r\n.event-c .overlay {\r\n border-top-color: #00b300;\r\n}\r\n.event-c.active .overlay {\r\n border-color: #00b300;\r\n}\r\n\r\n/* new styles */\r\n.frame-content >>> img,\r\n.dummy-frame >>> img {\r\n width: 100% !important;\r\n height: 100% !important;\r\n object-fit: fill !important;\r\n aspect-ratio: auto !important;\r\n display: block !important;\r\n}\r\n\r\n.frame-content >>> div,\r\n.dummy-frame >>> div {\r\n width: 100% !important;\r\n height: 100% !important;\r\n}\r\n\r\n.frame-content video {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: fill;\r\n z-index: 0;\r\n}\r\n\r\n.overlay {\r\n width: 100%;\r\n height: 100%;\r\n z-index: 5;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n border: 4px solid transparent;\r\n pointer-events: none;\r\n}\r\n/* «««««««« */\r\n.div-frame >>> img {\r\n width: 90vh !important;\r\n}\r\n</style>\r\n"]}, media: undefined });
|
|
893
|
+
inject("data-v-c42c15c0_0", { source: "\n.frame-container > div[data-v-c42c15c0] {\r\n max-height: 100%;\r\n height: 100%; /* Add this */\n}\n.frame-container[data-v-c42c15c0] {\r\n background-color: black;\r\n display: flex;\r\n justify-content: center;\n}\n.frame-content[data-v-c42c15c0],\r\n.dummy-frame[data-v-c42c15c0] {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\n}\n.frame-content video[data-v-c42c15c0] {\r\n flex-grow: 1;\r\n object-fit: fill;\r\n z-index: 0;\n}\n.overlay[data-v-c42c15c0] {\r\n width: 100%;\r\n height: 100%;\r\n z-index: 5;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n border: 4px solid transparent;\r\n pointer-events: none;\n}\n.active .overlay[data-v-c42c15c0] {\r\n border-color: #ffc966;\n}\r\n/* .between-time .overlay {\r\n border-top-color: var(--visualization-secondary);\r\n}\r\n.between-time.active .overlay {\r\n border-color: var(--visualization-secondary);\r\n} */\r\n\r\n/* Event Type A - Blue */\n.event-a .overlay[data-v-c42c15c0] {\r\n border-top-color: #ff6f00;\n}\n.event-a.active .overlay[data-v-c42c15c0] {\r\n border-color: #ff6f00;\n}\r\n\r\n/* Event Type B - Green */\n.event-b .overlay[data-v-c42c15c0] {\r\n border-top-color: #0073e6;\n}\n.event-b.active .overlay[data-v-c42c15c0] {\r\n border-color: #0073e6;\n}\r\n\r\n/* Event Type C - Orange */\n.event-c .overlay[data-v-c42c15c0] {\r\n border-top-color: #00b300;\n}\n.event-c.active .overlay[data-v-c42c15c0] {\r\n border-color: #00b300;\n}\r\n\r\n/* new styles */\n.frame-content[data-v-c42c15c0] img,\r\n.dummy-frame[data-v-c42c15c0] img {\r\n width: 100% !important;\r\n height: 100% !important;\r\n object-fit: fill !important;\r\n aspect-ratio: auto !important;\r\n display: block !important;\n}\n.frame-content[data-v-c42c15c0] div,\r\n.dummy-frame[data-v-c42c15c0] div {\r\n width: 100% !important;\r\n height: 100% !important;\n}\n.frame-content video[data-v-c42c15c0] {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: fill;\r\n z-index: 0;\n}\n.overlay[data-v-c42c15c0] {\r\n width: 100%;\r\n height: 100%;\r\n z-index: 5;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n border: 4px solid transparent;\r\n pointer-events: none;\n}\r\n/* «««««««« */\n.div-frame[data-v-c42c15c0] img {\r\n width: 90vh !important;\n}\r\n", map: {"version":3,"sources":["C:\\Workspace\\visualization\\src\\components\\Frame.vue"],"names":[],"mappings":";AA0eA;EACA,gBAAA;EACA,YAAA,EAAA,aAAA;AACA;AACA;EACA,uBAAA;EACA,aAAA;EACA,uBAAA;AACA;AACA;;EAEA,aAAA;EACA,mBAAA;EACA,uBAAA;AACA;AACA;EACA,YAAA;EACA,gBAAA;EACA,UAAA;AACA;AACA;EACA,WAAA;EACA,YAAA;EACA,UAAA;EACA,kBAAA;EACA,MAAA;EACA,OAAA;EACA,6BAAA;EACA,oBAAA;AACA;AACA;EACA,qBAAA;AACA;AACA;;;;;GAKA;;AAEA,wBAAA;AACA;EACA,yBAAA;AACA;AACA;EACA,qBAAA;AACA;;AAEA,yBAAA;AACA;EACA,yBAAA;AACA;AACA;EACA,qBAAA;AACA;;AAEA,0BAAA;AACA;EACA,yBAAA;AACA;AACA;EACA,qBAAA;AACA;;AAEA,eAAA;AACA;;EAEA,sBAAA;EACA,uBAAA;EACA,2BAAA;EACA,6BAAA;EACA,yBAAA;AACA;AAEA;;EAEA,sBAAA;EACA,uBAAA;AACA;AAEA;EACA,WAAA;EACA,YAAA;EACA,gBAAA;EACA,UAAA;AACA;AAEA;EACA,WAAA;EACA,YAAA;EACA,UAAA;EACA,kBAAA;EACA,MAAA;EACA,OAAA;EACA,6BAAA;EACA,oBAAA;AACA;AACA,aAAA;AACA;EACA,sBAAA;AACA","file":"Frame.vue","sourcesContent":["<template>\r\n <div\r\n :style=\"{\r\n maxHeight: `${maxHeight}px`,\r\n width: '100%',\r\n position: 'relative',\r\n }\"\r\n :class=\"{\r\n 'frame-container': true,\r\n 'initial-time': initialTime,\r\n 'end-time': endTime,\r\n 'checkpoint-time': checkpointTime,\r\n 'between-time': betweenTime,\r\n 'event-a': eventType === 'A',\r\n 'event-b': eventType === 'B',\r\n 'event-c': eventType === 'C',\r\n active: active,\r\n }\"\r\n >\r\n <GlobalEvents\r\n v-if=\"active && activeTab && parentComponent.settingsClosed\"\r\n @keydown.32.prevent=\"playOrPause\"\r\n @keydown.27.prevent=\"stop\"\r\n @keydown.79=\"toogleDetailFrame\"\r\n @keydown.13.prevent=\"jumpFrameToStart\"\r\n />\r\n <GlobalEvents\r\n v-if=\"showFrame\"\r\n @keydown.27.prevent=\"toogleDetailFrame\"\r\n @keydown.13.prevent=\"toogleDetailFrame\"\r\n />\r\n\r\n <vue-loading\r\n :active=\"isLoading\"\r\n spinner=\"ring\"\r\n color=\"var(--visualization-primary)\"\r\n background-color=\"#cacaca\"\r\n :style=\"{\r\n 'z-index': 0,\r\n minHeight: `${height}px`,\r\n minWidth: `${width}px`,\r\n }\"\r\n :text=\"videoIsLoading ? $t('infoBar.createVideo') : ''\"\r\n />\r\n <div\r\n :style=\"{\r\n backgroundColor: 'black',\r\n width: stretchFrame ? '100%' : 'auto',\r\n height: stretchFrame ? '100%' : 'auto',\r\n }\"\r\n >\r\n <div\r\n v-if=\"!isLoading\"\r\n :class=\"{\r\n 'frame-content': frame.title === -1 || videoStatus !== Status.stopped,\r\n }\"\r\n :style=\"\r\n stretchFrame\r\n ? {\r\n height: `${maxHeight}px`,\r\n width: '100%',\r\n color: 'white',\r\n }\r\n : {\r\n minHeight: `${height}px`,\r\n minWidth: `${width}px`,\r\n maxHeight: `${height}px`,\r\n maxWidth: `${width}px`,\r\n color: 'white',\r\n }\r\n \"\r\n @dblclick=\"playOrPause\"\r\n >\r\n <div\r\n v-if=\"frame.image && videoStatus === Status.stopped\"\r\n :class=\"{ 'dummy-frame': frame.title === -1 }\"\r\n v-html=\"frame.image\"\r\n style=\"width: 100%; height: 100%\"\r\n />\r\n\r\n <video\r\n v-if=\"\r\n !videoOnModal && videoStatus !== Status.stopped && !videoIsLoading\r\n \"\r\n ref=\"videoPlayer\"\r\n :maxHeight=\"`${height}px`\"\r\n :maxWidth=\"`${width}px`\"\r\n :src=\"videoUrlString\"\r\n @timeupdate=\"updateTimeLabel\"\r\n @ended=\"videoEnded\"\r\n @click=\"playOrPause\"\r\n @canplay=\"canPlay\"\r\n @error=\"() => stop(false)\"\r\n :style=\"{ width: '100%', aspectRatio: aspectRatioFrac }\"\r\n ></video>\r\n <div v-if=\"!loading\" class=\"overlay\"></div>\r\n </div>\r\n </div>\r\n\r\n <dialog\r\n ref=\"imageDetailsDialog\"\r\n style=\"\r\n top: 50%;\r\n left: 50%;\r\n transform: translateY(-50%) translateX(-50%);\r\n border: none;\r\n background: grey;\r\n width: 60%;\r\n \"\r\n >\r\n <div\r\n v-if=\"videoStatus == Status.stopped\"\r\n style=\"justify-content: center; display: flex\"\r\n :class=\"{\r\n 'dummy-frame': frame.title === -1,\r\n 'div-frame': frame.title !== -1,\r\n }\"\r\n v-html=\"frame.image\"\r\n @dblclick=\"changeToVideo\"\r\n ></div>\r\n <video\r\n v-if=\"videoOnModal && videoStatus !== Status.stopped && !videoIsLoading\"\r\n ref=\"videoPlayer\"\r\n :src=\"videoUrlString\"\r\n @ended=\"videoEnded\"\r\n style=\"width: 100%; aspect-ratio: 11/9\"\r\n controls=\"true\"\r\n width=\"200%\"\r\n ></video>\r\n </dialog>\r\n </div>\r\n</template>\r\n<script>\r\nimport VueLoading from 'vue-loading-twa'\r\n\r\nconst Status = Object.freeze({\r\n stopped: 0,\r\n playing: 1,\r\n paused: 2,\r\n})\r\n\r\nexport default {\r\n components: {\r\n VueLoading,\r\n },\r\n name: 'frame-component',\r\n props: {\r\n frame: {\r\n type: [Object, Number],\r\n required: true,\r\n },\r\n index: {\r\n type: Number,\r\n required: true,\r\n },\r\n loading: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n gridSettings: {\r\n type: Object,\r\n required: true,\r\n },\r\n initialTime: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n endTime: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n checkpointTime: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n betweenTime: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n active: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n activeTab: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n videoUrl: {\r\n type: [String, Promise],\r\n required: true,\r\n },\r\n playbackRate: {\r\n type: Number,\r\n default: 1,\r\n },\r\n videoControls: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n aspectRatio: {\r\n type: Number,\r\n default: 11 / 9,\r\n },\r\n stretchFrame: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n eventType: {\r\n type: String,\r\n default: null,\r\n },\r\n },\r\n data() {\r\n return {\r\n Status,\r\n width: 0,\r\n height: 0,\r\n maxWidth: 0,\r\n fullMaxWidth: 0,\r\n maxHeight: 0,\r\n showFrame: false,\r\n videoStatus: Status.stopped,\r\n videoStartTime: null,\r\n videoCurrentTime: null,\r\n videoUrlString: '',\r\n parentComponent: null,\r\n videoIsLoading: false,\r\n currentBlock: null,\r\n lastTimeEnterPressed: null,\r\n videoOnModal: false,\r\n }\r\n },\r\n created() {\r\n this.parentComponent = this.$parent\r\n },\r\n watch: {\r\n playbackRate(val) {\r\n if (\r\n this.videoStatus === Status.playing ||\r\n this.videoStatus === Status.paused\r\n ) {\r\n this.$refs.videoPlayer.playbackRate = val\r\n }\r\n },\r\n },\r\n computed: {\r\n isLoading() {\r\n return (\r\n ((!this.frame.image || !this.frame.time) && this.frame.title !== -1) ||\r\n this.loading ||\r\n this.videoIsLoading\r\n )\r\n },\r\n aspectRatioFrac() {\r\n var gcd = function (a, b) {\r\n if (b < 0.0000001) return a\r\n\r\n return gcd(b, Math.floor(a % b))\r\n }\r\n\r\n var denominator = Math.pow(10, this.aspectRatio.toString().length - 2)\r\n var numerator = this.aspectRatio * denominator\r\n\r\n var divisor = gcd(numerator, denominator)\r\n\r\n return `${Math.floor(numerator / divisor)}/${Math.floor(\r\n denominator / divisor\r\n )}`\r\n },\r\n },\r\n methods: {\r\n canPlay() {\r\n this.$refs.videoPlayer.play()\r\n },\r\n changeToVideo() {\r\n this.videoOnModal = true\r\n this.playOrPause()\r\n },\r\n jumpFrameToStart() {\r\n if (this.active) {\r\n const currentTime = new Date().getTime()\r\n\r\n if (currentTime - this.lastTimeEnterPressed <= 500) {\r\n this.parentComponent.changeHour(\r\n this.parentComponent.convertToAudienceTime(this.frame.time)\r\n )\r\n }\r\n\r\n this.lastTimeEnterPressed = currentTime\r\n }\r\n },\r\n changeSettings(value) {\r\n this.videoCurrentTime = value\r\n },\r\n toogleDetailFrame() {\r\n this.stop()\r\n if (this.showFrame) {\r\n this.videoOnModal = false\r\n this.$refs.imageDetailsDialog.close()\r\n } else {\r\n this.videoOnModal = true\r\n this.$refs.imageDetailsDialog.showModal()\r\n // Start playing the video (it will be paused in playOrPause)\r\n this.$nextTick(() => {\r\n this.playOrPause()\r\n // Pause immediately after starting\r\n this.$nextTick(() => {\r\n if (this.$refs.videoPlayer && this.videoStatus === Status.playing) {\r\n this.$refs.videoPlayer.pause()\r\n this.videoStatus = Status.paused\r\n this.$emit('playPauseStatus', false)\r\n }\r\n })\r\n })\r\n }\r\n this.showFrame = !this.showFrame\r\n },\r\n getMaxWidth() {\r\n const commandBarSize = document.getElementById('command-bar')\r\n\r\n return commandBarSize?.clientWidth\r\n },\r\n resize(height) {\r\n const commandBarSize = document.getElementById('command-bar')\r\n const infoBarSize = document.getElementById('info-bar')\r\n\r\n this.fullMaxWidth = infoBarSize?.clientWidth\r\n this.maxWidth =\r\n (this.fullMaxWidth - this.gridSettings.framesPerRow * 10) /\r\n this.gridSettings.framesPerRow\r\n\r\n const rowSpacing = 29 * this.gridSettings.numberOfRows\r\n\r\n let heightAvailable =\r\n height -\r\n (commandBarSize?.offsetHeight || 0) -\r\n infoBarSize?.offsetHeight -\r\n rowSpacing -\r\n 2 // bottom padding\r\n\r\n if (this.gridSettings.numberOfRows > 1) {\r\n heightAvailable += 12\r\n }\r\n\r\n this.maxHeight = heightAvailable / this.gridSettings.numberOfRows\r\n const minHeightPerRow = this.gridSettings.numberOfRows > 2 ? 50 : 60\r\n this.maxHeight = Math.max(this.maxHeight, minHeightPerRow)\r\n\r\n if (this.maxWidth / this.aspectRatio > this.maxHeight) {\r\n this.height = this.maxHeight\r\n this.width = this.maxHeight * this.aspectRatio\r\n } else {\r\n this.width = this.maxWidth\r\n this.height = this.maxWidth / this.aspectRatio\r\n }\r\n this.applyImageStyles()\r\n this.maxHeight = this.height\r\n },\r\n applyImageStyles() {\r\n this.$nextTick(() => {\r\n const frameImages = this.$el.querySelectorAll(\r\n '.frame-container img, .dummy-frame img'\r\n )\r\n\r\n frameImages.forEach((img) => {\r\n img.style.width = '100%'\r\n img.style.height = '100%'\r\n img.style.objectFit = 'fill'\r\n img.style.aspectRatio = 'auto'\r\n img.style.display = 'block'\r\n })\r\n })\r\n },\r\n async playOrPause() {\r\n switch (this.videoStatus) {\r\n case Status.stopped:\r\n this.videoIsLoading = true\r\n try {\r\n this.videoUrlString = await this.videoUrl\r\n const blockInfo =\r\n await this.parentComponent.fInterface?.getVideoStartTime(\r\n this.frame\r\n )\r\n\r\n this.videoStartTime = blockInfo.start\r\n this.videoUrlString =\r\n this.parentComponent.fInterface.getVideoRequestByUrl(\r\n blockInfo.url,\r\n this.frame.number + this.parentComponent.shiftFrames\r\n )\r\n } catch (error) {\r\n console.log(error)\r\n }\r\n this.videoIsLoading = false\r\n this.videoStatus = Status.playing\r\n this.$nextTick(() => {\r\n this.$refs.videoPlayer.onloaded = (e) => {\r\n this.$refs.videoPlayer.playbackRate = this.playbackRate\r\n // * atualizar slider\r\n this.$emit('updateSlider', this.videoStartTime, this.frame.time)\r\n //*\r\n this.$emit('startPlaying', this.frame, e.target.duration)\r\n this.$emit('playPauseStatus', true)\r\n }\r\n })\r\n break\r\n case Status.paused:\r\n this.$refs.videoPlayer.play()\r\n this.$refs.videoPlayer.playbackRate = this.playbackRate\r\n this.videoStatus = Status.playing\r\n this.$emit('playPauseStatus', true)\r\n break\r\n case Status.playing:\r\n this.$refs.videoPlayer.pause()\r\n\r\n this.videoStatus = Status.paused\r\n this.$emit('playPauseStatus', false)\r\n break\r\n }\r\n },\r\n stop(jump = true) {\r\n this.videoOnModal = false\r\n if (\r\n this.videoStatus === Status.playing ||\r\n this.videoStatus === Status.paused\r\n ) {\r\n if (jump) {\r\n this.parentComponent.changeHour(\r\n this.parentComponent.convertToAudienceTime(\r\n parseInt(this.videoCurrentTime)\r\n )\r\n )\r\n }\r\n this.videoStatus = Status.stopped\r\n this.videoUrlString = null\r\n this.videoCurrentTime = null\r\n this.videoStartTime = null\r\n }\r\n this.$emit('stopPlaying')\r\n },\r\n //eslint-disable-next-line\r\n videoJumpTo(time) {\r\n this.videoCurrentTime = this.videoStartTime + time\r\n this.$refs.videoPlayer.currentTime = time\r\n },\r\n async videoJumpToTimeStamp(time) {\r\n let frame = {\r\n time: time,\r\n }\r\n if (!time) return\r\n if (\r\n !(\r\n this.currentBlock &&\r\n time >= this.currentBlock.start &&\r\n time <= this.currentBlock.end\r\n )\r\n ) {\r\n this.currentBlock = await this.parentComponent.fInterface?.getBlockInfo(\r\n frame\r\n )\r\n this.videoStartTime = this.currentBlock.start\r\n this.videoUrlString =\r\n this.parentComponent.fInterface.getVideoUrlForTime(\r\n this.videoStartTime\r\n )\r\n }\r\n this.videoCurrentTime = time - this.videoStartTime\r\n this.$refs.videoPlayer.currentTime = this.videoCurrentTime\r\n },\r\n updateTimeLabel(e) {\r\n this.videoCurrentTime = this.videoStartTime + e.target.currentTime\r\n this.parentComponent.updateVideoTime(this.index, this.videoCurrentTime)\r\n this.parentComponent.updateVideoStatus(e.target.currentTime)\r\n },\r\n async videoEnded() {\r\n this.videoStartTime =\r\n await this.parentComponent.fInterface?.getNextVideoStartTime(\r\n this.frame.time - this.parentComponent.shiftFrames\r\n )\r\n this.videoUrlString = this.parentComponent.fInterface.getVideoUrlForTime(\r\n this.videoStartTime\r\n )\r\n // * Ao acabar o vídeo tenho de mandar atualizar slider\r\n // * aqui o video current time tambem vai ser o videoStartTime\r\n this.$emit('updateSlider', this.videoStartTime, this.videoStartTime)\r\n },\r\n },\r\n}\r\n</script>\r\n<style scoped>\r\n.frame-container > div {\r\n max-height: 100%;\r\n height: 100%; /* Add this */\r\n}\r\n.frame-container {\r\n background-color: black;\r\n display: flex;\r\n justify-content: center;\r\n}\r\n.frame-content,\r\n.dummy-frame {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n.frame-content video {\r\n flex-grow: 1;\r\n object-fit: fill;\r\n z-index: 0;\r\n}\r\n.overlay {\r\n width: 100%;\r\n height: 100%;\r\n z-index: 5;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n border: 4px solid transparent;\r\n pointer-events: none;\r\n}\r\n.active .overlay {\r\n border-color: #ffc966;\r\n}\r\n/* .between-time .overlay {\r\n border-top-color: var(--visualization-secondary);\r\n}\r\n.between-time.active .overlay {\r\n border-color: var(--visualization-secondary);\r\n} */\r\n\r\n/* Event Type A - Blue */\r\n.event-a .overlay {\r\n border-top-color: #ff6f00;\r\n}\r\n.event-a.active .overlay {\r\n border-color: #ff6f00;\r\n}\r\n\r\n/* Event Type B - Green */\r\n.event-b .overlay {\r\n border-top-color: #0073e6;\r\n}\r\n.event-b.active .overlay {\r\n border-color: #0073e6;\r\n}\r\n\r\n/* Event Type C - Orange */\r\n.event-c .overlay {\r\n border-top-color: #00b300;\r\n}\r\n.event-c.active .overlay {\r\n border-color: #00b300;\r\n}\r\n\r\n/* new styles */\r\n.frame-content >>> img,\r\n.dummy-frame >>> img {\r\n width: 100% !important;\r\n height: 100% !important;\r\n object-fit: fill !important;\r\n aspect-ratio: auto !important;\r\n display: block !important;\r\n}\r\n\r\n.frame-content >>> div,\r\n.dummy-frame >>> div {\r\n width: 100% !important;\r\n height: 100% !important;\r\n}\r\n\r\n.frame-content video {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: fill;\r\n z-index: 0;\r\n}\r\n\r\n.overlay {\r\n width: 100%;\r\n height: 100%;\r\n z-index: 5;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n border: 4px solid transparent;\r\n pointer-events: none;\r\n}\r\n/* «««««««« */\r\n.div-frame >>> img {\r\n width: 90vh !important;\r\n}\r\n</style>\r\n"]}, media: undefined });
|
|
896
894
|
|
|
897
895
|
};
|
|
898
896
|
/* scoped */
|
|
899
|
-
const __vue_scope_id__$5 = "data-v-
|
|
897
|
+
const __vue_scope_id__$5 = "data-v-c42c15c0";
|
|
900
898
|
/* module identifier */
|
|
901
899
|
const __vue_module_identifier__$5 = undefined;
|
|
902
900
|
/* functional template */
|
|
@@ -2971,7 +2969,7 @@ var script$1 = {
|
|
|
2971
2969
|
type: Number,
|
|
2972
2970
|
default: 1,
|
|
2973
2971
|
},
|
|
2974
|
-
maxShift: {
|
|
2972
|
+
maxShift: {
|
|
2975
2973
|
type: Number,
|
|
2976
2974
|
default: 20,
|
|
2977
2975
|
},
|
|
@@ -3219,6 +3217,10 @@ var script$1 = {
|
|
|
3219
3217
|
deep: true,
|
|
3220
3218
|
},
|
|
3221
3219
|
customFramesPerRow(newVal) {
|
|
3220
|
+
if (newVal > 20) {
|
|
3221
|
+
this.customFramesPerRow = 20;
|
|
3222
|
+
return
|
|
3223
|
+
}
|
|
3222
3224
|
if (this.isCustomMode && newVal && parseInt(newVal) > 0) {
|
|
3223
3225
|
this.$emit('set-custom-grid-values', [
|
|
3224
3226
|
parseInt(this.customNumberOfRows),
|
|
@@ -3228,6 +3230,10 @@ var script$1 = {
|
|
|
3228
3230
|
},
|
|
3229
3231
|
|
|
3230
3232
|
customNumberOfRows(newVal) {
|
|
3233
|
+
if (newVal > 10) {
|
|
3234
|
+
this.customNumberOfRows = 10;
|
|
3235
|
+
return
|
|
3236
|
+
}
|
|
3231
3237
|
if (this.isCustomMode && newVal && parseInt(newVal) > 0) {
|
|
3232
3238
|
this.$emit('set-custom-grid-values', [
|
|
3233
3239
|
parseInt(newVal),
|
|
@@ -3393,7 +3399,7 @@ var __vue_render__$1 = function () {
|
|
|
3393
3399
|
attrs: {
|
|
3394
3400
|
type: "number",
|
|
3395
3401
|
min: "1",
|
|
3396
|
-
max: "
|
|
3402
|
+
max: "20",
|
|
3397
3403
|
placeholder: _vm.customFramesPerRow,
|
|
3398
3404
|
},
|
|
3399
3405
|
domProps: { value: _vm.customFramesPerRow },
|
|
@@ -4034,11 +4040,11 @@ __vue_render__$1._withStripped = true;
|
|
|
4034
4040
|
/* style */
|
|
4035
4041
|
const __vue_inject_styles__$1 = function (inject) {
|
|
4036
4042
|
if (!inject) return
|
|
4037
|
-
inject("data-v-6d1063b7_0", { source: "\ndialog[data-v-6d1063b7] {\r\n max-width: 60vw;\r\n /* padding: 12px; */\r\n border: none;\r\n background: white;\r\n border-radius: 6px;\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 position: fixed;\r\n top: 50%;\r\n left: 50%;\r\n transform: translateY(-50%) translateX(-50%);\n}\n.visualization-row[data-v-6d1063b7] {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex: 1 1 auto;\n}\n.visualization-col[data-v-6d1063b7] {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 12px;\n}\r\n", map: {"version":3,"sources":["C:\\Workspace\\visualization\\src\\components\\Settings.vue"],"names":[],"mappings":";AAgrBA;EACA,eAAA;EACA,mBAAA;EACA,YAAA;EACA,iBAAA;EACA,kBAAA;EACA;mCACA;EACA,eAAA;EACA,QAAA;EACA,SAAA;EACA,4CAAA;AACA;AACA;EACA,aAAA;EACA,eAAA;EACA,cAAA;AACA;AAEA;EACA,aAAA;EACA,YAAA;EACA,eAAA;EACA,aAAA;AACA","file":"Settings.vue","sourcesContent":["<template>\r\n <div>\r\n <GlobalEvents v-if=\"isOpen\" @keydown.27=\"close\" @keydown.13=\"close\" />\r\n <GlobalEvents\r\n v-if=\"dialogsVisibility.frames\"\r\n @keydown.37=\"prevFormat\"\r\n @keydown.39=\"nextFormat\" />\r\n <!-- Formato da grelha -->\r\n <dialog ref=\"frames\">\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 40px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 10px;\r\n \">\r\n {{ $t('infoBar.gridForm') }}\r\n </div>\r\n <div class=\"visualization-row\" style=\"justify-content: center\">\r\n <div\r\n v-for=\"(item, index) in items\"\r\n :key=\"index\"\r\n class=\"visualization-col\"\r\n style=\"min-width: 200px; max-width: 200px\">\r\n <img\r\n @click=\"() => (framesValue = item.value)\"\r\n :src=\"`data:image/svg+xml;base64,${item.image}`\"\r\n width=\"100%\"\r\n :style=\"framesValue !== item.value ? 'filter: grayscale(1)' : ''\" />\r\n </div>\r\n </div>\r\n <div\r\n v-if=\"showCustomInputs\"\r\n style=\"\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n color: #333;\r\n gap: 8px;\r\n \">\r\n <span>\r\n <input\r\n v-model.number=\"customFramesPerRow\"\r\n type=\"number\"\r\n min=\"1\"\r\n max=\"15\"\r\n :placeholder=\"customFramesPerRow\"\r\n style=\"\r\n height: 30px;\r\n width: 50px;\r\n border: 0.1px solid #c2c9d6;\r\n border-radius: 3px;\r\n padding: 3px;\r\n \" />\r\n Frames por</span\r\n >\r\n <span>\r\n <input\r\n v-model.number=\"customNumberOfRows\"\r\n type=\"number\"\r\n min=\"1\"\r\n max=\"10\"\r\n :placeholder=\"customNumberOfRows\"\r\n style=\"\r\n height: 30px;\r\n width: 50px;\r\n border: 0.1px solid #c2c9d6;\r\n border-radius: 3px;\r\n padding: 5px;\r\n \" />\r\n Linhas</span\r\n >\r\n </div>\r\n <div class=\"visualization-divider\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n display: flex;\r\n justify-content: center;\r\n margin-top: 10px;\r\n margin-bottom: 10px;\r\n \">\r\n <button\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n \"\r\n @click=\"$emit('close', 'frames')\">\r\n Ok\r\n </button>\r\n </div>\r\n </dialog>\r\n\r\n <!-- Frequencia das Imagens -->\r\n <dialog ref=\"secondsPerFrame\">\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 40px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 10px;\r\n \">\r\n {{ $t('infoBar.secondImage') + ' (1-' + maxSteps + ')' }}\r\n </div>\r\n\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n margin: 25px;\r\n display: flex;\r\n align-items: center;\r\n height: 60px;\r\n border-radius: 8px;\r\n padding: 0 20px;\r\n \">\r\n <input\r\n ref=\"secondsPerFrameInput\"\r\n v-model.number=\"secondsPerFrameValue\"\r\n type=\"number\"\r\n step=\"1\"\r\n :max=\"maxSteps\"\r\n min=\"1\"\r\n style=\"\r\n height: 40px;\r\n width: 100px;\r\n padding: 5px 10px;\r\n font-size: 16px;\r\n border: 1px solid #ccc;\r\n border-radius: 4px;\r\n box-sizing: border-box;\r\n flex: 1;\r\n \"\r\n @input=\"validateInput\" />\r\n <span style=\"padding-left: 20px; font-size: 16px; color: #333\">\r\n {{ secondsPerFrame + ' s' }}\r\n </span>\r\n </div>\r\n\r\n <div class=\"visualization-divider\" style=\"margin: 10px\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"display: flex; justify-content: center; margin-top: 10px\">\r\n <button\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n margin-bottom: 8px;\r\n \"\r\n @click=\"$emit('close', 'secondsPerFrame')\">\r\n Ok\r\n </button>\r\n </div>\r\n </dialog>\r\n\r\n <!-- Go To -->\r\n <dialog ref=\"goTo\">\r\n <div\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 35px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 8px;\r\n \">\r\n {{ $t('infoBar.goTo') }}\r\n </div>\r\n <div class=\"visualization-row\" style=\"margin: 25px; height: 20px\">\r\n <input\r\n v-model=\"goToValue\"\r\n type=\"text\"\r\n placeholder=\"hh:mm:ss\"\r\n v-mask=\"'##:##:##'\"\r\n style=\"\r\n height: 30px;\r\n width: 300px;\r\n border: 0.1px solid #c2c9d6;\r\n border-radius: 3px;\r\n padding: 5px;\r\n \" />\r\n </div>\r\n <div class=\"visualization-divider\" style=\"margin-top: 40px\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n display: flex;\r\n justify-content: center;\r\n margin-top: 10px;\r\n margin-bottom: 10px;\r\n \">\r\n <button\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n \"\r\n @click=\"\r\n () => {\r\n $emit('close', 'goTo')\r\n $emit('change-go-to', goToValue)\r\n goToValue = ''\r\n }\r\n \">\r\n Ok\r\n </button>\r\n <GlobalEvents\r\n @keydown.13.prevent=\"\r\n () => {\r\n $emit('close', 'goTo')\r\n $emit('change-go-to', goToValue)\r\n goToValue = ''\r\n }\r\n \" />\r\n </div>\r\n </dialog>\r\n\r\n <!-- Velocidade Reprodução -->\r\n <GlobalEvents\r\n @keydown.107=\"changePlaybackRate(1)\"\r\n @keydown.109=\"changePlaybackRate(-1)\" />\r\n <dialog ref=\"playbackRate\">\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 35px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 8px;\r\n \">\r\n {{ $t('infoBar.playbackSpeed') }}\r\n </div>\r\n <div\r\n class=\"visualization-row\"\r\n style=\"margin: 25px; height: 20px; width: 380px\">\r\n <input\r\n ref=\"playbackRateInput\"\r\n v-model=\"playbackRateValue\"\r\n type=\"range\"\r\n step=\"0.25\"\r\n max=\"3\"\r\n min=\"1\"\r\n style=\"height: 30px; width: 300px\" />\r\n <span\r\n style=\"padding-left: 20px; padding-right: 20px; font-size: 16px\"\r\n >{{ playbackRate + 'x' }}</span\r\n >\r\n </div>\r\n <div class=\"visualization-divider\" style=\"margin-top: 40px\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n display: flex;\r\n justify-content: center;\r\n margin-top: 10px;\r\n margin-bottom: 10px;\r\n \">\r\n <button\r\n @click=\"$emit('close', 'playbackRate')\"\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n \">\r\n Ok\r\n </button>\r\n </div>\r\n </dialog>\r\n\r\n <!-- Deslocamento das Imagens -->\r\n <dialog ref=\"shiftFrames\">\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 40px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 10px;\r\n \">\r\n {{ `${$t('infoBar.shiftFrames')} (${maxShift * -1} - ${maxShift})` }}\r\n </div>\r\n\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n margin: 25px;\r\n display: flex;\r\n align-items: center;\r\n height: 60px;\r\n border-radius: 8px;\r\n padding: 0 20px;\r\n \">\r\n <input\r\n ref=\"shiftFramesInput\"\r\n v-model.number=\"shiftFramesValue\"\r\n type=\"number\"\r\n step=\"1\"\r\n :max=\"maxShift\"\r\n :min=\"maxShift * -1\"\r\n style=\"\r\n height: 40px;\r\n width: 100px;\r\n padding: 5px 10px;\r\n font-size: 16px;\r\n border: 1px solid #ccc;\r\n border-radius: 4px;\r\n box-sizing: border-box;\r\n flex: 1;\r\n \"\r\n @input=\"validateShift\" />\r\n <span style=\"padding-left: 20px; font-size: 16px; color: #333\">\r\n {{ shiftFrames + ' s' }}\r\n </span>\r\n </div>\r\n\r\n <div class=\"visualization-divider\" style=\"margin: 10px\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"display: flex; justify-content: center; margin-top: 10px\">\r\n <button\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n margin-bottom: 8px;\r\n \"\r\n @click=\"$emit('close', 'shiftFrames')\">\r\n Ok\r\n </button>\r\n </div>\r\n </dialog>\r\n </div>\r\n</template>\r\n<script>\r\nimport GridImages from '../assets/grid/index.js'\r\nimport { debounce } from 'lodash'\r\n\r\nexport default {\r\n props: {\r\n dialogsVisibility: {\r\n type: Object,\r\n required: false,\r\n },\r\n playbackRate: {\r\n type: Number,\r\n required: true,\r\n },\r\n secondsPerFrame: {\r\n type: Number,\r\n required: true,\r\n },\r\n framesPerRow: {\r\n type: Number,\r\n required: true,\r\n },\r\n numberOfRows: {\r\n type: Number,\r\n required: true,\r\n },\r\n shiftFrames: {\r\n type: Number,\r\n required: true,\r\n },\r\n maxSteps: {\r\n type: Number,\r\n default: 1,\r\n },\r\n maxShift: { \r\n type: Number,\r\n default: 20,\r\n },\r\n customGridSize: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n },\r\n data() {\r\n return {\r\n openBlocksDialog: false,\r\n time: '',\r\n rangeBlocks: {\r\n ini: null,\r\n end: null,\r\n date: null,\r\n },\r\n shiftFramesValue: this.shiftFrames,\r\n\r\n /* items: [\r\n { text: '1x1', value: 1, image: GridImages['1x1'] },\r\n { text: '2x1', value: 2, image: GridImages['2x1'] },\r\n { text: '3x1', value: 3, image: GridImages['3x1'] },\r\n { text: '3x2', value: 4, image: GridImages['3x2'] },\r\n { text: '4x1', value: 5, image: GridImages['4x1'] },\r\n { text: '4x2', value: 6, image: GridImages['4x2'] },\r\n { text: '5x1', value: 7, image: GridImages['5x1'] },\r\n { text: '5x2', value: 8, image: GridImages['5x2'] },\r\n { text: '6x1', value: 9, image: GridImages['6x1'] },\r\n { text: '6x2', value: 10, image: GridImages['6x2'] },\r\n {\r\n text: 'custom',\r\n value: 11,\r\n image: GridImages['custom'],\r\n },\r\n ], */\r\n debouncedShiftFrames: null,\r\n // NEW\r\n goToValue: '',\r\n customNumberOfRows: this.numberOfRows,\r\n customFramesPerRow: this.framesPerRow,\r\n showCustomInputs: false,\r\n isCustomMode: false,\r\n }\r\n },\r\n mounted() {\r\n this.debouncedShiftFrames = debounce(function () {\r\n this.$emit('change-shift-frames', parseInt(this.shiftFramesValue))\r\n }, 350)\r\n this.customNumberOfRows = this.numberOfRows\r\n this.customFramesPerRow = this.framesPerRow\r\n if (this.framesValue === 11) {\r\n this.isCustomMode = true\r\n this.showCustomInputs = true\r\n }\r\n this.toogleDialogs()\r\n },\r\n computed: {\r\n items() {\r\n const baseItems = [\r\n { text: '1x1', value: 1, image: GridImages['1x1'] },\r\n { text: '2x1', value: 2, image: GridImages['2x1'] },\r\n { text: '3x1', value: 3, image: GridImages['3x1'] },\r\n { text: '3x2', value: 4, image: GridImages['3x2'] },\r\n { text: '4x1', value: 5, image: GridImages['4x1'] },\r\n { text: '4x2', value: 6, image: GridImages['4x2'] },\r\n { text: '5x1', value: 7, image: GridImages['5x1'] },\r\n { text: '5x2', value: 8, image: GridImages['5x2'] },\r\n { text: '6x1', value: 9, image: GridImages['6x1'] },\r\n { text: '6x2', value: 10, image: GridImages['6x2'] },\r\n ]\r\n\r\n if (this.customGridSize) {\r\n baseItems.push({\r\n text: 'custom',\r\n value: 11,\r\n image: GridImages['custom'],\r\n })\r\n }\r\n\r\n return baseItems\r\n },\r\n isOpen() {\r\n return !!Object.keys(this.dialogsVisibility).find(\r\n (key) => this.dialogsVisibility[key]\r\n )\r\n },\r\n secondsPerFrameValue: {\r\n get() {\r\n return this.secondsPerFrame\r\n },\r\n set(val) {\r\n if (+val > 0 && +val <= this.maxSteps) {\r\n this.$emit('change-seconds-per-frame', parseInt(val))\r\n }\r\n },\r\n },\r\n playbackRateValue: {\r\n get() {\r\n return this.playbackRate\r\n },\r\n set(val) {\r\n this.$emit('change-playback-rate', parseFloat(val))\r\n },\r\n },\r\n framesValue: {\r\n get() {\r\n if (this.isCustomMode) {\r\n return 11\r\n }\r\n const preset = this.items.find(\r\n (item) =>\r\n item.value !== 11 &&\r\n item.text === `${this.framesPerRow}x${this.numberOfRows}`\r\n )\r\n\r\n return preset ? preset.value : 11\r\n },\r\n set(value) {\r\n if (parseInt(value) === 11) {\r\n this.isCustomMode = true\r\n this.showCustomInputs = true\r\n\r\n this.$nextTick(() => {\r\n this.$emit('set-custom-grid-values', [\r\n parseInt(this.customNumberOfRows),\r\n parseInt(this.customFramesPerRow),\r\n ])\r\n })\r\n } else {\r\n this.isCustomMode = false\r\n this.showCustomInputs = false\r\n this.$emit('set-frames-selection', value)\r\n }\r\n },\r\n },\r\n timeRules() {\r\n return [\r\n (time) =>\r\n (time >= '02:30:00' && time <= '26:29:59') ||\r\n this.$i18n.t('form.mustBeBetween'),\r\n ]\r\n },\r\n },\r\n methods: {\r\n validateInput() {\r\n if (parseInt(this.$refs.secondsPerFrameInput.value) < 1) {\r\n this.$refs.secondsPerFrameInput.value = this.secondsPerFrameValue\r\n } else if (\r\n parseInt(this.$refs.secondsPerFrameInput.value) > this.maxSteps\r\n ) {\r\n this.$refs.secondsPerFrameInput.value = this.secondsPerFrameValue\r\n }\r\n },\r\n validateShift() {\r\n if (parseInt(this.shiftFramesValue) < this.maxShift * -1) {\r\n this.shiftFramesValue = this.shiftFrames\r\n } else if (parseInt(this.shiftFramesValue) > this.maxShift) {\r\n this.shiftFramesValue = this.shiftFrames\r\n }\r\n\r\n this.debouncedShiftFrames()\r\n },\r\n close() {\r\n const openDialog = Object.keys(this.dialogsVisibility).find(\r\n (key) => this.dialogsVisibility[key]\r\n )\r\n this.$emit('close', openDialog)\r\n },\r\n toogleDialogs() {\r\n for (const dialog of Object.values(this.$refs)) {\r\n dialog.close?.()\r\n }\r\n\r\n const openDialog = Object.keys(this.dialogsVisibility).find(\r\n (key) => this.dialogsVisibility[key]\r\n )\r\n\r\n if (openDialog) {\r\n this.$refs[openDialog].showModal()\r\n }\r\n },\r\n prevFormat() {\r\n if (this.items.find((format) => format.value === this.framesValue - 1)) {\r\n this.framesValue--\r\n }\r\n },\r\n nextFormat() {\r\n if (this.items.find((format) => format.value === this.framesValue + 1)) {\r\n this.framesValue++\r\n }\r\n },\r\n changePlaybackRate(direction = 1) {\r\n if (direction === 1) {\r\n this.$refs.playbackRateInput.stepUp()\r\n } else if (direction === -1) {\r\n this.$refs.playbackRateInput.stepDown()\r\n }\r\n this.playbackRateValue = this.$refs.playbackRateInput.value\r\n },\r\n closeBlocksDialog() {\r\n Object.entries(this.rangeBlocks).forEach(([key, value]) => {\r\n if (!value || key == 'date') return\r\n var res = value.replace(/\\D/g, '')\r\n if (res.length < 6) {\r\n for (let i = res.length; i < 6; i++) {\r\n res += '0'\r\n }\r\n }\r\n res.match(/.{1,2}/g)\r\n let a = res.substring(0, 2)\r\n let b = res.substring(2, 4)\r\n let c = res.substring(4, 6)\r\n this.rangeBlocks[key] = a + ':' + b + ':' + c\r\n })\r\n\r\n this.openBlocksDialog = false\r\n this.$emit('goToBlockInterval', this.rangeBlocks)\r\n },\r\n checkIfCustomGrid() {\r\n const hasPreset = this.items.some(\r\n (item) =>\r\n item.value !== 11 &&\r\n item.text === `${this.framesPerRow}x${this.numberOfRows}`\r\n )\r\n\r\n if (!hasPreset) {\r\n this.showCustomInputs = true\r\n this.customNumberOfRows = this.numberOfRows\r\n this.customFramesPerRow = this.framesPerRow\r\n }\r\n },\r\n validateCustomInput(value, max = 10) {\r\n const num = parseInt(value)\r\n if (isNaN(num) || num < 1) return 1\r\n if (num > max) return max\r\n return num\r\n },\r\n },\r\n watch: {\r\n dialogsVisibility: {\r\n handler() {\r\n this.toogleDialogs()\r\n },\r\n deep: true,\r\n },\r\n customFramesPerRow(newVal) {\r\n if (this.isCustomMode && newVal && parseInt(newVal) > 0) {\r\n this.$emit('set-custom-grid-values', [\r\n parseInt(this.customNumberOfRows),\r\n parseInt(newVal),\r\n ])\r\n }\r\n },\r\n\r\n customNumberOfRows(newVal) {\r\n if (this.isCustomMode && newVal && parseInt(newVal) > 0) {\r\n this.$emit('set-custom-grid-values', [\r\n parseInt(newVal),\r\n parseInt(this.customFramesPerRow),\r\n ])\r\n }\r\n },\r\n\r\n numberOfRows(newVal) {\r\n if (!this.isCustomMode) {\r\n this.customNumberOfRows = newVal\r\n }\r\n },\r\n\r\n framesPerRow(newVal) {\r\n if (!this.isCustomMode) {\r\n this.customFramesPerRow = newVal\r\n }\r\n },\r\n },\r\n}\r\n</script>\r\n<style scoped>\r\ndialog {\r\n max-width: 60vw;\r\n /* padding: 12px; */\r\n border: none;\r\n background: white;\r\n border-radius: 6px;\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 position: fixed;\r\n top: 50%;\r\n left: 50%;\r\n transform: translateY(-50%) translateX(-50%);\r\n}\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: 12px;\r\n}\r\n</style>\r\n"]}, media: undefined });
|
|
4043
|
+
inject("data-v-06c4fe2e_0", { source: "\ndialog[data-v-06c4fe2e] {\r\n max-width: 60vw;\r\n /* padding: 12px; */\r\n border: none;\r\n background: white;\r\n border-radius: 6px;\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 position: fixed;\r\n top: 50%;\r\n left: 50%;\r\n transform: translateY(-50%) translateX(-50%);\n}\n.visualization-row[data-v-06c4fe2e] {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex: 1 1 auto;\n}\n.visualization-col[data-v-06c4fe2e] {\r\n flex-basis: 0;\r\n flex-grow: 1;\r\n max-width: 100%;\r\n padding: 12px;\n}\r\n", map: {"version":3,"sources":["C:\\Workspace\\visualization\\src\\components\\Settings.vue"],"names":[],"mappings":";AAwrBA;EACA,eAAA;EACA,mBAAA;EACA,YAAA;EACA,iBAAA;EACA,kBAAA;EACA;mCACA;EACA,eAAA;EACA,QAAA;EACA,SAAA;EACA,4CAAA;AACA;AACA;EACA,aAAA;EACA,eAAA;EACA,cAAA;AACA;AAEA;EACA,aAAA;EACA,YAAA;EACA,eAAA;EACA,aAAA;AACA","file":"Settings.vue","sourcesContent":["<template>\r\n <div>\r\n <GlobalEvents v-if=\"isOpen\" @keydown.27=\"close\" @keydown.13=\"close\" />\r\n <GlobalEvents\r\n v-if=\"dialogsVisibility.frames\"\r\n @keydown.37=\"prevFormat\"\r\n @keydown.39=\"nextFormat\" />\r\n <!-- Formato da grelha -->\r\n <dialog ref=\"frames\">\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 40px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 10px;\r\n \">\r\n {{ $t('infoBar.gridForm') }}\r\n </div>\r\n <div class=\"visualization-row\" style=\"justify-content: center\">\r\n <div\r\n v-for=\"(item, index) in items\"\r\n :key=\"index\"\r\n class=\"visualization-col\"\r\n style=\"min-width: 200px; max-width: 200px\">\r\n <img\r\n @click=\"() => (framesValue = item.value)\"\r\n :src=\"`data:image/svg+xml;base64,${item.image}`\"\r\n width=\"100%\"\r\n :style=\"framesValue !== item.value ? 'filter: grayscale(1)' : ''\" />\r\n </div>\r\n </div>\r\n <div\r\n v-if=\"showCustomInputs\"\r\n style=\"\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n color: #333;\r\n gap: 8px;\r\n \">\r\n <span>\r\n <input\r\n v-model.number=\"customFramesPerRow\"\r\n type=\"number\"\r\n min=\"1\"\r\n max=\"20\"\r\n :placeholder=\"customFramesPerRow\"\r\n style=\"\r\n height: 30px;\r\n width: 50px;\r\n border: 0.1px solid #c2c9d6;\r\n border-radius: 3px;\r\n padding: 3px;\r\n \" />\r\n Frames por</span\r\n >\r\n <span>\r\n <input\r\n v-model.number=\"customNumberOfRows\"\r\n type=\"number\"\r\n min=\"1\"\r\n max=\"10\"\r\n :placeholder=\"customNumberOfRows\"\r\n style=\"\r\n height: 30px;\r\n width: 50px;\r\n border: 0.1px solid #c2c9d6;\r\n border-radius: 3px;\r\n padding: 5px;\r\n \" />\r\n Linhas</span\r\n >\r\n </div>\r\n <div class=\"visualization-divider\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n display: flex;\r\n justify-content: center;\r\n margin-top: 10px;\r\n margin-bottom: 10px;\r\n \">\r\n <button\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n \"\r\n @click=\"$emit('close', 'frames')\">\r\n Ok\r\n </button>\r\n </div>\r\n </dialog>\r\n\r\n <!-- Frequencia das Imagens -->\r\n <dialog ref=\"secondsPerFrame\">\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 40px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 10px;\r\n \">\r\n {{ $t('infoBar.secondImage') + ' (1-' + maxSteps + ')' }}\r\n </div>\r\n\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n margin: 25px;\r\n display: flex;\r\n align-items: center;\r\n height: 60px;\r\n border-radius: 8px;\r\n padding: 0 20px;\r\n \">\r\n <input\r\n ref=\"secondsPerFrameInput\"\r\n v-model.number=\"secondsPerFrameValue\"\r\n type=\"number\"\r\n step=\"1\"\r\n :max=\"maxSteps\"\r\n min=\"1\"\r\n style=\"\r\n height: 40px;\r\n width: 100px;\r\n padding: 5px 10px;\r\n font-size: 16px;\r\n border: 1px solid #ccc;\r\n border-radius: 4px;\r\n box-sizing: border-box;\r\n flex: 1;\r\n \"\r\n @input=\"validateInput\" />\r\n <span style=\"padding-left: 20px; font-size: 16px; color: #333\">\r\n {{ secondsPerFrame + ' s' }}\r\n </span>\r\n </div>\r\n\r\n <div class=\"visualization-divider\" style=\"margin: 10px\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"display: flex; justify-content: center; margin-top: 10px\">\r\n <button\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n margin-bottom: 8px;\r\n \"\r\n @click=\"$emit('close', 'secondsPerFrame')\">\r\n Ok\r\n </button>\r\n </div>\r\n </dialog>\r\n\r\n <!-- Go To -->\r\n <dialog ref=\"goTo\">\r\n <div\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 35px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 8px;\r\n \">\r\n {{ $t('infoBar.goTo') }}\r\n </div>\r\n <div class=\"visualization-row\" style=\"margin: 25px; height: 20px\">\r\n <input\r\n v-model=\"goToValue\"\r\n type=\"text\"\r\n placeholder=\"hh:mm:ss\"\r\n v-mask=\"'##:##:##'\"\r\n style=\"\r\n height: 30px;\r\n width: 300px;\r\n border: 0.1px solid #c2c9d6;\r\n border-radius: 3px;\r\n padding: 5px;\r\n \" />\r\n </div>\r\n <div class=\"visualization-divider\" style=\"margin-top: 40px\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n display: flex;\r\n justify-content: center;\r\n margin-top: 10px;\r\n margin-bottom: 10px;\r\n \">\r\n <button\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n \"\r\n @click=\"\r\n () => {\r\n $emit('close', 'goTo')\r\n $emit('change-go-to', goToValue)\r\n goToValue = ''\r\n }\r\n \">\r\n Ok\r\n </button>\r\n <GlobalEvents\r\n @keydown.13.prevent=\"\r\n () => {\r\n $emit('close', 'goTo')\r\n $emit('change-go-to', goToValue)\r\n goToValue = ''\r\n }\r\n \" />\r\n </div>\r\n </dialog>\r\n\r\n <!-- Velocidade Reprodução -->\r\n <GlobalEvents\r\n @keydown.107=\"changePlaybackRate(1)\"\r\n @keydown.109=\"changePlaybackRate(-1)\" />\r\n <dialog ref=\"playbackRate\">\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 35px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 8px;\r\n \">\r\n {{ $t('infoBar.playbackSpeed') }}\r\n </div>\r\n <div\r\n class=\"visualization-row\"\r\n style=\"margin: 25px; height: 20px; width: 380px\">\r\n <input\r\n ref=\"playbackRateInput\"\r\n v-model=\"playbackRateValue\"\r\n type=\"range\"\r\n step=\"0.25\"\r\n max=\"3\"\r\n min=\"1\"\r\n style=\"height: 30px; width: 300px\" />\r\n <span\r\n style=\"padding-left: 20px; padding-right: 20px; font-size: 16px\"\r\n >{{ playbackRate + 'x' }}</span\r\n >\r\n </div>\r\n <div class=\"visualization-divider\" style=\"margin-top: 40px\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n display: flex;\r\n justify-content: center;\r\n margin-top: 10px;\r\n margin-bottom: 10px;\r\n \">\r\n <button\r\n @click=\"$emit('close', 'playbackRate')\"\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n \">\r\n Ok\r\n </button>\r\n </div>\r\n </dialog>\r\n\r\n <!-- Deslocamento das Imagens -->\r\n <dialog ref=\"shiftFrames\">\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n padding: 5px;\r\n font-weight: bold;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n height: 40px;\r\n color: white;\r\n display: flex;\r\n justify-content: center;\r\n padding-top: 10px;\r\n \">\r\n {{ `${$t('infoBar.shiftFrames')} (${maxShift * -1} - ${maxShift})` }}\r\n </div>\r\n\r\n <div\r\n class=\"visualization-row\"\r\n style=\"\r\n margin: 25px;\r\n display: flex;\r\n align-items: center;\r\n height: 60px;\r\n border-radius: 8px;\r\n padding: 0 20px;\r\n \">\r\n <input\r\n ref=\"shiftFramesInput\"\r\n v-model.number=\"shiftFramesValue\"\r\n type=\"number\"\r\n step=\"1\"\r\n :max=\"maxShift\"\r\n :min=\"maxShift * -1\"\r\n style=\"\r\n height: 40px;\r\n width: 100px;\r\n padding: 5px 10px;\r\n font-size: 16px;\r\n border: 1px solid #ccc;\r\n border-radius: 4px;\r\n box-sizing: border-box;\r\n flex: 1;\r\n \"\r\n @input=\"validateShift\" />\r\n <span style=\"padding-left: 20px; font-size: 16px; color: #333\">\r\n {{ shiftFrames + ' s' }}\r\n </span>\r\n </div>\r\n\r\n <div class=\"visualization-divider\" style=\"margin: 10px\" />\r\n <div\r\n class=\"visualization-row\"\r\n style=\"display: flex; justify-content: center; margin-top: 10px\">\r\n <button\r\n style=\"\r\n border-radius: 4px;\r\n height: 35px;\r\n width: 70px;\r\n background-color: var(--visualization-primary);\r\n border-color: var(--visualization-primary);\r\n color: white;\r\n margin-bottom: 8px;\r\n \"\r\n @click=\"$emit('close', 'shiftFrames')\">\r\n Ok\r\n </button>\r\n </div>\r\n </dialog>\r\n </div>\r\n</template>\r\n<script>\r\nimport GridImages from '../assets/grid/index.js'\r\nimport { debounce } from 'lodash'\r\n\r\nexport default {\r\n props: {\r\n dialogsVisibility: {\r\n type: Object,\r\n required: false,\r\n },\r\n playbackRate: {\r\n type: Number,\r\n required: true,\r\n },\r\n secondsPerFrame: {\r\n type: Number,\r\n required: true,\r\n },\r\n framesPerRow: {\r\n type: Number,\r\n required: true,\r\n },\r\n numberOfRows: {\r\n type: Number,\r\n required: true,\r\n },\r\n shiftFrames: {\r\n type: Number,\r\n required: true,\r\n },\r\n maxSteps: {\r\n type: Number,\r\n default: 1,\r\n },\r\n maxShift: {\r\n type: Number,\r\n default: 20,\r\n },\r\n customGridSize: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n },\r\n data() {\r\n return {\r\n openBlocksDialog: false,\r\n time: '',\r\n rangeBlocks: {\r\n ini: null,\r\n end: null,\r\n date: null,\r\n },\r\n shiftFramesValue: this.shiftFrames,\r\n\r\n /* items: [\r\n { text: '1x1', value: 1, image: GridImages['1x1'] },\r\n { text: '2x1', value: 2, image: GridImages['2x1'] },\r\n { text: '3x1', value: 3, image: GridImages['3x1'] },\r\n { text: '3x2', value: 4, image: GridImages['3x2'] },\r\n { text: '4x1', value: 5, image: GridImages['4x1'] },\r\n { text: '4x2', value: 6, image: GridImages['4x2'] },\r\n { text: '5x1', value: 7, image: GridImages['5x1'] },\r\n { text: '5x2', value: 8, image: GridImages['5x2'] },\r\n { text: '6x1', value: 9, image: GridImages['6x1'] },\r\n { text: '6x2', value: 10, image: GridImages['6x2'] },\r\n {\r\n text: 'custom',\r\n value: 11,\r\n image: GridImages['custom'],\r\n },\r\n ], */\r\n debouncedShiftFrames: null,\r\n // NEW\r\n goToValue: '',\r\n customNumberOfRows: this.numberOfRows,\r\n customFramesPerRow: this.framesPerRow,\r\n showCustomInputs: false,\r\n isCustomMode: false,\r\n }\r\n },\r\n mounted() {\r\n this.debouncedShiftFrames = debounce(function () {\r\n this.$emit('change-shift-frames', parseInt(this.shiftFramesValue))\r\n }, 350)\r\n this.customNumberOfRows = this.numberOfRows\r\n this.customFramesPerRow = this.framesPerRow\r\n if (this.framesValue === 11) {\r\n this.isCustomMode = true\r\n this.showCustomInputs = true\r\n }\r\n this.toogleDialogs()\r\n },\r\n computed: {\r\n items() {\r\n const baseItems = [\r\n { text: '1x1', value: 1, image: GridImages['1x1'] },\r\n { text: '2x1', value: 2, image: GridImages['2x1'] },\r\n { text: '3x1', value: 3, image: GridImages['3x1'] },\r\n { text: '3x2', value: 4, image: GridImages['3x2'] },\r\n { text: '4x1', value: 5, image: GridImages['4x1'] },\r\n { text: '4x2', value: 6, image: GridImages['4x2'] },\r\n { text: '5x1', value: 7, image: GridImages['5x1'] },\r\n { text: '5x2', value: 8, image: GridImages['5x2'] },\r\n { text: '6x1', value: 9, image: GridImages['6x1'] },\r\n { text: '6x2', value: 10, image: GridImages['6x2'] },\r\n ]\r\n\r\n if (this.customGridSize) {\r\n baseItems.push({\r\n text: 'custom',\r\n value: 11,\r\n image: GridImages['custom'],\r\n })\r\n }\r\n\r\n return baseItems\r\n },\r\n isOpen() {\r\n return !!Object.keys(this.dialogsVisibility).find(\r\n (key) => this.dialogsVisibility[key]\r\n )\r\n },\r\n secondsPerFrameValue: {\r\n get() {\r\n return this.secondsPerFrame\r\n },\r\n set(val) {\r\n if (+val > 0 && +val <= this.maxSteps) {\r\n this.$emit('change-seconds-per-frame', parseInt(val))\r\n }\r\n },\r\n },\r\n playbackRateValue: {\r\n get() {\r\n return this.playbackRate\r\n },\r\n set(val) {\r\n this.$emit('change-playback-rate', parseFloat(val))\r\n },\r\n },\r\n framesValue: {\r\n get() {\r\n if (this.isCustomMode) {\r\n return 11\r\n }\r\n const preset = this.items.find(\r\n (item) =>\r\n item.value !== 11 &&\r\n item.text === `${this.framesPerRow}x${this.numberOfRows}`\r\n )\r\n\r\n return preset ? preset.value : 11\r\n },\r\n set(value) {\r\n if (parseInt(value) === 11) {\r\n this.isCustomMode = true\r\n this.showCustomInputs = true\r\n\r\n this.$nextTick(() => {\r\n this.$emit('set-custom-grid-values', [\r\n parseInt(this.customNumberOfRows),\r\n parseInt(this.customFramesPerRow),\r\n ])\r\n })\r\n } else {\r\n this.isCustomMode = false\r\n this.showCustomInputs = false\r\n this.$emit('set-frames-selection', value)\r\n }\r\n },\r\n },\r\n timeRules() {\r\n return [\r\n (time) =>\r\n (time >= '02:30:00' && time <= '26:29:59') ||\r\n this.$i18n.t('form.mustBeBetween'),\r\n ]\r\n },\r\n },\r\n methods: {\r\n validateInput() {\r\n if (parseInt(this.$refs.secondsPerFrameInput.value) < 1) {\r\n this.$refs.secondsPerFrameInput.value = this.secondsPerFrameValue\r\n } else if (\r\n parseInt(this.$refs.secondsPerFrameInput.value) > this.maxSteps\r\n ) {\r\n this.$refs.secondsPerFrameInput.value = this.secondsPerFrameValue\r\n }\r\n },\r\n validateShift() {\r\n if (parseInt(this.shiftFramesValue) < this.maxShift * -1) {\r\n this.shiftFramesValue = this.shiftFrames\r\n } else if (parseInt(this.shiftFramesValue) > this.maxShift) {\r\n this.shiftFramesValue = this.shiftFrames\r\n }\r\n\r\n this.debouncedShiftFrames()\r\n },\r\n close() {\r\n const openDialog = Object.keys(this.dialogsVisibility).find(\r\n (key) => this.dialogsVisibility[key]\r\n )\r\n this.$emit('close', openDialog)\r\n },\r\n toogleDialogs() {\r\n for (const dialog of Object.values(this.$refs)) {\r\n dialog.close?.()\r\n }\r\n\r\n const openDialog = Object.keys(this.dialogsVisibility).find(\r\n (key) => this.dialogsVisibility[key]\r\n )\r\n\r\n if (openDialog) {\r\n this.$refs[openDialog].showModal()\r\n }\r\n },\r\n prevFormat() {\r\n if (this.items.find((format) => format.value === this.framesValue - 1)) {\r\n this.framesValue--\r\n }\r\n },\r\n nextFormat() {\r\n if (this.items.find((format) => format.value === this.framesValue + 1)) {\r\n this.framesValue++\r\n }\r\n },\r\n changePlaybackRate(direction = 1) {\r\n if (direction === 1) {\r\n this.$refs.playbackRateInput.stepUp()\r\n } else if (direction === -1) {\r\n this.$refs.playbackRateInput.stepDown()\r\n }\r\n this.playbackRateValue = this.$refs.playbackRateInput.value\r\n },\r\n closeBlocksDialog() {\r\n Object.entries(this.rangeBlocks).forEach(([key, value]) => {\r\n if (!value || key == 'date') return\r\n var res = value.replace(/\\D/g, '')\r\n if (res.length < 6) {\r\n for (let i = res.length; i < 6; i++) {\r\n res += '0'\r\n }\r\n }\r\n res.match(/.{1,2}/g)\r\n let a = res.substring(0, 2)\r\n let b = res.substring(2, 4)\r\n let c = res.substring(4, 6)\r\n this.rangeBlocks[key] = a + ':' + b + ':' + c\r\n })\r\n\r\n this.openBlocksDialog = false\r\n this.$emit('goToBlockInterval', this.rangeBlocks)\r\n },\r\n checkIfCustomGrid() {\r\n const hasPreset = this.items.some(\r\n (item) =>\r\n item.value !== 11 &&\r\n item.text === `${this.framesPerRow}x${this.numberOfRows}`\r\n )\r\n\r\n if (!hasPreset) {\r\n this.showCustomInputs = true\r\n this.customNumberOfRows = this.numberOfRows\r\n this.customFramesPerRow = this.framesPerRow\r\n }\r\n },\r\n validateCustomInput(value, max = 10) {\r\n const num = parseInt(value)\r\n if (isNaN(num) || num < 1) return 1\r\n if (num > max) return max\r\n return num\r\n },\r\n },\r\n watch: {\r\n dialogsVisibility: {\r\n handler() {\r\n this.toogleDialogs()\r\n },\r\n deep: true,\r\n },\r\n customFramesPerRow(newVal) {\r\n if (newVal > 20) {\r\n this.customFramesPerRow = 20\r\n return\r\n }\r\n if (this.isCustomMode && newVal && parseInt(newVal) > 0) {\r\n this.$emit('set-custom-grid-values', [\r\n parseInt(this.customNumberOfRows),\r\n parseInt(newVal),\r\n ])\r\n }\r\n },\r\n\r\n customNumberOfRows(newVal) {\r\n if (newVal > 10) {\r\n this.customNumberOfRows = 10\r\n return\r\n }\r\n if (this.isCustomMode && newVal && parseInt(newVal) > 0) {\r\n this.$emit('set-custom-grid-values', [\r\n parseInt(newVal),\r\n parseInt(this.customFramesPerRow),\r\n ])\r\n }\r\n },\r\n\r\n numberOfRows(newVal) {\r\n if (!this.isCustomMode) {\r\n this.customNumberOfRows = newVal\r\n }\r\n },\r\n\r\n framesPerRow(newVal) {\r\n if (!this.isCustomMode) {\r\n this.customFramesPerRow = newVal\r\n }\r\n },\r\n },\r\n}\r\n</script>\r\n<style scoped>\r\ndialog {\r\n max-width: 60vw;\r\n /* padding: 12px; */\r\n border: none;\r\n background: white;\r\n border-radius: 6px;\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 position: fixed;\r\n top: 50%;\r\n left: 50%;\r\n transform: translateY(-50%) translateX(-50%);\r\n}\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: 12px;\r\n}\r\n</style>\r\n"]}, media: undefined });
|
|
4038
4044
|
|
|
4039
4045
|
};
|
|
4040
4046
|
/* scoped */
|
|
4041
|
-
const __vue_scope_id__$1 = "data-v-
|
|
4047
|
+
const __vue_scope_id__$1 = "data-v-06c4fe2e";
|
|
4042
4048
|
/* module identifier */
|
|
4043
4049
|
const __vue_module_identifier__$1 = undefined;
|
|
4044
4050
|
/* functional template */
|
|
@@ -4132,6 +4138,14 @@ var script = {
|
|
|
4132
4138
|
type: Boolean,
|
|
4133
4139
|
default: false,
|
|
4134
4140
|
},
|
|
4141
|
+
visualizationInsArray: {
|
|
4142
|
+
type: Array,
|
|
4143
|
+
default: () => [],
|
|
4144
|
+
},
|
|
4145
|
+
insertDefaults: {
|
|
4146
|
+
type: Array,
|
|
4147
|
+
default: () => [],
|
|
4148
|
+
},
|
|
4135
4149
|
},
|
|
4136
4150
|
components: {
|
|
4137
4151
|
Frame: __vue_component__$5,
|
|
@@ -4170,6 +4184,8 @@ var script = {
|
|
|
4170
4184
|
C: [],
|
|
4171
4185
|
},
|
|
4172
4186
|
currentEventA: null,
|
|
4187
|
+
activeEvents: {}, // Track active events by type
|
|
4188
|
+
eventBlocked: false, // Track if event creation is blocked
|
|
4173
4189
|
canInsertTime: false,
|
|
4174
4190
|
lastHeight: 0,
|
|
4175
4191
|
loopInterval: null,
|
|
@@ -4203,7 +4219,7 @@ var script = {
|
|
|
4203
4219
|
async created() {
|
|
4204
4220
|
this.changeServer = this.serverOfFrames === 'alternative';
|
|
4205
4221
|
this.alternativeServer = this.serverOfFrames === 'alternative';
|
|
4206
|
-
|
|
4222
|
+
// console.log('visualization array updated: ', this.visualizationInsArray)
|
|
4207
4223
|
const settings = [
|
|
4208
4224
|
{
|
|
4209
4225
|
framesPerRow: 1,
|
|
@@ -4265,14 +4281,29 @@ var script = {
|
|
|
4265
4281
|
}
|
|
4266
4282
|
|
|
4267
4283
|
document.addEventListener('wheel', this.scrollEvent);
|
|
4284
|
+
document.addEventListener('keydown', this.handleKeydown);
|
|
4268
4285
|
|
|
4269
4286
|
await this.createFramesInterface();
|
|
4270
4287
|
this.$nextTick(this.resize);
|
|
4271
4288
|
},
|
|
4272
4289
|
beforeDestroy() {
|
|
4273
4290
|
document.removeEventListener('wheel', this.scrollEvent);
|
|
4291
|
+
document.removeEventListener('keydown', this.handleKeydown);
|
|
4274
4292
|
},
|
|
4275
4293
|
methods: {
|
|
4294
|
+
handleKeydown(event) {
|
|
4295
|
+
if (!this.active || !this.settingsClosed || this.readOnly) return
|
|
4296
|
+
|
|
4297
|
+
// Check if the pressed key matches any shortcut in insertDefaults
|
|
4298
|
+
const matchedConfig = this.insertDefaults.find(
|
|
4299
|
+
(config) => config.shortcut.toUpperCase() === event.key.toUpperCase()
|
|
4300
|
+
);
|
|
4301
|
+
|
|
4302
|
+
if (matchedConfig) {
|
|
4303
|
+
event.preventDefault();
|
|
4304
|
+
this.toggleEvent(matchedConfig.event);
|
|
4305
|
+
}
|
|
4306
|
+
},
|
|
4276
4307
|
scrollEvent(e) {
|
|
4277
4308
|
if (this.active) {
|
|
4278
4309
|
if (Math.sign(e.deltaY) !== Math.sign(this.scrollCounter)) {
|
|
@@ -4710,108 +4741,128 @@ var script = {
|
|
|
4710
4741
|
this.activeFrame = index;
|
|
4711
4742
|
}
|
|
4712
4743
|
},
|
|
4713
|
-
|
|
4744
|
+
toggleEvent(eventConfig) {
|
|
4714
4745
|
const currentFrame = this.$refs.frames?.find(
|
|
4715
4746
|
(f) => f.index === this.activeFrame
|
|
4716
4747
|
);
|
|
4717
4748
|
if (!currentFrame || !currentFrame.frame.time) return
|
|
4718
4749
|
|
|
4719
4750
|
const frameTime = currentFrame.frame.time;
|
|
4751
|
+
const eventType = eventConfig.type;
|
|
4752
|
+
|
|
4753
|
+
// Check if events are blocked (when type B is active outside Type A)
|
|
4754
|
+
if (this.eventBlocked && eventType !== 'B' && eventType !== 'C') {
|
|
4755
|
+
console.warn(
|
|
4756
|
+
'Events are blocked. Close the active type B/C event first.'
|
|
4757
|
+
);
|
|
4758
|
+
return
|
|
4759
|
+
}
|
|
4760
|
+
|
|
4761
|
+
if (eventType === 'A') {
|
|
4762
|
+
this.handleTypeA(frameTime, eventConfig);
|
|
4763
|
+
} else if (eventType === 'B') {
|
|
4764
|
+
this.handleTypeB(frameTime, eventConfig);
|
|
4765
|
+
} else if (eventType === 'C') {
|
|
4766
|
+
this.handleTypeC(frameTime, eventConfig);
|
|
4767
|
+
}
|
|
4720
4768
|
|
|
4769
|
+
this.canInsertTime = this.hasAnyEvents();
|
|
4770
|
+
document.getElementById(`frame-${this.activeFrame}`).click();
|
|
4771
|
+
},
|
|
4772
|
+
handleTypeA(frameTime, eventConfig) {
|
|
4721
4773
|
if (this.currentEventA && this.currentEventA.hour_ini) {
|
|
4722
4774
|
// Close the event A
|
|
4723
4775
|
this.currentEventA.hour_end = frameTime;
|
|
4724
4776
|
this.events.A.push({ ...this.currentEventA });
|
|
4725
4777
|
this.currentEventA = null;
|
|
4726
|
-
|
|
4727
|
-
// Auto-close any unclosed B or C events on the same frame
|
|
4728
|
-
const lastEventB = this.events.B[this.events.B.length - 1];
|
|
4729
|
-
const lastEventC = this.events.C[this.events.C.length - 1];
|
|
4730
|
-
|
|
4731
|
-
if (lastEventB && !lastEventB.hour_end) {
|
|
4732
|
-
lastEventB.hour_end = frameTime;
|
|
4733
|
-
}
|
|
4734
|
-
if (lastEventC && !lastEventC.hour_end) {
|
|
4735
|
-
lastEventC.hour_end = frameTime;
|
|
4736
|
-
}
|
|
4778
|
+
delete this.activeEvents.A;
|
|
4737
4779
|
} else {
|
|
4738
4780
|
// Start a new event A
|
|
4781
|
+
// Type A cannot be nested inside another Type A (already checked by currentEventA)
|
|
4739
4782
|
this.currentEventA = {
|
|
4740
|
-
|
|
4783
|
+
...eventConfig,
|
|
4741
4784
|
hour_ini: frameTime,
|
|
4742
4785
|
hour_end: null,
|
|
4743
4786
|
};
|
|
4787
|
+
this.activeEvents.A = true;
|
|
4744
4788
|
}
|
|
4745
|
-
this.canInsertTime = this.hasAnyEvents();
|
|
4746
|
-
document.getElementById(`frame-${this.activeFrame}`).click();
|
|
4747
4789
|
},
|
|
4748
|
-
|
|
4749
|
-
if (!this.currentEventA) {
|
|
4750
|
-
console.warn('Event B must be inside an event A');
|
|
4751
|
-
return
|
|
4752
|
-
}
|
|
4753
|
-
|
|
4754
|
-
const currentFrame = this.$refs.frames?.find(
|
|
4755
|
-
(f) => f.index === this.activeFrame
|
|
4756
|
-
);
|
|
4757
|
-
if (!currentFrame || !currentFrame.frame.time) return
|
|
4758
|
-
|
|
4759
|
-
const frameTime = currentFrame.frame.time;
|
|
4790
|
+
handleTypeB(frameTime, eventConfig) {
|
|
4760
4791
|
const lastEventB = this.events.B[this.events.B.length - 1];
|
|
4761
4792
|
const lastEventC = this.events.C[this.events.C.length - 1];
|
|
4762
4793
|
|
|
4763
4794
|
if (lastEventB && !lastEventB.hour_end) {
|
|
4764
4795
|
// Close the event B
|
|
4765
4796
|
lastEventB.hour_end = frameTime;
|
|
4797
|
+
delete this.activeEvents.B;
|
|
4798
|
+
this.eventBlocked = false; // Unblock events
|
|
4766
4799
|
} else {
|
|
4800
|
+
// Check if we're outside Type A and already have an unclosed B or C
|
|
4801
|
+
if (!this.currentEventA) {
|
|
4802
|
+
if (
|
|
4803
|
+
(lastEventB && !lastEventB.hour_end) ||
|
|
4804
|
+
(lastEventC && !lastEventC.hour_end)
|
|
4805
|
+
) {
|
|
4806
|
+
console.warn(
|
|
4807
|
+
'Only 1 B/C event allowed outside Type A. Close the current event first.'
|
|
4808
|
+
);
|
|
4809
|
+
return
|
|
4810
|
+
}
|
|
4811
|
+
// Outside Type A: block other events when B is active
|
|
4812
|
+
this.eventBlocked = true;
|
|
4813
|
+
}
|
|
4814
|
+
|
|
4767
4815
|
// Auto-close any unclosed C event before starting B
|
|
4768
4816
|
if (lastEventC && !lastEventC.hour_end) {
|
|
4769
4817
|
lastEventC.hour_end = frameTime - 1;
|
|
4818
|
+
delete this.activeEvents.C;
|
|
4770
4819
|
}
|
|
4771
4820
|
|
|
4772
4821
|
// Start a new event B
|
|
4773
4822
|
this.events.B.push({
|
|
4774
|
-
|
|
4823
|
+
...eventConfig,
|
|
4775
4824
|
hour_ini: frameTime,
|
|
4776
4825
|
hour_end: null,
|
|
4777
4826
|
});
|
|
4827
|
+
this.activeEvents.B = true;
|
|
4778
4828
|
}
|
|
4779
|
-
this.canInsertTime = this.hasAnyEvents();
|
|
4780
|
-
document.getElementById(`frame-${this.activeFrame}`).click();
|
|
4781
4829
|
},
|
|
4782
|
-
|
|
4783
|
-
if (!this.currentEventA) {
|
|
4784
|
-
console.warn('Event C must be inside an event A');
|
|
4785
|
-
return
|
|
4786
|
-
}
|
|
4787
|
-
|
|
4788
|
-
const currentFrame = this.$refs.frames?.find(
|
|
4789
|
-
(f) => f.index === this.activeFrame
|
|
4790
|
-
);
|
|
4791
|
-
if (!currentFrame || !currentFrame.frame.time) return
|
|
4792
|
-
|
|
4793
|
-
const frameTime = currentFrame.frame.time;
|
|
4830
|
+
handleTypeC(frameTime, eventConfig) {
|
|
4794
4831
|
const lastEventC = this.events.C[this.events.C.length - 1];
|
|
4795
4832
|
const lastEventB = this.events.B[this.events.B.length - 1];
|
|
4796
4833
|
|
|
4797
4834
|
if (lastEventC && !lastEventC.hour_end) {
|
|
4798
4835
|
// Close the event C
|
|
4799
4836
|
lastEventC.hour_end = frameTime;
|
|
4837
|
+
delete this.activeEvents.C;
|
|
4800
4838
|
} else {
|
|
4839
|
+
// Check if we're outside Type A and already have an unclosed B or C
|
|
4840
|
+
if (!this.currentEventA) {
|
|
4841
|
+
if (
|
|
4842
|
+
(lastEventB && !lastEventB.hour_end) ||
|
|
4843
|
+
(lastEventC && !lastEventC.hour_end)
|
|
4844
|
+
) {
|
|
4845
|
+
console.warn(
|
|
4846
|
+
'Only 1 B/C event allowed outside Type A. Close the current event first.'
|
|
4847
|
+
);
|
|
4848
|
+
return
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
|
|
4801
4852
|
// Auto-close any unclosed B event before starting C
|
|
4802
4853
|
if (lastEventB && !lastEventB.hour_end) {
|
|
4803
4854
|
lastEventB.hour_end = frameTime - 1;
|
|
4855
|
+
delete this.activeEvents.B;
|
|
4804
4856
|
}
|
|
4805
4857
|
|
|
4806
4858
|
// Start a new event C
|
|
4807
4859
|
this.events.C.push({
|
|
4808
|
-
|
|
4860
|
+
...eventConfig,
|
|
4809
4861
|
hour_ini: frameTime,
|
|
4810
4862
|
hour_end: null,
|
|
4811
4863
|
});
|
|
4864
|
+
this.activeEvents.C = true;
|
|
4812
4865
|
}
|
|
4813
|
-
this.canInsertTime = this.hasAnyEvents();
|
|
4814
|
-
document.getElementById(`frame-${this.activeFrame}`).click();
|
|
4815
4866
|
},
|
|
4816
4867
|
hasAnyEvents() {
|
|
4817
4868
|
return (
|
|
@@ -5139,6 +5190,7 @@ var script = {
|
|
|
5139
5190
|
|
|
5140
5191
|
// Generate A events to fill the gaps
|
|
5141
5192
|
const generatedAEvents = [];
|
|
5193
|
+
const standaloneBCEvents = [];
|
|
5142
5194
|
|
|
5143
5195
|
// Get all A events (both completed and current)
|
|
5144
5196
|
const allAEvents = [...this.events.A];
|
|
@@ -5149,6 +5201,18 @@ var script = {
|
|
|
5149
5201
|
});
|
|
5150
5202
|
}
|
|
5151
5203
|
|
|
5204
|
+
// Separate B/C events into those inside Type A and standalone
|
|
5205
|
+
for (const bcEvent of bcEvents) {
|
|
5206
|
+
const isInsideA = allAEvents.some(
|
|
5207
|
+
(aEvent) =>
|
|
5208
|
+
bcEvent.hour_ini >= aEvent.hour_ini &&
|
|
5209
|
+
bcEvent.hour_end <= aEvent.hour_end
|
|
5210
|
+
);
|
|
5211
|
+
if (!isInsideA) {
|
|
5212
|
+
standaloneBCEvents.push(bcEvent);
|
|
5213
|
+
}
|
|
5214
|
+
}
|
|
5215
|
+
|
|
5152
5216
|
// For each A event, generate gap-filling A events around B/C events
|
|
5153
5217
|
for (const aEvent of allAEvents) {
|
|
5154
5218
|
const aStart = aEvent.hour_ini;
|
|
@@ -5162,7 +5226,7 @@ var script = {
|
|
|
5162
5226
|
if (bcEventsInA.length === 0) {
|
|
5163
5227
|
// No B or C events inside this A, keep the A event as is
|
|
5164
5228
|
generatedAEvents.push({
|
|
5165
|
-
|
|
5229
|
+
...aEvent,
|
|
5166
5230
|
hour_ini: aStart,
|
|
5167
5231
|
hour_end: aEnd,
|
|
5168
5232
|
});
|
|
@@ -5173,7 +5237,7 @@ var script = {
|
|
|
5173
5237
|
// Create A event before this B/C event
|
|
5174
5238
|
if (currentTime < bcEvent.hour_ini) {
|
|
5175
5239
|
generatedAEvents.push({
|
|
5176
|
-
|
|
5240
|
+
...aEvent,
|
|
5177
5241
|
hour_ini: currentTime,
|
|
5178
5242
|
hour_end: bcEvent.hour_ini - 1,
|
|
5179
5243
|
});
|
|
@@ -5184,7 +5248,7 @@ var script = {
|
|
|
5184
5248
|
// Create final A event after last B/C event
|
|
5185
5249
|
if (currentTime <= aEnd) {
|
|
5186
5250
|
generatedAEvents.push({
|
|
5187
|
-
|
|
5251
|
+
...aEvent,
|
|
5188
5252
|
hour_ini: currentTime,
|
|
5189
5253
|
hour_end: aEnd,
|
|
5190
5254
|
});
|
|
@@ -5193,35 +5257,79 @@ var script = {
|
|
|
5193
5257
|
}
|
|
5194
5258
|
|
|
5195
5259
|
// Create D events for each manually marked A event
|
|
5196
|
-
const dEvents = allAEvents.map((aEvent) => ({
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
}))
|
|
5260
|
+
// const dEvents = allAEvents.map((aEvent) => ({
|
|
5261
|
+
// type: 'D',
|
|
5262
|
+
// hour_ini: this.convertToAudienceTime(aEvent.hour_ini, ''),
|
|
5263
|
+
// hour_end: this.convertToAudienceTime(aEvent.hour_end, ''),
|
|
5264
|
+
// timestamp: aEvent.hour_ini, // For sorting
|
|
5265
|
+
// }))
|
|
5202
5266
|
|
|
5203
5267
|
// Convert generated A events
|
|
5204
|
-
const aEventsFormatted = generatedAEvents.map((e) =>
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5268
|
+
const aEventsFormatted = generatedAEvents.map((e) => {
|
|
5269
|
+
const formatted = {
|
|
5270
|
+
type: e.type,
|
|
5271
|
+
hour_ini: this.convertToAudienceTime(e.hour_ini, ''),
|
|
5272
|
+
hour_end: this.convertToAudienceTime(e.hour_end, ''),
|
|
5273
|
+
timestamp: e.hour_ini, // For sorting
|
|
5274
|
+
};
|
|
5275
|
+
// Add any extra properties from the original event
|
|
5276
|
+
Object.keys(e).forEach((key) => {
|
|
5277
|
+
if (!['type', 'hour_ini', 'hour_end', 'timestamp'].includes(key)) {
|
|
5278
|
+
formatted[key] = e[key];
|
|
5279
|
+
}
|
|
5280
|
+
});
|
|
5281
|
+
return formatted
|
|
5282
|
+
});
|
|
5283
|
+
|
|
5284
|
+
// Convert B/C events inside Type A
|
|
5285
|
+
const bcEventsInsideA = bcEvents.filter((bcEvent) =>
|
|
5286
|
+
allAEvents.some(
|
|
5287
|
+
(aEvent) =>
|
|
5288
|
+
bcEvent.hour_ini >= aEvent.hour_ini &&
|
|
5289
|
+
bcEvent.hour_end <= aEvent.hour_end
|
|
5290
|
+
)
|
|
5291
|
+
);
|
|
5292
|
+
|
|
5293
|
+
const bcEventsFormatted = bcEventsInsideA.map((e) => {
|
|
5294
|
+
const formatted = {
|
|
5295
|
+
type: e.type,
|
|
5296
|
+
hour_ini: this.convertToAudienceTime(e.hour_ini, ''),
|
|
5297
|
+
hour_end: this.convertToAudienceTime(e.hour_end, ''),
|
|
5298
|
+
timestamp: e.hour_ini, // For sorting
|
|
5299
|
+
};
|
|
5300
|
+
// Add any extra properties from the original event
|
|
5301
|
+
Object.keys(e).forEach((key) => {
|
|
5302
|
+
if (!['type', 'hour_ini', 'hour_end', 'timestamp'].includes(key)) {
|
|
5303
|
+
formatted[key] = e[key];
|
|
5304
|
+
}
|
|
5305
|
+
});
|
|
5306
|
+
return formatted
|
|
5307
|
+
});
|
|
5308
|
+
|
|
5309
|
+
// Convert standalone B/C events (outside Type A)
|
|
5310
|
+
const standaloneBCFormatted = standaloneBCEvents.map((e) => {
|
|
5311
|
+
const formatted = {
|
|
5312
|
+
type: e.type,
|
|
5313
|
+
hour_ini: this.convertToAudienceTime(e.hour_ini, ''),
|
|
5314
|
+
hour_end: this.convertToAudienceTime(e.hour_end, ''),
|
|
5315
|
+
timestamp: e.hour_ini, // For sorting
|
|
5316
|
+
};
|
|
5317
|
+
// Add any extra properties from the original event
|
|
5318
|
+
Object.keys(e).forEach((key) => {
|
|
5319
|
+
if (!['type', 'hour_ini', 'hour_end', 'timestamp'].includes(key)) {
|
|
5320
|
+
formatted[key] = e[key];
|
|
5321
|
+
}
|
|
5322
|
+
});
|
|
5323
|
+
return formatted
|
|
5324
|
+
});
|
|
5218
5325
|
|
|
5219
5326
|
// Combine all events and sort chronologically
|
|
5220
5327
|
// D events come before A events when they have the same timestamp
|
|
5221
5328
|
const allEventsToSend = [
|
|
5222
|
-
...dEvents,
|
|
5329
|
+
// ...dEvents,
|
|
5223
5330
|
...aEventsFormatted,
|
|
5224
5331
|
...bcEventsFormatted,
|
|
5332
|
+
...standaloneBCFormatted,
|
|
5225
5333
|
].sort((a, b) => {
|
|
5226
5334
|
if (a.timestamp === b.timestamp) {
|
|
5227
5335
|
// If same timestamp, D comes before A, A comes before B/C
|
|
@@ -5270,18 +5378,16 @@ var script = {
|
|
|
5270
5378
|
return
|
|
5271
5379
|
}
|
|
5272
5380
|
|
|
5273
|
-
|
|
5381
|
+
allEvents.flatMap((e) =>
|
|
5274
5382
|
[e.hour_ini, e.hour_end].filter(Boolean)
|
|
5275
5383
|
);
|
|
5276
|
-
const lowestHourIni = Math.min(...allTimes);
|
|
5277
|
-
const biggestHourEnd = Math.max(...allTimes);
|
|
5278
5384
|
|
|
5279
5385
|
const eventsToSend = [
|
|
5280
|
-
{
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
},
|
|
5386
|
+
// {
|
|
5387
|
+
// type: 'D',
|
|
5388
|
+
// hour_ini: this.convertToAudienceTime(lowestHourIni, ''),
|
|
5389
|
+
// hour_end: this.convertToAudienceTime(biggestHourEnd, ''),
|
|
5390
|
+
// },
|
|
5285
5391
|
...allEvents
|
|
5286
5392
|
.filter((e) => e.hour_end)
|
|
5287
5393
|
.map((e) => ({
|
|
@@ -5410,6 +5516,13 @@ var script = {
|
|
|
5410
5516
|
stretchFrame() {
|
|
5411
5517
|
this.$nextTick(this.resize);
|
|
5412
5518
|
},
|
|
5519
|
+
visualizationInsArray: {
|
|
5520
|
+
handler: function (newValue) {
|
|
5521
|
+
//! aqui amigo prica
|
|
5522
|
+
//console.log('visualizationInsArray changed', newValue)
|
|
5523
|
+
},
|
|
5524
|
+
deep: true,
|
|
5525
|
+
},
|
|
5413
5526
|
},
|
|
5414
5527
|
};
|
|
5415
5528
|
|
|
@@ -5447,36 +5560,6 @@ var __vue_render__ = function () {
|
|
|
5447
5560
|
})
|
|
5448
5561
|
: _vm._e(),
|
|
5449
5562
|
_vm._v(" "),
|
|
5450
|
-
!_vm.readOnly && _vm.active && _vm.settingsClosed
|
|
5451
|
-
? _c("GlobalEvents", {
|
|
5452
|
-
on: {
|
|
5453
|
-
keydown: [
|
|
5454
|
-
function ($event) {
|
|
5455
|
-
if (!$event.type.indexOf("key") && $event.keyCode !== 113) {
|
|
5456
|
-
return null
|
|
5457
|
-
}
|
|
5458
|
-
$event.preventDefault();
|
|
5459
|
-
return _vm.toggleEventA.apply(null, arguments)
|
|
5460
|
-
},
|
|
5461
|
-
function ($event) {
|
|
5462
|
-
if (!$event.type.indexOf("key") && $event.keyCode !== 114) {
|
|
5463
|
-
return null
|
|
5464
|
-
}
|
|
5465
|
-
$event.preventDefault();
|
|
5466
|
-
return _vm.toggleEventC.apply(null, arguments)
|
|
5467
|
-
},
|
|
5468
|
-
function ($event) {
|
|
5469
|
-
if (!$event.type.indexOf("key") && $event.keyCode !== 116) {
|
|
5470
|
-
return null
|
|
5471
|
-
}
|
|
5472
|
-
$event.preventDefault();
|
|
5473
|
-
return _vm.toggleEventB.apply(null, arguments)
|
|
5474
|
-
},
|
|
5475
|
-
],
|
|
5476
|
-
},
|
|
5477
|
-
})
|
|
5478
|
-
: _vm._e(),
|
|
5479
|
-
_vm._v(" "),
|
|
5480
5563
|
_vm.active && _vm.settingsClosed
|
|
5481
5564
|
? _c("GlobalEvents", {
|
|
5482
5565
|
on: {
|
|
@@ -6021,11 +6104,11 @@ __vue_render__._withStripped = true;
|
|
|
6021
6104
|
/* style */
|
|
6022
6105
|
const __vue_inject_styles__ = function (inject) {
|
|
6023
6106
|
if (!inject) return
|
|
6024
|
-
inject("data-v-3884e77a_0", { source: "\n.visualization-row[data-v-3884e77a] {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex: 1 1 auto;\n}\n.visualization-col[data-v-3884e77a] {\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-3884e77a] {\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-3884e77a] {\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-3884e77a] {\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-3884e77a],\r\n*[data-v-3884e77a] .visualization-justify-center {\r\n justify-content: center;\n}\n.visualization-align-center[data-v-3884e77a] {\r\n align-items: center;\n}\n#visualization-container[data-v-3884e77a] {\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-3884e77a] {\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-3884e77a],\r\n#info-bar[data-v-3884e77a] {\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-3884e77a] {\r\n width: 42px;\r\n height: 36px;\r\n border: none;\r\n background: none;\n}\n#command-bar button[data-v-3884e77a]:hover {\r\n cursor: pointer;\r\n background: rgba(0, 0, 0, 0.12);\n}\n#command-bar svg[data-v-3884e77a] {\r\n font-size: 16px;\n}\n#command-bar[data-v-3884e77a] {\r\n padding: 0 !important;\n}\n#info-bar[data-v-3884e77a] {\r\n padding: 4px;\r\n font-size: 14px;\r\n position: relative;\n}\n.settings-container[data-v-3884e77a] {\r\n position: absolute;\r\n right: 14px;\r\n top: 50%;\r\n transform: translateY(-50%);\n}\n.settings-container > *[data-v-3884e77a] {\r\n margin: 0 2px;\r\n cursor: pointer;\n}\n#info-bar svg[data-v-3884e77a] {\r\n font-size: 16px;\n}\n#info-bar .divider[data-v-3884e77a] {\r\n margin: 0 8px;\n}\nsvg[data-v-3884e77a]:focus {\r\n border: none;\n}\n.visualization-card[data-v-3884e77a] {\r\n border-left: 8px solid #eee;\n}\n.active-tab[data-v-3884e77a] {\r\n border-left: 8px solid var(--visualization-primary) !important;\r\n border-image-slice: 1;\n}\n[id^='frame-'][data-v-3884e77a] {\r\n padding: 1px;\r\n display: flex;\r\n flex-flow: column;\n}\n.tooltip[data-v-3884e77a] {\r\n display: block !important;\r\n z-index: 10000;\n}\n.tooltip .tooltip-inner[data-v-3884e77a] {\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-3884e77a] {\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-3884e77a] {\r\n margin-bottom: 5px;\n}\n.tooltip[x-placement^='top'] .tooltip-arrow[data-v-3884e77a] {\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-3884e77a] {\r\n margin-top: 5px;\n}\n.tooltip[x-placement^='bottom'] .tooltip-arrow[data-v-3884e77a] {\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-3884e77a] {\r\n margin-left: 5px;\n}\n.tooltip[x-placement^='right'] .tooltip-arrow[data-v-3884e77a] {\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-3884e77a] {\r\n margin-right: 5px;\n}\n.tooltip[x-placement^='left'] .tooltip-arrow[data-v-3884e77a] {\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-3884e77a] {\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-3884e77a] {\r\n border-color: #f9f9f9;\n}\n.tooltip[aria-hidden='true'][data-v-3884e77a] {\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-3884e77a] {\r\n visibility: visible;\r\n opacity: 1;\r\n transition: opacity 0.15s;\n}\n.custom-frametime[data-v-3884e77a] {\r\n font-size: smaller;\n}\r\n", map: {"version":3,"sources":["C:\\Workspace\\visualization\\src\\Visualization.vue"],"names":[],"mappings":";AAomDA;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=\"!readOnly && active && settingsClosed\"\r\n @keydown.113.prevent=\"toggleEventA\"\r\n @keydown.114.prevent=\"toggleEventC\"\r\n @keydown.116.prevent=\"toggleEventB\"\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 },\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 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\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\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 },\r\n methods: {\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 toggleEventA() {\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\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\r\n // Auto-close any unclosed B or C events on the same frame\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 lastEventB.hour_end = frameTime\r\n }\r\n if (lastEventC && !lastEventC.hour_end) {\r\n lastEventC.hour_end = frameTime\r\n }\r\n } else {\r\n // Start a new event A\r\n this.currentEventA = {\r\n type: 'A',\r\n hour_ini: frameTime,\r\n hour_end: null,\r\n }\r\n }\r\n this.canInsertTime = this.hasAnyEvents()\r\n document.getElementById(`frame-${this.activeFrame}`).click()\r\n },\r\n toggleEventB() {\r\n if (!this.currentEventA) {\r\n console.warn('Event B must be inside an event A')\r\n return\r\n }\r\n\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 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 } else {\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 }\r\n\r\n // Start a new event B\r\n this.events.B.push({\r\n type: 'B',\r\n hour_ini: frameTime,\r\n hour_end: null,\r\n })\r\n }\r\n this.canInsertTime = this.hasAnyEvents()\r\n document.getElementById(`frame-${this.activeFrame}`).click()\r\n },\r\n toggleEventC() {\r\n if (!this.currentEventA) {\r\n console.warn('Event C must be inside an event A')\r\n return\r\n }\r\n\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 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 } else {\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 }\r\n\r\n // Start a new event C\r\n this.events.C.push({\r\n type: 'C',\r\n hour_ini: frameTime,\r\n hour_end: null,\r\n })\r\n }\r\n this.canInsertTime = this.hasAnyEvents()\r\n document.getElementById(`frame-${this.activeFrame}`).click()\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\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 // 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 type: 'A',\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 type: 'A',\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 type: 'A',\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 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\r\n // Convert B/C events\r\n const bcEventsFormatted = bcEvents.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 timestamp: e.hour_ini, // For sorting\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 ].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 },\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 });
|
|
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 });
|
|
6025
6108
|
|
|
6026
6109
|
};
|
|
6027
6110
|
/* scoped */
|
|
6028
|
-
const __vue_scope_id__ = "data-v-
|
|
6111
|
+
const __vue_scope_id__ = "data-v-50023844";
|
|
6029
6112
|
/* module identifier */
|
|
6030
6113
|
const __vue_module_identifier__ = undefined;
|
|
6031
6114
|
/* functional template */
|