let ws let pointer, scroller, response, screenshotImg let scrollLastTimestamp, scrollLastValue let mousePosX, mousePosY, mouseInitPosX, mouseInitPosY let isLive = false let wsLock = false let isPointerLive = false let isScreenshotWaiting = null let isPointerScreenshotWaiting = false let emptyImg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gIJDjc3srQk8gAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAADElEQVQI12P48+cPAAXsAvVTWDc6AAAAAElFTkSuQmCC" function createWebSocketConnection() { const protocol = location.protocol === 'https:' ? 'wss' : 'ws' ws = new WebSocket(`${protocol}://${window.location.hostname}:${window.location.port}/ws`) ws.addEventListener('open', function(event) { document.querySelector('#disconneced').style.display = 'none' unLock() }) ws.addEventListener('close', function(event) { unLock() document.querySelector('#disconneced').style.display = 'block' window.setTimeout(createWebSocketConnection, 5000) }) ws.addEventListener('message', function(event) { unLock() let data = JSON.parse(event.data) if (data.type === 'response') { response.innerText = data.value response.style.display = 'block' window.setTimeout(function() { response.style.display = 'none' }, 2500) return } if (data.type === 'volume') { if (data.value.length) { setVolume(parseInt(data.value)) } return } }) } function isLocked() { return wsLock === true } function lock() { wsLock = true } function unLock() { wsLock = false } function send(message) { if (isLocked()) { return } lock() ws.send(message) } function navigationClickHandler(e) { if (e.target.getAttribute('href') === '#') { return } Array.from(document.querySelectorAll('.pane')).forEach((item) => { item.style.display = 'none' }) document.querySelector(e.target.getAttribute('href')).style.display = 'block' Array.from(document.querySelectorAll('#nav a')).forEach((item) => { item.classList.remove('active') }) e.target.classList.add('active') } function buttonClickHandler(e) { send(e.target.getAttribute('data-msg')) } function shortcutClearClickHandler(e) { document.querySelector('#shortcut-key').value = '' Array.from(document.querySelectorAll('#shortcuts_special_keys input:checked')).forEach((item) => { item.checked = false item.change() }) } function shortcutSendClickHandler(e) { let keys = [] let key = document.querySelector('#shortcut-key').value Array.from(document.querySelectorAll('#shortcuts_special_keys input:checked')).forEach((item) => { keys.push(item.value) }) if (keys.length) { if (key) { keys.push(key) } send('{"type":"keys","value": "' + (keys.join(',').replace('"', '\\"')) + '"}') } } function textClearClickHandler(e) { document.querySelector('#text').value = '' } function textSendClickHandler(e) { const keys = document.querySelector('#text').value if (keys.length) { send('{"type":"text","value": "' + (keys.replace('"', '\\"')) + '"}') } } function textKeyUpHandler(e) { const keys = document.querySelector('#text').value if (e.keyCode === 13) { send('{"type":"text","value": "' + (keys.replace('"', '\\"')) + '"}') } } function liveTextKeyUpHandler(e) { const value = e.target.value const size = value.length const messages = [] if (size > 0) { messages.push('{"type":"text","value": "' + (value.replace('"', '\\"')) + '"}') if (value[size-1] === ' ') { messages.push('{"type":"key","value":"space"}') } } if (e.keyCode === 8) { messages.push('{"type":"key","value": "backspace"}') } else if (e.keyCode === 13 && size === 0) { messages.push('{"type":"key","value": "enter"}') } send(`{"type":"messages","value":[${messages.join(',')}]}`) e.target.value = '' } function shortcutsSpecialKeysOnChangeHandler(e) { Array.from(document.querySelectorAll('#shortcuts_special_keys input:checked')).forEach((item) => { item.parentNode.classList.add('btn-primary') item.parentNode.classList.remove('btn-secondary') }) Array.from(document.querySelectorAll('#shortcuts_special_keys input:not(:checked)')).forEach((item) => { item.parentNode.classList.add('btn-secondary') item.parentNode.classList.remove('btn-primary') }) } function pointerClickHandler(e) { send('{"type":"pointer","click":"left"}') } function scrollerTouchStartHandler(e) { mouseInitPosY = e.targetTouches[0].pageY } function scrollerTouchMoveHandler(e) { let touch = e.changedTouches[0] let value = ((touch.pageY - mouseInitPosY > 0) ? 'down' : 'up') let now = new Date().getTime() if (touch.pageY === mouseInitPosY || value === scrollLastValue && scrollLastTimestamp !== null && now - scrollLastTimestamp < 200) { return } scrollLastTimestamp = now scrollLastValue = value mouseInitPosY = touch.pageY send('{"type":"scroll","value": "' + value + '"}') } function pointerTouchStartHandler(e) { const touch = e.targetTouches[0] mouseInitPosX = touch.pageX mouseInitPosY = touch.pageY } function pointerLiveHandler(e) { if (!e.target.checked) { pointer.style.backgroundImage = "" } else { pointer.style.backgroundImage = `url("/capture?type=live&pointer=1&${Math.random()}")` } } function pointerTouchMoveHandler(e) { if (e.changedTouches.length === 2) { return scrollerTouchMoveHandler(e) } const touch = e.changedTouches[0] mousePosX = touch.pageX mousePosY = touch.pageY const newX = mousePosX - mouseInitPosX const newY = mousePosY - mouseInitPosY mouseInitPosX = mousePosX mouseInitPosY = mousePosY let msg = '{"type":"pointer","x": "' + newX + '","y": "' + newY + '"}' ws.send(msg) } function capture(mode) { } function captureScreenshotClickHandler(e) { const img = e.target.parentNode.querySelector('.capture-img img') img.src = "/capture?type=screenshot&" + Math.random() } function captureLiveClickHandler(e) { const img = e.target.parentNode.querySelector('.capture-img img') if (img.src.indexOf("live") > -1) { img.src = emptyImg } else { img.src = "/capture?type=live&" + Math.random() } } function fullscreenHandler(e) { const targetConf = e.target.getAttribute('data-target') const isFullscreen = parseInt(e.target.getAttribute('data-fullscreen')) const element = (targetConf === 'this') ? e.target : document.querySelector(targetConf) document.querySelector('body').classList.toggle('fullscreen', isFullscreen) if (isFullscreen) { e.target.setAttribute('data-fullscreen', '0') if (document.exitFullscreen) { document.exitFullscreen() } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen() } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen() } } else { e.target.setAttribute('data-fullscreen', '1') if (element.requestFullscreen) { element.requestFullscreen() } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen() } else if (element.mozRequestFullScreen) { element.mozRequestFullScreen() } } } function documentHashHandler() { const hash = window.location.hash if (hash) { document.querySelector('a[href="' + hash + '"]').click() } else { document.querySelector('#nav > li:first-child a').click() } } function addEventListenerOn(selector, eventName, listener) { if (typeof selector === 'string') { Array.from(document.querySelectorAll(selector)).forEach((element) => { element.addEventListener(eventName, listener) }) } else { selector.addEventListener(eventName, listener) } } function addListeners() { addEventListenerOn('#nav a', 'click', navigationClickHandler) addEventListenerOn('button[data-msg]', 'click', buttonClickHandler) addEventListenerOn('#shortcut-clear', 'click', shortcutClearClickHandler) addEventListenerOn('#shortcuts_special_keys input', 'change', shortcutsSpecialKeysOnChangeHandler) addEventListenerOn('#shortcut-send', 'click', shortcutSendClickHandler) addEventListenerOn('#text-clear', 'click', textClearClickHandler) addEventListenerOn('#text-send', 'click', textSendClickHandler) addEventListenerOn('#text', 'keyup', textKeyUpHandler) Array.from(document.querySelectorAll('.live-text')).forEach((element) => { element.setAttribute('data-composing', '0') addEventListenerOn(element, 'compositionstart', (e) => { element.setAttribute('data-composing', '1') }) addEventListenerOn(element, 'compositionend', (e) => { element.setAttribute('data-composing', '0') }) addEventListenerOn(element, 'keyup', (e) => { if (element.getAttribute('data-composing') === '1') { return } liveTextKeyUpHandler(e) }) }) addEventListenerOn(scroller, 'touchstart', scrollerTouchStartHandler) addEventListenerOn(scroller, 'touchmove', scrollerTouchMoveHandler) addEventListenerOn(pointer, 'click', pointerClickHandler) addEventListenerOn(pointer, 'touchstart', pointerTouchStartHandler) addEventListenerOn(pointer, 'touchmove', pointerTouchMoveHandler) addEventListenerOn('#mouse-screenshot-live input', 'change', pointerLiveHandler) addEventListenerOn('.capture-live', 'click', captureLiveClickHandler) addEventListenerOn('.capture-screenshot', 'click', captureScreenshotClickHandler) addEventListenerOn('.btn-fullscreen', 'click', fullscreenHandler) } function getVolume() { if (document.querySelectorAll('.volume').length) { try { send('{"type":"volume","value":"value"}') } catch (e) { } document.querySelectorAll('.volume input[type="range"]').forEach(function(input) { if (input.getAttribute('data-event')) { return } input.setAttribute('data-event', 'ok') input.addEventListener('change', (e) => { send(`{"type":"volume","value":"${e.target.value}"}`) }) }) } window.setTimeout(getVolume, 2000) } function setVolume(value) { document.querySelectorAll('.volume').forEach(function(item) { item.querySelector('input[type="range"]').value = value }) } function bootstrap() { pointer = document.querySelector('#pointer') scroller = document.querySelector('#scrollbar') response = document.querySelector('#response') screenshotImg = document.querySelector('#screenshot img') shortcutsSpecialKeysOnChangeHandler() createWebSocketConnection() addListeners() documentHashHandler() getVolume() if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/static/js/service_worker.js') } } addEventListenerOn(window, 'DOMContentLoaded', bootstrap)