diff --git a/rice-box.go b/rice-box.go index f214110..56f108d 100644 --- a/rice-box.go +++ b/rice-box.go @@ -18,9 +18,9 @@ func init() { } file4 := &embedded.EmbeddedFile{ Filename: "css/main.css", - FileModTime: time.Unix(1702222023, 0), + FileModTime: time.Unix(1736106288, 0), - Content: string(":root {\n --link-color: #1e3650;\n --bs-link-color: var(--link-color);\n}\n\na {\n color: var(--link-color);\n}\n\n.btn-primary {\n background: #1e3650;\n border-color: #0e2640;\n}\n\n.nav-pills .nav-link.active {\n background: #1e3650;\n}\n\n.nav-pills .nav-link {\n padding-left: 3px;\n padding-right: 3px;\n}\n\n.nav-link {\n font-size: 10px;\n}\n\n.legend {\n color: #777;\n margin: 3px 0;\n padding: 3px 0;\n border-bottom: 1px solid #eee;\n font-size: 11px;\n text-transform: uppercase;\n}\n\n.btn-sm {\n font-size: 9px;\n}\n\n.select2 {\n min-width: 100%;\n}\n\n.line {\n height: 3px;\n}\n\n.pane {\n display: none;\n}\n\n.no-margin {\n margin: 0;\n}\n\n.no-padding {\n padding: 0;\n}\n\n.no-radius {\n border-radius: 0 !important;\n}\n\n#pointer {\n height: calc(100vh - 33px - 38px);\n top: calc(33px + 38px);\n margin: auto;\n background: #ccc;\n background-size: contain;\n background-repeat: no-repeat;\n position: absolute;\n width: calc(100% - 50px);\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n\n#scrollbar {\n height: calc(100vh - 80px);\n width: 50px;\n background: #333;\n position: absolute;\n z-index: 100;\n right: 0;\n}\n\n#pane-pointer .form-group {\n padding: 0;\n margin: 0;\n}\n\n#pointer-buttons {\n margin-top: -42px;\n width: 100%;\n z-index: 110;\n position: fixed;\n bottom: 0;\n padding-left: 0;\n padding-right: 0;\n}\n\n#pointer-buttons .btn {\n height: 50px;\n}\n\n#disconneced {\n position: absolute;\n top: 0;\n width: 100%;\n background: #ff6161;\n color: #fff;\n padding: 5px;\n}\n\n#disconneced a {\n color: #fff;\n font-weight: bold;\n}\n\n#nav {\n border-bottom: 2px solid #1e3650;\n}\n\n#shortcuts_special_keys input {\n display: none;\n}\n\n#response {\n position: absolute;\n bottom: 0;\n width: 100%;\n color: #fff;\n background: #748c26;\n padding: 5px;\n display: none;\n}\n\n#screenshot img {\n max-width: 100%;\n margin-top: 10px;\n cursor: pointer;\n}\n\n#mouse-screenshot-live {\n display: inline-block;\n width: 80px;\n padding-left: 5px;\n}\n\n#mouse-text-live {\n display: inline-block;\n width: calc(100% - 100px);\n}\n"), + Content: string(":root {\n --link-color: #1e3650;\n --bs-link-color: var(--link-color);\n}\n\n* {\n overscroll-behavior: contain !important;\n}\n\na {\n color: var(--link-color);\n}\n\n.btn-primary {\n background: #1e3650;\n border-color: #0e2640;\n}\n\n.nav-pills .nav-link.active {\n background: #1e3650;\n}\n\n.nav-pills .nav-link {\n padding-left: 3px;\n padding-right: 3px;\n}\n\n.nav-link {\n font-size: 10px;\n}\n\n.legend {\n color: #777;\n margin: 3px 0;\n padding: 3px 0;\n border-bottom: 1px solid #eee;\n font-size: 11px;\n text-transform: uppercase;\n}\n\n.btn-sm {\n font-size: 9px;\n}\n\n.select2 {\n min-width: 100%;\n}\n\n.line {\n height: 3px;\n}\n\n.pane {\n display: none;\n}\n\n.no-margin {\n margin: 0;\n}\n\n.no-padding {\n padding: 0;\n}\n\n.no-radius {\n border-radius: 0 !important;\n}\n\n#pointer {\n height: calc(100vh - 33px);\n bottom: 33px;\n margin: auto;\n background: #ccc;\n background-size: contain;\n background-repeat: no-repeat;\n position: absolute;\n width: calc(100% - 50px);\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n\n#scrollbar {\n height: calc(100vh - 33px);\n width: 50px;\n background: #333;\n position: fixed;\n bottom: 33px;\n z-index: 100;\n right: 0;\n}\n\n#mouse {\n position: fixed;\n bottom: 83px;\n z-index: 200;\n width: calc(100vw - 50px);\n padding: 10px;\n background: #fff;\n}\n\n#pane-pointer .form-group {\n padding: 0;\n margin: 0;\n}\n\n#pointer-buttons {\n margin-top: -42px;\n width: 100%;\n z-index: 110;\n position: fixed;\n bottom: 31px;\n padding-left: 0;\n padding-right: 0;\n}\n\n#pointer-buttons .btn {\n height: 50px;\n}\n\n#disconneced {\n position: absolute;\n top: 0;\n width: 100%;\n background: #ff6161;\n color: #fff;\n padding: 5px;\n}\n\n#disconneced a {\n color: #fff;\n font-weight: bold;\n}\n\n#nav {\n border-top: 2px solid #1e3650;\n position: fixed;\n bottom: 0;\n width: 100%;\n height: 33px;\n}\n\n#shortcuts_special_keys input {\n display: none;\n}\n\n#response {\n position: absolute;\n bottom: 0;\n width: 100%;\n color: #fff;\n background: #748c26;\n padding: 5px;\n display: none;\n}\n\n#screenshot img {\n max-width: 100%;\n margin-top: 10px;\n cursor: pointer;\n}\n\n#mouse-screenshot-live {\n display: inline-block;\n width: 80px;\n padding-left: 5px;\n}\n\n#mouse-text-live {\n display: inline-block;\n width: calc(100% - 100px);\n}\n"), } file6 := &embedded.EmbeddedFile{ Filename: "img/icon.png", @@ -30,9 +30,9 @@ func init() { } file8 := &embedded.EmbeddedFile{ Filename: "js/main.js", - FileModTime: time.Unix(1702222023, 0), + FileModTime: time.Unix(1736106956, 0), - Content: string("let ws\nlet pointer, scroller, response, screenshotImg\nlet scrollLastTimestamp, scrollLastValue\nlet mousePosX, mousePosY, mouseInitPosX, mouseInitPosY\nlet isLive = false\nlet isPointerLive = false\nlet isScreenshotWaiting = false\nlet isPointerScreenshotWaiting = false\n\nfunction createWebSocketConnection() {\n const protocol = location.protocol === 'https:' ? 'wss' : 'ws'\n\n ws = new WebSocket(`${protocol}://${window.location.hostname}:${window.location.port}/ws`)\n\n ws.onopen = function(event) {\n document.querySelector('#disconneced').style.display = 'none'\n }\n\n ws.onclose = function(event) {\n document.querySelector('#disconneced').style.display = 'block'\n\n window.setTimeout(createWebSocketConnection, 5000)\n }\n\n ws.onmessage = function(event) {\n let data = JSON.parse(event.data)\n\n if (data.type === 'response') {\n response.innerText = data.value\n response.style.display = 'block'\n\n window.setTimeout(function() {\n response.style.display = 'none'\n }, 2500)\n } else if (data.type === 'screenshot') {\n if (isScreenshotWaiting) {\n isScreenshotWaiting = false\n screenshotImg.setAttribute('src', 'data:image/png;base64, ' + data.value)\n }\n\n let pointer = document.querySelector('#pointer')\n\n if (isPointerScreenshotWaiting) {\n pointer.style.backgroundImage = `url('data:image/png;base64, ${data.value}')`\n isPointerScreenshotWaiting = false\n } else {\n pointer.style.backgroundImage = 'none'\n }\n }\n }\n}\n\nfunction navigationClickHandler(e) {\n if (e.target.getAttribute('href') === '#') {\n return\n }\n\n Array.from(document.querySelectorAll('.pane')).forEach((item) => {\n item.style.display = 'none'\n })\n\n document.querySelector(e.target.getAttribute('href')).style.display = 'block'\n\n Array.from(document.querySelectorAll('#nav a')).forEach((item) => {\n item.classList.remove('active')\n })\n\n e.target.classList.add('active')\n}\n\nfunction buttonClickHandler(e) {\n ws.send(e.target.getAttribute('data-msg'))\n}\n\nfunction shortcutClearClickHandler(e) {\n document.querySelector('#shortcut-key').value = ''\n\n Array.from(document.querySelectorAll('#shortcuts_special_keys input:checked')).forEach((item) => {\n item.checked = false\n item.change()\n })\n}\n\nfunction shortcutSendClickHandler(e) {\n let keys = []\n let key = document.querySelector('#shortcut-key').value\n\n Array.from(document.querySelectorAll('#shortcuts_special_keys input:checked')).forEach((item) => {\n keys.push(item.value)\n })\n\n if (keys.length) {\n if (key) {\n keys.push(key)\n }\n\n ws.send('{\"type\":\"keys\",\"value\": \"' + (keys.join(',').replace('\"', '\\\\\"')) + '\"}')\n }\n}\n\nfunction textClearClickHandler(e) {\n document.querySelector('#text').value = ''\n}\n\nfunction textSendClickHandler(e) {\n const keys = document.querySelector('#text').value\n\n if (keys.length) {\n ws.send('{\"type\":\"text\",\"value\": \"' + (keys.replace('\"', '\\\\\"')) + '\"}')\n }\n}\n\nfunction textKeyUpHandler(e) {\n const keys = document.querySelector('#text').value\n\n if (e.keyCode === 13) {\n ws.send('{\"type\":\"text\",\"value\": \"' + (keys.replace('\"', '\\\\\"')) + '\"}')\n }\n}\n\nfunction liveTextKeyUpHandler(e) {\n const value = e.target.value\n\n if (e.keyCode === 8) {\n ws.send('{\"type\":\"key\",\"value\": \"backspace\"}')\n } else if (e.keyCode === 13) {\n ws.send('{\"type\":\"key\",\"value\": \"enter\"}')\n } else if (value.length) {\n if (value === ' ') {\n ws.send('{\"type\":\"key\",\"value\": \"space\"}')\n } else {\n ws.send('{\"type\":\"text\",\"value\": \"' + (value.replace('\"', '\\\\\"')) + '\"}')\n }\n\n e.target.value = ''\n }\n}\n\nfunction shortcutsSpecialKeysOnChangeHandler(e) {\n Array.from(document.querySelectorAll('#shortcuts_special_keys input:checked')).forEach((item) => {\n item.parentNode.classList.add('btn-primary')\n item.parentNode.classList.remove('btn-secondary')\n })\n\n Array.from(document.querySelectorAll('#shortcuts_special_keys input:not(:checked)')).forEach((item) => {\n item.parentNode.classList.add('btn-secondary')\n item.parentNode.classList.remove('btn-primary')\n })\n}\n\nfunction pointerClickHandler(e) {\n ws.send('{\"type\":\"pointer\",\"click\":\"left\"}')\n}\n\nfunction scrollerTouchStartHandler(e) {\n mouseInitPosY = e.targetTouches[0].pageY\n}\n\nfunction scrollerTouchMoveHandler(e) {\n let touch = e.changedTouches[0]\n let value = ((touch.pageY - mouseInitPosY > 0) ? 'down' : 'up')\n let now = new Date().getTime()\n\n if (touch.pageY === mouseInitPosY || value === scrollLastValue && scrollLastTimestamp !== null && now - scrollLastTimestamp < 200) {\n return\n }\n\n scrollLastTimestamp = now\n scrollLastValue = value\n mouseInitPosY = touch.pageY\n\n ws.send('{\"type\":\"scroll\",\"value\": \"' + value + '\"}')\n}\n\nfunction pointerTouchStartHandler(e) {\n const touch = e.targetTouches[0]\n mouseInitPosX = touch.pageX\n mouseInitPosY = touch.pageY\n}\n\nfunction pointerLiveHandler(e) {\n if (!e.target.checked) {\n isPointerLive = false\n isPointerScreenshotWaiting = false\n\n return\n }\n\n isPointerLive = true\n\n let doScreenshot = function() {\n if (isPointerLive) {\n if (!isPointerScreenshotWaiting) {\n isPointerScreenshotWaiting = true\n ws.send(`{\"type\":\"screenshot\",\"quality\":\"lq\",\"pointer\":true}`)\n }\n\n window.setTimeout(doScreenshot, 300)\n }\n }\n\n doScreenshot()\n}\n\nfunction pointerTouchMoveHandler(e) {\n if (e.changedTouches.length === 2) {\n return scrollerTouchMoveHandler(e)\n }\n\n const touch = e.changedTouches[0]\n mousePosX = touch.pageX\n mousePosY = touch.pageY\n\n const newX = mousePosX - mouseInitPosX\n const newY = mousePosY - mouseInitPosY\n\n mouseInitPosX = mousePosX\n mouseInitPosY = mousePosY\n\n let msg = '{\"type\":\"pointer\",\"x\": \"' + newX + '\",\"y\": \"' + newY + '\"}'\n\n ws.send(msg)\n}\n\nfunction liveHqClickHandler(e) {\n return liveClickHandler(e, 'hq')\n}\n\nfunction liveLqClickHandler(e) {\n return liveClickHandler(e, 'lq')\n}\n\nfunction liveClickHandler(e, quality) {\n if (isLive) {\n isLive = false\n isScreenshotWaiting = false\n\n document.querySelector('#live-hq').innerText = 'Live HQ'\n document.querySelector('#live-lq').innerText = 'Live LQ'\n\n return\n }\n\n isLive = true\n\n e.target.innerText = 'Stop live'\n\n let doScreenshot = function() {\n if (isLive) {\n if (!isScreenshotWaiting) {\n isScreenshotWaiting = true\n ws.send(`{\"type\":\"screenshot\",\"quality\":\"${quality}\"}`)\n }\n\n window.setTimeout(doScreenshot, 100)\n }\n }\n\n doScreenshot()\n}\n\nfunction fullscreenHandler(e) {\n let element = document.querySelector(e.target.getAttribute('data-target'))\n let isFullscreen = parseInt(e.target.getAttribute('data-fullscreen'))\n\n document.querySelector('body').classList.toggle('fullscreen', isFullscreen)\n\n if (isFullscreen) {\n e.target.setAttribute('data-fullscreen', '0')\n\n if (document.exitFullscreen) {\n document.exitFullscreen()\n } else if (document.webkitExitFullscreen) {\n document.webkitExitFullscreen()\n } else if (document.mozCancelFullScreen) {\n document.mozCancelFullScreen()\n }\n } else {\n e.target.setAttribute('data-fullscreen', '1')\n\n if (element.requestFullscreen) {\n element.requestFullscreen()\n } else if (element.webkitRequestFullscreen) {\n element.webkitRequestFullscreen()\n } else if (element.mozRequestFullScreen) {\n element.mozRequestFullScreen()\n }\n }\n}\n\nfunction documentHashHandler() {\n const hash = window.location.hash\n\n if (hash) {\n document.querySelector('a[href=\"' + hash + '\"]').click()\n } else {\n document.querySelector('#nav > li:first-child a').click()\n }\n}\n\nfunction addEventListenerOn(selector, eventName, listener) {\n if (typeof selector === 'string') {\n Array.from(document.querySelectorAll(selector)).forEach((element) => {\n element.addEventListener(eventName, listener)\n })\n } else {\n selector.addEventListener(eventName, listener)\n }\n}\n\nfunction addListeners() {\n addEventListenerOn('#nav a', 'click', navigationClickHandler)\n addEventListenerOn('button[data-msg]', 'click', buttonClickHandler)\n\n addEventListenerOn('#shortcut-clear', 'click', shortcutClearClickHandler)\n addEventListenerOn('#shortcuts_special_keys input', 'change', shortcutsSpecialKeysOnChangeHandler)\n addEventListenerOn('#shortcut-send', 'click', shortcutSendClickHandler)\n\n addEventListenerOn('#text-clear', 'click', textClearClickHandler)\n addEventListenerOn('#text-send', 'click', textSendClickHandler)\n addEventListenerOn('#text', 'keyup', textKeyUpHandler)\n addEventListenerOn('.live-text', 'keyup', liveTextKeyUpHandler)\n\n addEventListenerOn(scroller, 'touchstart', scrollerTouchStartHandler)\n addEventListenerOn(scroller, 'touchmove', scrollerTouchMoveHandler)\n\n addEventListenerOn(pointer, 'click', pointerClickHandler)\n addEventListenerOn(pointer, 'touchstart', pointerTouchStartHandler)\n addEventListenerOn(pointer, 'touchmove', pointerTouchMoveHandler)\n addEventListenerOn('#mouse-screenshot-live input', 'change', pointerLiveHandler)\n\n addEventListenerOn('#live-hq', 'click', liveHqClickHandler)\n addEventListenerOn('#live-lq', 'click', liveLqClickHandler)\n addEventListenerOn('.btn-fullscreen', 'click', fullscreenHandler)\n}\n\nfunction bootstrap() {\n pointer = document.querySelector('#pointer')\n scroller = document.querySelector('#scrollbar')\n response = document.querySelector('#response')\n screenshotImg = document.querySelector('#screenshot img')\n\n shortcutsSpecialKeysOnChangeHandler()\n createWebSocketConnection()\n addListeners()\n documentHashHandler()\n\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.register('/static/js/service_worker.js')\n }\n}\n\naddEventListenerOn(window, 'DOMContentLoaded', bootstrap)\n"), + Content: string("let ws\nlet pointer, scroller, response, screenshotImg\nlet scrollLastTimestamp, scrollLastValue\nlet mousePosX, mousePosY, mouseInitPosX, mouseInitPosY\nlet isLive = false\nlet isPointerLive = false\nlet isScreenshotWaiting = null\nlet isPointerScreenshotWaiting = false\n\nfunction createWebSocketConnection() {\n const protocol = location.protocol === 'https:' ? 'wss' : 'ws'\n\n ws = new WebSocket(`${protocol}://${window.location.hostname}:${window.location.port}/ws`)\n\n ws.onopen = function(event) {\n document.querySelector('#disconneced').style.display = 'none'\n }\n\n ws.onclose = function(event) {\n document.querySelector('#disconneced').style.display = 'block'\n\n window.setTimeout(createWebSocketConnection, 5000)\n }\n\n ws.onmessage = function(event) {\n let data = JSON.parse(event.data)\n\n if (data.type === 'response') {\n response.innerText = data.value\n response.style.display = 'block'\n\n window.setTimeout(function() {\n response.style.display = 'none'\n }, 2500)\n } else if (data.type === 'screenshot') {\n if (isScreenshotWaiting || isScreenshotWaiting === null) {\n if (isScreenshotWaiting) {\n isScreenshotWaiting = false\n }\n\n screenshotImg.setAttribute('src', 'data:image/png;base64, ' + data.value)\n }\n\n let pointer = document.querySelector('#pointer')\n\n if (isPointerScreenshotWaiting) {\n pointer.style.backgroundImage = `url('data:image/png;base64, ${data.value}')`\n isPointerScreenshotWaiting = false\n } else {\n pointer.style.backgroundImage = 'none'\n }\n }\n }\n}\n\nfunction navigationClickHandler(e) {\n if (e.target.getAttribute('href') === '#') {\n return\n }\n\n Array.from(document.querySelectorAll('.pane')).forEach((item) => {\n item.style.display = 'none'\n })\n\n document.querySelector(e.target.getAttribute('href')).style.display = 'block'\n\n Array.from(document.querySelectorAll('#nav a')).forEach((item) => {\n item.classList.remove('active')\n })\n\n e.target.classList.add('active')\n}\n\nfunction buttonClickHandler(e) {\n ws.send(e.target.getAttribute('data-msg'))\n}\n\nfunction shortcutClearClickHandler(e) {\n document.querySelector('#shortcut-key').value = ''\n\n Array.from(document.querySelectorAll('#shortcuts_special_keys input:checked')).forEach((item) => {\n item.checked = false\n item.change()\n })\n}\n\nfunction shortcutSendClickHandler(e) {\n let keys = []\n let key = document.querySelector('#shortcut-key').value\n\n Array.from(document.querySelectorAll('#shortcuts_special_keys input:checked')).forEach((item) => {\n keys.push(item.value)\n })\n\n if (keys.length) {\n if (key) {\n keys.push(key)\n }\n\n ws.send('{\"type\":\"keys\",\"value\": \"' + (keys.join(',').replace('\"', '\\\\\"')) + '\"}')\n }\n}\n\nfunction textClearClickHandler(e) {\n document.querySelector('#text').value = ''\n}\n\nfunction textSendClickHandler(e) {\n const keys = document.querySelector('#text').value\n\n if (keys.length) {\n ws.send('{\"type\":\"text\",\"value\": \"' + (keys.replace('\"', '\\\\\"')) + '\"}')\n }\n}\n\nfunction textKeyUpHandler(e) {\n const keys = document.querySelector('#text').value\n\n if (e.keyCode === 13) {\n ws.send('{\"type\":\"text\",\"value\": \"' + (keys.replace('\"', '\\\\\"')) + '\"}')\n }\n}\n\nfunction liveTextKeyUpHandler(e) {\n const value = e.target.value\n\n if (e.keyCode === 8) {\n ws.send('{\"type\":\"key\",\"value\": \"backspace\"}')\n } else if (e.keyCode === 13) {\n ws.send('{\"type\":\"key\",\"value\": \"enter\"}')\n } else if (value.length) {\n if (value === ' ') {\n ws.send('{\"type\":\"key\",\"value\": \"space\"}')\n } else {\n ws.send('{\"type\":\"text\",\"value\": \"' + (value.replace('\"', '\\\\\"')) + '\"}')\n }\n\n e.target.value = ''\n }\n}\n\nfunction shortcutsSpecialKeysOnChangeHandler(e) {\n Array.from(document.querySelectorAll('#shortcuts_special_keys input:checked')).forEach((item) => {\n item.parentNode.classList.add('btn-primary')\n item.parentNode.classList.remove('btn-secondary')\n })\n\n Array.from(document.querySelectorAll('#shortcuts_special_keys input:not(:checked)')).forEach((item) => {\n item.parentNode.classList.add('btn-secondary')\n item.parentNode.classList.remove('btn-primary')\n })\n}\n\nfunction pointerClickHandler(e) {\n ws.send('{\"type\":\"pointer\",\"click\":\"left\"}')\n}\n\nfunction scrollerTouchStartHandler(e) {\n mouseInitPosY = e.targetTouches[0].pageY\n}\n\nfunction scrollerTouchMoveHandler(e) {\n let touch = e.changedTouches[0]\n let value = ((touch.pageY - mouseInitPosY > 0) ? 'down' : 'up')\n let now = new Date().getTime()\n\n if (touch.pageY === mouseInitPosY || value === scrollLastValue && scrollLastTimestamp !== null && now - scrollLastTimestamp < 200) {\n return\n }\n\n scrollLastTimestamp = now\n scrollLastValue = value\n mouseInitPosY = touch.pageY\n\n ws.send('{\"type\":\"scroll\",\"value\": \"' + value + '\"}')\n}\n\nfunction pointerTouchStartHandler(e) {\n const touch = e.targetTouches[0]\n mouseInitPosX = touch.pageX\n mouseInitPosY = touch.pageY\n}\n\nfunction pointerLiveHandler(e) {\n if (!e.target.checked) {\n isPointerLive = false\n isPointerScreenshotWaiting = null\n\n return\n }\n\n isPointerLive = true\n\n let doScreenshot = function() {\n if (isPointerLive) {\n if (!isPointerScreenshotWaiting) {\n isPointerScreenshotWaiting = true\n ws.send(`{\"type\":\"screenshot\",\"quality\":\"lq\",\"pointer\":true}`)\n }\n\n window.setTimeout(doScreenshot, 300)\n }\n }\n\n doScreenshot()\n}\n\nfunction pointerTouchMoveHandler(e) {\n if (e.changedTouches.length === 2) {\n return scrollerTouchMoveHandler(e)\n }\n\n const touch = e.changedTouches[0]\n mousePosX = touch.pageX\n mousePosY = touch.pageY\n\n const newX = mousePosX - mouseInitPosX\n const newY = mousePosY - mouseInitPosY\n\n mouseInitPosX = mousePosX\n mouseInitPosY = mousePosY\n\n let msg = '{\"type\":\"pointer\",\"x\": \"' + newX + '\",\"y\": \"' + newY + '\"}'\n\n ws.send(msg)\n}\n\nfunction liveHqClickHandler(e) {\n return liveClickHandler(e, 'hq')\n}\n\nfunction liveLqClickHandler(e) {\n return liveClickHandler(e, 'lq')\n}\n\nfunction liveClickHandler(e, quality) {\n if (isLive) {\n isLive = false\n isScreenshotWaiting = null\n\n document.querySelector('#live-hq').innerText = 'Live HQ'\n document.querySelector('#live-lq').innerText = 'Live LQ'\n\n return\n }\n\n isLive = true\n\n e.target.innerText = 'Stop live'\n\n let doScreenshot = function() {\n if (isLive) {\n if (!isScreenshotWaiting) {\n isScreenshotWaiting = true\n ws.send(`{\"type\":\"screenshot\",\"quality\":\"${quality}\"}`)\n }\n\n window.setTimeout(doScreenshot, 100)\n }\n }\n\n doScreenshot()\n}\n\nfunction fullscreenHandler(e) {\n let element = document.querySelector(e.target.getAttribute('data-target'))\n let isFullscreen = parseInt(e.target.getAttribute('data-fullscreen'))\n\n document.querySelector('body').classList.toggle('fullscreen', isFullscreen)\n\n if (isFullscreen) {\n e.target.setAttribute('data-fullscreen', '0')\n\n if (document.exitFullscreen) {\n document.exitFullscreen()\n } else if (document.webkitExitFullscreen) {\n document.webkitExitFullscreen()\n } else if (document.mozCancelFullScreen) {\n document.mozCancelFullScreen()\n }\n } else {\n e.target.setAttribute('data-fullscreen', '1')\n\n if (element.requestFullscreen) {\n element.requestFullscreen()\n } else if (element.webkitRequestFullscreen) {\n element.webkitRequestFullscreen()\n } else if (element.mozRequestFullScreen) {\n element.mozRequestFullScreen()\n }\n }\n}\n\nfunction documentHashHandler() {\n const hash = window.location.hash\n\n if (hash) {\n document.querySelector('a[href=\"' + hash + '\"]').click()\n } else {\n document.querySelector('#nav > li:first-child a').click()\n }\n}\n\nfunction addEventListenerOn(selector, eventName, listener) {\n if (typeof selector === 'string') {\n Array.from(document.querySelectorAll(selector)).forEach((element) => {\n element.addEventListener(eventName, listener)\n })\n } else {\n selector.addEventListener(eventName, listener)\n }\n}\n\nfunction addListeners() {\n addEventListenerOn('#nav a', 'click', navigationClickHandler)\n addEventListenerOn('button[data-msg]', 'click', buttonClickHandler)\n\n addEventListenerOn('#shortcut-clear', 'click', shortcutClearClickHandler)\n addEventListenerOn('#shortcuts_special_keys input', 'change', shortcutsSpecialKeysOnChangeHandler)\n addEventListenerOn('#shortcut-send', 'click', shortcutSendClickHandler)\n\n addEventListenerOn('#text-clear', 'click', textClearClickHandler)\n addEventListenerOn('#text-send', 'click', textSendClickHandler)\n addEventListenerOn('#text', 'keyup', textKeyUpHandler)\n addEventListenerOn('.live-text', 'keyup', liveTextKeyUpHandler)\n\n addEventListenerOn(scroller, 'touchstart', scrollerTouchStartHandler)\n addEventListenerOn(scroller, 'touchmove', scrollerTouchMoveHandler)\n\n addEventListenerOn(pointer, 'click', pointerClickHandler)\n addEventListenerOn(pointer, 'touchstart', pointerTouchStartHandler)\n addEventListenerOn(pointer, 'touchmove', pointerTouchMoveHandler)\n addEventListenerOn('#mouse-screenshot-live input', 'change', pointerLiveHandler)\n\n addEventListenerOn('#live-hq', 'click', liveHqClickHandler)\n addEventListenerOn('#live-lq', 'click', liveLqClickHandler)\n addEventListenerOn('.btn-fullscreen', 'click', fullscreenHandler)\n}\n\nfunction bootstrap() {\n pointer = document.querySelector('#pointer')\n scroller = document.querySelector('#scrollbar')\n response = document.querySelector('#response')\n screenshotImg = document.querySelector('#screenshot img')\n\n shortcutsSpecialKeysOnChangeHandler()\n createWebSocketConnection()\n addListeners()\n documentHashHandler()\n\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.register('/static/js/service_worker.js')\n }\n}\n\naddEventListenerOn(window, 'DOMContentLoaded', bootstrap)\n"), } file9 := &embedded.EmbeddedFile{ Filename: "js/service_worker.js", @@ -49,7 +49,7 @@ func init() { } dir2 := &embedded.EmbeddedDir{ Filename: "css", - DirModTime: time.Unix(1702222023, 0), + DirModTime: time.Unix(1736106629, 0), ChildFiles: []*embedded.EmbeddedFile{ file3, // "css/bootstrap.min.css" file4, // "css/main.css" @@ -66,7 +66,7 @@ func init() { } dir7 := &embedded.EmbeddedDir{ Filename: "js", - DirModTime: time.Unix(1702222023, 0), + DirModTime: time.Unix(1736115575, 0), ChildFiles: []*embedded.EmbeddedFile{ file8, // "js/main.js" file9, // "js/service_worker.js" diff --git a/static/css/main.css b/static/css/main.css index 8172f20..9a918b8 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -3,146 +3,164 @@ --bs-link-color: var(--link-color); } +* { + overscroll-behavior: contain !important; +} + a { - color: var(--link-color); + color: var(--link-color); } .btn-primary { - background: #1e3650; - border-color: #0e2640; + background: #1e3650; + border-color: #0e2640; } .nav-pills .nav-link.active { - background: #1e3650; + background: #1e3650; } .nav-pills .nav-link { - padding-left: 3px; - padding-right: 3px; + padding-left: 3px; + padding-right: 3px; } .nav-link { - font-size: 10px; + font-size: 10px; } .legend { - color: #777; - margin: 3px 0; - padding: 3px 0; - border-bottom: 1px solid #eee; - font-size: 11px; - text-transform: uppercase; + color: #777; + margin: 3px 0; + padding: 3px 0; + border-bottom: 1px solid #eee; + font-size: 11px; + text-transform: uppercase; } .btn-sm { - font-size: 9px; + font-size: 9px; } .select2 { - min-width: 100%; + min-width: 100%; } .line { - height: 3px; + height: 3px; } .pane { - display: none; + display: none; } .no-margin { - margin: 0; + margin: 0; } .no-padding { - padding: 0; + padding: 0; } .no-radius { - border-radius: 0 !important; + border-radius: 0 !important; } #pointer { - height: calc(100vh - 33px - 38px); - top: calc(33px + 38px); - margin: auto; - background: #ccc; - background-size: contain; - background-repeat: no-repeat; - position: absolute; - width: calc(100% - 50px); - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + height: calc(100vh - 33px); + bottom: 33px; + margin: auto; + background: #ccc; + background-size: contain; + background-repeat: no-repeat; + position: absolute; + width: calc(100% - 50px); + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } #scrollbar { - height: calc(100vh - 80px); - width: 50px; - background: #333; - position: absolute; - z-index: 100; - right: 0; + height: calc(100vh - 33px); + width: 50px; + background: #333; + position: fixed; + bottom: 33px; + z-index: 100; + right: 0; +} + +#mouse { + position: fixed; + bottom: 83px; + z-index: 200; + width: calc(100vw - 50px); + padding: 10px; + background: #fff; } #pane-pointer .form-group { - padding: 0; - margin: 0; + padding: 0; + margin: 0; } #pointer-buttons { - margin-top: -42px; - width: 100%; - z-index: 110; - position: fixed; - bottom: 0; - padding-left: 0; - padding-right: 0; + margin-top: -42px; + width: 100%; + z-index: 110; + position: fixed; + bottom: 31px; + padding-left: 0; + padding-right: 0; } #pointer-buttons .btn { - height: 50px; + height: 50px; } #disconneced { - position: absolute; - top: 0; - width: 100%; - background: #ff6161; - color: #fff; - padding: 5px; + position: absolute; + top: 0; + width: 100%; + background: #ff6161; + color: #fff; + padding: 5px; } #disconneced a { - color: #fff; - font-weight: bold; + color: #fff; + font-weight: bold; } #nav { - border-bottom: 2px solid #1e3650; + border-top: 2px solid #1e3650; + position: fixed; + bottom: 0; + width: 100%; + height: 33px; } #shortcuts_special_keys input { - display: none; + display: none; } #response { - position: absolute; - bottom: 0; - width: 100%; - color: #fff; - background: #748c26; - padding: 5px; - display: none; + position: absolute; + bottom: 0; + width: 100%; + color: #fff; + background: #748c26; + padding: 5px; + display: none; } #screenshot img { - max-width: 100%; - margin-top: 10px; - cursor: pointer; + max-width: 100%; + margin-top: 10px; + cursor: pointer; } #mouse-screenshot-live { diff --git a/static/js/main.js b/static/js/main.js index 9e0b987..9a318b1 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -4,7 +4,7 @@ let scrollLastTimestamp, scrollLastValue let mousePosX, mousePosY, mouseInitPosX, mouseInitPosY let isLive = false let isPointerLive = false -let isScreenshotWaiting = false +let isScreenshotWaiting = null let isPointerScreenshotWaiting = false function createWebSocketConnection() { @@ -33,8 +33,11 @@ function createWebSocketConnection() { response.style.display = 'none' }, 2500) } else if (data.type === 'screenshot') { - if (isScreenshotWaiting) { - isScreenshotWaiting = false + if (isScreenshotWaiting || isScreenshotWaiting === null) { + if (isScreenshotWaiting) { + isScreenshotWaiting = false + } + screenshotImg.setAttribute('src', 'data:image/png;base64, ' + data.value) } @@ -181,7 +184,7 @@ function pointerTouchStartHandler(e) { function pointerLiveHandler(e) { if (!e.target.checked) { isPointerLive = false - isPointerScreenshotWaiting = false + isPointerScreenshotWaiting = null return } @@ -233,7 +236,7 @@ function liveLqClickHandler(e) { function liveClickHandler(e, quality) { if (isLive) { isLive = false - isScreenshotWaiting = false + isScreenshotWaiting = null document.querySelector('#live-hq').innerText = 'Live HQ' document.querySelector('#live-lq').innerText = 'Live LQ' diff --git a/views/layout/base.html b/views/layout/base.html index 9d2cfa0..516162e 100644 --- a/views/layout/base.html +++ b/views/layout/base.html @@ -5,7 +5,7 @@ - + Remote i3-wm