/* jshint node: true */ /* global io, Hammer, $ */ 'use strict'; //-----GLOBAL STATE VARIABLES-----// var socket = io(); var move = false; //Toggles moving functionality var updateKey; // Modal overlay var settingsModal = document.getElementById('myModal');// Modal textbox var editModal = document.getElementById('edit-modal');// Modal textbox var inputModal = document.getElementById('inputText'); var keyboardWidth = document.getElementById('keyboard').offsetWidth;// Width pixel of display var keyboardHeight = document.getElementById('keyboard').offsetHeight;// Height pixel of //$("#touchpad").hide(); //Purpose: Uses interact.js library to enable keys to move around var draggableSettings = { snap: { targets: [ interact.createSnapGrid({ x: .05*keyboardWidth, y: .1*keyboardHeight }) ], range: Infinity }, inertia: true, // enable inertial throwing restrict: { restriction: "parent", // keep the element within the area of it's parent endOnly: true, elementRect: { top: 0, left: 0, bottom: 1, right: 1 } }, autoScroll: true, // enable autoScroll onmove: dragMoveListener, // call this function on every dragmove event onend: function (event) { // call this function on every dragend event } }; /* -----------SETTINGS MODAL--------------- */ var customButtonCounts = {}; var customCount = 0; var move = false; $('#new-keyboard').click(function() { var newSlide ='<div class = "custom swiper-slide swiper-slide-inner" id="custom-' + customCount.toString() + '"> </div>'; customCount++; swiperInner.appendSlide(newSlide) swiper.slideTo(4, 200, false); swiperInner.slideTo(customCount-1, 200, false); customButtonCounts['custom-' + swiperInner.activeIndex.toString()] = 0 socket.emit('newBoard', { index: customCount-1}); }); $('#add-key').click(function() { var newButtonId = 'custom-key-' + swiperInner.activeIndex.toString() + "-" + (customButtonCounts['custom-' + swiperInner.activeIndex.toString()]+1).toString(); $('#custom-' + swiperInner.activeIndex.toString()).append('<button class = "custom-key draggable key-button" id="' + newButtonId + '"></button>'); if(move == false){ $('#'+newButtonId).addClass('activestyle'); } var ele = $('#'+newButtonId) interact('#'+newButtonId).draggable(draggableSettings); customButtonCounts['custom-' + swiperInner.activeIndex.toString()] += 1; ele.text('Yo') console.log((keyboardWidth*.02).toString()) ele.css({position:'absolute', left:'0%', top: '0%', minHeight: (keyboardWidth*.02).toString() + "px"}); }); $('#edit-key').click(function() { if(move){ $('.custom-key').addClass('activestyle'); swiper.allowTouchMove = true; swiperInner.allowTouchMove = true; move = false; } else{ $('.custom-key').removeClass('activestyle'); swiper.allowTouchMove = false; swiperInner.allowTouchMove = false; move = true; } }); $('.close').click(function() { settingsModal.style.display = "none"; }); $('#modal-close').click(function() { editModal.style.display = "none"; }); /* -----------SWIPING FUNCTIONALITY--------------- */ var swiper = new Swiper('.swiper-container', { }); var swiperInner = new Swiper('.swiper-container-inner', { direction: 'vertical', }); var swipeIndex = 0; $('#s0').css("background-color", "green") var mousePos = true; $('.icon-selection').click(function() { if (this.id == 'swap') { if (mousePos){ $('#touchpad').insertBefore('.swiper-container'); mousePos = false; } else { $('.swiper-container').insertBefore('#touchpad'); mousePos = true; } return; } if (this.id == 'settings-select') { // Bring up setting menu settingsModal.style.display = "block"; return; } var newId = parseInt(this.id[1]) if (newId == swipeIndex) { return; } console.log(newId) console.log(swipeIndex) $('#s'+swipeIndex.toString()).css("background-color", "turquoise") $(this).css("background-color", "green") swipeIndex = newId; swiper.slideTo(newId, 200, false); }); //-----LOADING KEYBOARDS-----// //Purpose: Receives information from the server to update the presentation of the keys on the client socket.on('updateKeys', function(newVals) { console.log(newVals); for (var i = 0; i < newVals.x.length; i++){ //special case $('#keyboard').append('<button class = "draggable activestyle key-button" id="button' + (i+1).toString() + '">' + newVals.k[i] + '</button>'); var ele = $('#button' + (i+1).toString()) ele.text(newVals.k[i]) ele.css({position:'absolute', left:newVals.x[i] + '%', top:(newVals.y[i]) + '%', minHeight: (keyboardWidth*.02).toString() + "px"}); } $('#loading').html(''); }); //Purpose: Receives information from the server to update the presentation of the keys on the client socket.on('updateUrls', function(newVals) { console.log(newVals); for (var i = 0; i < newVals.x.length; i++){ $('#hotkeys').append('<button class = "draggable activestyle url-button" id="url' + (i+1).toString() + '">' + newVals.k[i] + '</button>'); var ele = $('#url' + (i+1).toString()) ele.text(newVals.k[i]) ele.css({position:'absolute', left:newVals.x[i] + '%', top:(newVals.y[i]) + '%', minHeight: (keyboardWidth*.02).toString() + "px", width: '20%', 'text-indent': '-9999px'}); ele.append('<button class="url-icon" id="url-icon' +(i+1).toString() +'"> </button>') var urlIcon = $('#url-icon' + (i+1).toString()) try { var favicon_url = getFavicon(newVals.k[i]); } catch(err) { var favicon_url = '' } urlIcon.css({position: 'absolute', left: '35%', bottom: '20%', width: '30%', height: '60%', background: 'url(' + favicon_url + ')', 'background-size': 'contain', 'border-radius': '0px', border:'0px', 'background-repeat': 'no-repeat'}); } $('#loading').html(''); }); //Purpose: Receives information from the server to update the presentation of the keys on the client socket.on('updateNumPad', function(newVals) { console.log(newVals); for (var i = 0; i < newVals.x.length; i++){ $('#numpad').append('<button class = "draggable activestyle key-button" id="pad' + (i+1).toString() + '">' + newVals.k[i] + '</button>'); var ele = $('#pad' + (i+1).toString()) ele.text(newVals.k[i]) ele.css({position:'absolute', left:newVals.x[i] + '%', top:(newVals.y[i]) + '%', minHeight: (keyboardWidth*.02).toString() + "px"}); } $('#loading').html(''); }); //Purpose: Receives information from the server to update the presentation of the keys on the client socket.on('updateCustom', function(newVals) { console.log(newVals); var newSlide ='<div class = "custom swiper-slide swiper-slide-inner" id="custom-' + customCount + '"> </div>'; swiperInner.appendSlide(newSlide) for (var i = 0; i < newVals.x.length; i++){ $('#custom-' + customCount).append('<button class = "custom-key draggable activestyle key-button" id="custom-key-' + customCount + "-" + (i+1).toString() + '"></button>'); var ele = $('#custom-key-' + customCount.toString() + "-" + (i+1).toString()) ele.text(newVals.k[i]) ele.css({width:'auto', left:newVals.x[i] + '%', top:(newVals.y[i]) + '%', minHeight: (keyboardWidth*.02).toString() + "px"}); } customButtonCounts['custom-' + customCount] = newVals.x.length; customCount++; $('#loading').html(''); }); /* -----------KEYBOARD CONTROLS--------------- The Following Functions Control the KEYBOARD FUNCTIONALITY */ //Purpose: Emits the key of keyboard upon click var emitKey = function(str) { if (move === false){ socket.emit('string', str); console.log(str); } }; var emitUrl = function(str) { if (move === false){ console.log(str); socket.emit('url', str); } }; interact('.custom-key').draggable(draggableSettings); var emitText = function(text) { if (move === false){ console.log(text); socket.emit('text', text); } }; $( "#textfield" ).keydown(function(event) { if (event.key === 'Enter') { // enter emitText(document.getElementById('textfield').value); event.preventDefault(); if (document.getElementById('textfield').value === '') { socket.emit('functionality', 'enter'); } document.getElementById('textfield').value = ''; } else if (event.key === 'Backspace' && document.getElementById('textfield').value === "") { // backspace socket.emit('functionality', 'backspace'); } else if (event.key.startsWith('Arrow') && document.getElementById('textfield').value === "") { // arrow keys socket.emit('functionality', event.key); } else if (event.keyCode === 32 && document.getElementById('textfield').value === '') { // space socket.emit('functionality', 'Space'); document.getElementById('textfield').value = ''; } }); //Purpose: Uses interact.js library to enable keys to move around interact('.draggable').draggable({ snap: { targets: [ interact.createSnapGrid({ x: .05*keyboardWidth, y: .1*keyboardHeight }) ], range: Infinity }, inertia: true, // enable inertial throwing restrict: { restriction: "parent", // keep the element within the area of it's parent endOnly: true, elementRect: { top: 0, left: 0, bottom: 1, right: 1 } }, autoScroll: true, // enable autoScroll onmove: dragMoveListener, // call this function on every dragmove event onend: function (event) { // call this function on every dragend event } }); function dragMoveListener (event) { if (move === true){ var target = event.target, // keep the dragged position in the data-x/data-y attributes x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx, y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy; // translate the element target.style.webkitTransform = target.style.transform = 'translate(' + x + 'px, ' + y + 'px)'; // update the posiion attributes target.setAttribute('data-x', x); target.setAttribute('data-y', y); } } //Purpose: Event listener on tapping the keys interact('.key-button').on('tap', function (event) { emitKey(event.target.innerText); }) interact('.custom-key').on('hold', function (event) { if (move == true){ editModal.style.display = "block"; //Allows the modal to be displayed to User updateKey = event.target inputModal.value = updateKey.innerText } }); interact('.static-key').on('tap', function (event) { emitKey(event.target.innerText); }); interact('.url-button').on('tap', function (event) { if(event.target.id.indexOf("url-icon") != -1){ emitUrl($("#" + event.target.id).parent().text().trim()) } else{ emitUrl(event.target.innerText); } }); //Purpose: Updates button with textbox value $('#modal-save').click(function() { var newX = parseInt(updateKey.style.left) + updateKey.getAttribute('data-x')/keyboardWidth*100 var newY = parseInt(updateKey.style.top) + updateKey.getAttribute('data-y')/keyboardHeight*100 newX = parseInt(newX) newY = parseInt(newY) newX = Math.min(Math.max(0, newX),100).toString() newY = Math.min(Math.max(0, newY),100).toString() console.log(newX, newY) updateKey.innerText = inputModal.value; $("#"+updateKey.id).css({'width': 'auto'}) socket.emit('saveKey', { index: swiperInner.activeIndex, id: updateKey.id, val: updateKey.innerText, x: newX, y: newY }); }); //Purpose: Used for resizing and gestures window.dragMoveListener = dragMoveListener; /* -----------MOUSE CONTROLS--------------- The Following Functions Control the Mouse */ var touchElem = document.getElementById('mousepad'); var lcElem = document.getElementById('leftClick'); var scrollElem = document.getElementById('scrollWheel'); var rcElem = document.getElementById('rightClick'); var delta = null; var moving = false; var control = 'touch'; var passcode = ''; var pos = {x: 0, y: 0, cmd: null, pw: ''}; //Purpose: Wrapper Function to send information from client to server via socket var emitMouse = function(x, y, cmd) { pos.x = x; pos.y = y; pos.cmd = cmd; pos.pw = passcode; socket.emit('mouse', pos); }; //Purpose: Handles touch movement events from the clients var handlePan = function(eventName, e) { if (e.type == eventName + 'start') { delta = null; moving = true; console.log('start ' + eventName); emitMouse(0, 0, eventName + 'start'); } if (e.type == eventName + 'end') { delta = null; moving = false; emitMouse(0, 0, eventName + 'end'); } if (moving && delta != null) { emitMouse(e.deltaX - delta.x, e.deltaY - delta.y, eventName); } delta = {x: e.deltaX, y: e.deltaY}; }; //Purpose: Using Hammer.js library to add different touching functionality var mc = new Hammer.Manager(touchElem); var mcRc = new Hammer.Manager(rcElem); var mcLc = new Hammer.Manager(lcElem); var mcScroll = new Hammer.Manager(scrollElem); mc.add(new Hammer.Pan({event: 'move', threshold: 0, pointers: 1, direction: Hammer.DIRECTION_ALL})); mc.add(new Hammer.Pan({event: 'scroll', threshold: 0, pointers: 2,direction: Hammer.DIRECTION_ALL})); mcScroll.add(new Hammer.Pan({event: 'scroll', threshold: 0, pointers: 1,direction: Hammer.DIRECTION_ALL})); mc.add(new Hammer.Pan({event: 'drag', threshold: 0, pointers: 3, direction: Hammer.DIRECTION_ALL})); mcLc.add(new Hammer.Pan({event: 'drag', threshold: 0, pointers: 1,direction: Hammer.DIRECTION_ALL})); //Purpose: Tapping functionality var singleTap = new Hammer.Tap({event: 'click', pointers: 1}); var doubleTap = new Hammer.Tap({event: 'doubleclick', pointers: 1, taps: 2}); var tripleTap = new Hammer.Tap({event: 'tripleclick', pointers: 1, taps: 3}); mc.add([tripleTap, doubleTap, singleTap]); mcLc.add([tripleTap, doubleTap, singleTap]); tripleTap.recognizeWith([doubleTap, singleTap]); doubleTap.recognizeWith(singleTap); doubleTap.requireFailure(tripleTap); singleTap.requireFailure([tripleTap, doubleTap]); mc.add(new Hammer.Tap({event: 'rightclick', pointers: 2})); mcRc.add(new Hammer.Tap({event: 'rightclick', pointers: 1})); //Purpose: Using Hammer.js event listeners to trigger functionality by sending data to mc.on('movestart moveend moveup movedown moveleft moveright', function(e) { handlePan('move', e); }); mc.on('scrollstart scrollend scrollup scrolldown scrollleft scrollright', function(e) { handlePan('scroll', e); }); mcScroll.on('scrollstart scrollend scrollup scrolldown scrollleft scrollright', function(e) { handlePan('scroll', e); }); mc.on('dragstart dragend dragup dragdown dragleft dragright', function(e) { handlePan('drag', e); }); mcLc.on('dragstart dragend dragup dragdown dragleft dragright', function(e) { handlePan('drag', e); }); mc.on('click', function(e) { console.log('click'); emitMouse(0, 0, 'click'); }); mcLc.on('click', function(e) { console.log('click'); emitMouse(0, 0, 'click'); }); mc.on('rightclick', function(e) { console.info('rightclick'); emitMouse(0, 0, 'rightclick'); }); mcRc.on('rightclick', function(e) { console.info('rightclick'); emitMouse(0, 0, 'rightclick'); }); mc.on('doubleclick', function(e) { console.log('doubleclick'); emitMouse(0, 0, 'doubleclick'); }); mcLc.on('doubleclick', function(e) { console.log('doubleclick'); emitMouse(0, 0, 'doubleclick'); }); /* -----------MENU CONTROLS--------------- The Following Functions Control the Main Menu */ document.body.requestFullscreen = document.body.requestFullScreen || document.body.webkitRequestFullScreen || document.body.mozRequestFullScreen || document.body.msRequestFullScreen; document.cancelFullscreen = document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen; //PURPOSE: Toggle movement functionality $('#fullscreen-toggle').click(function() { if (this.checked) { $('.draggable').hide(); $('#keyboard').hide(); $("#touchpad").show(); //document.body.requestFullscreen(); } else { $('.draggable').show(); $('#keyboard').show(); $("#touchpad").hide(); //document.cancelFullscreen(); } }); //PURPOSE: Toggle movement functionality $('.selection').click(function() { if (this.checked) { $('.draggable').hide(); $('#keyboard').hide(); $("#touchpad").show(); //document.body.requestFullscreen(); } else { $('.draggable').show(); $('#keyboard').show(); $("#touchpad").hide(); //document.cancelFullscreen(); } }); //PURPOSE: Toggle movement functionality $('#move-toggle').click(function() { if (this.checked) { move = true; } else { move = false; } }); //PURPOSE: If password is set, asks user for password $('#passcode').click(function() { passcode = prompt('Enter a passcode'); }); //PURPOSE: About the project $('#about').click(function() { if (confirm('UBoard: A mobile mouse for Brad')) { open('https://github.com/jjlustig/EECS498_uBoard'); } });