mirror of
https://github.com/lovasoa/whitebophir
synced 2024-11-10 06:24:17 +00:00
Add a cursor (#46)
* Added cursors * Prepare cursor code for future settings to toggle cursors on or off. Let cursor be the color the person has currently selected * fix cursor on mobile (still won't display it in most cases as there is no hover on mobile but at least it won't throw errors) * use correct size for cursor * throttle cursor update rate to dramatically improve performance by eliminating congestion * fix remote cursor size on desktop * show own cursor by default and renove offset * use svg as mouse cursor for pencil to be able to apply a reduced opacity to it and view our cursor * don't throttle local cursor * throttle local cursor at an independent higher rate. This could be made user adjustable for low power devices * remove let and const from client-side code * get emit count and emit count period from configuration * reduce network cursor updates a lot to prevent instantly getting banned with the current defaults * prevent eraser from deleting cursors * use group inside of svg as drawing area and only delete elements inside it with the eraser * use transform: translate to move cursors around instead of manipulating x and y directly * fix: add socket ids to cursor messages * fix incorrect remote cursor scaling and make local cursor visible again after it has been moved after being hidden due to inactivity * create cursors in a proper fashion and keep them in a separate group * scaling has been fixed ina1a5580
* move duplicated cursor creation code to function * show cursors above content * pass some of ther server configuration through to the client * fix bug introduced ina833ce9
* allocate at most half of the allowed traffic to cursor updates * remove debugging leftover * use feature detection instead of ua sniffing Co-Authored-By: Ophir LOJKINE <ophir.lojkine@auto-grid.com> * fix regression where local cursor color was not updated on color change * Define the cursor as a tool * Remove the cursor tool from the UI * Throttle remote cursor updates, not local ones * Do not increment notification count on cursor move * Use only one pencil icon Use the same image for the pencil icon in the menu and the pencil cursor that appears while drawing * Add a test for the new cursor feature * only stop drawing remote cursor when using some tools and always draw local cursor * increase idle period before hiding cursor * change idle duration back and set whether a cursor should be sent when using a tool in the respective tool Co-authored-by: Robert Beach <rdbeach@gmail.com> Co-authored-by: Ophir LOJKINE <ophir.lojkine@auto-grid.com> Co-authored-by: ophir <pere.jobs@gmail.com>
This commit is contained in:
parent
b3e8e30c16
commit
fce694df28
18 changed files with 269 additions and 61 deletions
|
@ -237,3 +237,12 @@ text {
|
|||
user-select:none;
|
||||
-moz-user-select:none;
|
||||
}
|
||||
|
||||
circle.opcursor {
|
||||
pointer-events: none;
|
||||
transition: .1s;
|
||||
}
|
||||
|
||||
#cursor-me {
|
||||
transition: 0s;
|
||||
}
|
|
@ -27,6 +27,8 @@
|
|||
<div id="board">
|
||||
<svg id="canvas" width="500" height="500" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs id="defs"></defs>
|
||||
<g id="drawingArea"></g>
|
||||
<g id="cursors"></g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
|
@ -64,7 +66,7 @@
|
|||
<rect x=2 y=0 width=2 height=2 fill=#eeeeee />
|
||||
<rect x=0 y=2 width=2 height=2 fill=#eeeeee />
|
||||
</pattern>
|
||||
<circle cx=4 cy=4 id="opacityIndicator" r=3.5 fill="url(#opacityPattern)" />
|
||||
<circle cx=4 cy=4 id="opacityIndicator" r=3.5 fill="url(#opacityPattern)" />
|
||||
</svg>
|
||||
</span>
|
||||
<label class="tool-name slider" for="chooseOpacity">
|
||||
|
@ -78,11 +80,13 @@
|
|||
</div>
|
||||
|
||||
|
||||
<script type="application/json" id="translations">{{{json translations}}}</script>
|
||||
<script type="application/json" id="translations">{{{ json translations }}}</script>
|
||||
<script type="application/json" id="configuration">{{{ json configuration }}}</script>
|
||||
<script src="../js/path-data-polyfill.js"></script>
|
||||
<script src="../js/minitpl.js"></script>
|
||||
<script src="../js/board.js"></script>
|
||||
<script src="../tools/pencil/pencil.js"></script>
|
||||
<script src="../tools/cursor/cursor.js"></script>
|
||||
<script src="../tools/line/line.js"></script>
|
||||
<script src="../tools/rect/rect.js"></script>
|
||||
<script src="../tools/text/text.js"></script>
|
||||
|
|
|
@ -36,14 +36,20 @@ Tools.i18n = (function i18n() {
|
|||
};
|
||||
})();
|
||||
|
||||
Tools.server_config = JSON.parse(document.getElementById("configuration").text);
|
||||
|
||||
Tools.board = document.getElementById("board");
|
||||
Tools.svg = document.getElementById("canvas");
|
||||
Tools.drawingArea = Tools.svg.getElementById("drawingArea");
|
||||
|
||||
//Initialization
|
||||
Tools.curTool = null;
|
||||
Tools.showMarker = true;
|
||||
Tools.showOtherCursors = true;
|
||||
Tools.showMyCursor = true;
|
||||
|
||||
Tools.socket = null;
|
||||
Tools.connect = function() {
|
||||
Tools.connect = function () {
|
||||
var self = this;
|
||||
|
||||
// Destroy socket if one already exists
|
||||
|
@ -112,7 +118,7 @@ Tools.HTML = {
|
|||
Tools.i18n.t(toolName) + " (" +
|
||||
Tools.i18n.t("keyboard shortcut") + ": " +
|
||||
toolShortcut + ")" +
|
||||
(Tools.list[toolName].toggle? " [" + Tools.i18n.t("Click to togle")+ "]":"");
|
||||
(Tools.list[toolName].toggle ? " [" + Tools.i18n.t("Click to togle") + "]" : "");
|
||||
});
|
||||
},
|
||||
changeTool: function (oldToolName, newToolName) {
|
||||
|
@ -146,7 +152,10 @@ Tools.HTML = {
|
|||
|
||||
Tools.list = {}; // An array of all known tools. {"toolName" : {toolObject}}
|
||||
|
||||
Tools.add = function (newTool) {
|
||||
/**
|
||||
* Register a new tool, without touching the User Interface
|
||||
*/
|
||||
Tools.register = function registerTool(newTool) {
|
||||
if (newTool.name in Tools.list) {
|
||||
console.log("Tools.add: The tool '" + newTool.name + "' is already" +
|
||||
"in the list. Updating it...");
|
||||
|
@ -158,13 +167,6 @@ Tools.add = function (newTool) {
|
|||
//Add the tool to the list
|
||||
Tools.list[newTool.name] = newTool;
|
||||
|
||||
if (newTool.stylesheet) {
|
||||
Tools.HTML.addStylesheet(newTool.stylesheet);
|
||||
}
|
||||
|
||||
//Add the tool to the GUI
|
||||
Tools.HTML.addTool(newTool.name, newTool.icon, newTool.iconHTML, newTool.shortcut);
|
||||
|
||||
//There may be pending messages for the tool
|
||||
var pending = Tools.pendingMessages[newTool.name];
|
||||
if (pending) {
|
||||
|
@ -175,6 +177,20 @@ Tools.add = function (newTool) {
|
|||
newTool.draw(msg, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new tool to the user interface
|
||||
*/
|
||||
Tools.add = function (newTool) {
|
||||
Tools.register(newTool);
|
||||
|
||||
if (newTool.stylesheet) {
|
||||
Tools.HTML.addStylesheet(newTool.stylesheet);
|
||||
}
|
||||
|
||||
//Add the tool to the GUI
|
||||
Tools.HTML.addTool(newTool.name, newTool.icon, newTool.iconHTML, newTool.shortcut);
|
||||
};
|
||||
|
||||
Tools.change = function (toolName) {
|
||||
|
@ -185,7 +201,7 @@ Tools.change = function (toolName) {
|
|||
var newtool = Tools.list[toolName];
|
||||
|
||||
if (newtool === Tools.curTool) {
|
||||
if(newtool.toggle){
|
||||
if (newtool.toggle) {
|
||||
var elem = document.getElementById("toolID-" + newtool.name);
|
||||
newtool.toggle(elem);
|
||||
}
|
||||
|
@ -208,26 +224,34 @@ Tools.change = function (toolName) {
|
|||
if (newtool === Tools.curTool) return;
|
||||
|
||||
//Remove the old event listeners
|
||||
for (var event in Tools.curTool.compiledListeners) {
|
||||
var listener = Tools.curTool.compiledListeners[event];
|
||||
Tools.board.removeEventListener(event, listener);
|
||||
}
|
||||
Tools.removeToolListeners(Tools.curTool);
|
||||
|
||||
//Call the callbacks of the old tool
|
||||
Tools.curTool.onquit(newtool);
|
||||
}
|
||||
|
||||
//Add the new event listeners
|
||||
for (var event in newtool.compiledListeners) {
|
||||
var listener = newtool.compiledListeners[event];
|
||||
Tools.board.addEventListener(event, listener, { 'passive': false });
|
||||
}
|
||||
Tools.addToolListeners(newtool);
|
||||
|
||||
//Call the start callback of the new tool
|
||||
newtool.onstart(Tools.curTool);
|
||||
Tools.curTool = newtool;
|
||||
};
|
||||
|
||||
Tools.addToolListeners = function addToolListeners(tool) {
|
||||
for (var event in tool.compiledListeners) {
|
||||
var listener = tool.compiledListeners[event];
|
||||
Tools.board.addEventListener(event, listener, { 'passive': false });
|
||||
}
|
||||
}
|
||||
|
||||
Tools.removeToolListeners = function removeToolListeners(tool) {
|
||||
for (var event in tool.compiledListeners) {
|
||||
var listener = tool.compiledListeners[event];
|
||||
Tools.board.removeEventListener(event, listener);
|
||||
}
|
||||
}
|
||||
|
||||
Tools.send = function (data, toolName) {
|
||||
toolName = toolName || Tools.curTool.name;
|
||||
var d = data;
|
||||
|
@ -236,13 +260,14 @@ Tools.send = function (data, toolName) {
|
|||
var message = {
|
||||
"board": Tools.boardName,
|
||||
"data": d
|
||||
}
|
||||
};
|
||||
Tools.socket.emit('broadcast', message);
|
||||
};
|
||||
|
||||
Tools.drawAndSend = function (data) {
|
||||
Tools.curTool.draw(data, true);
|
||||
Tools.send(data);
|
||||
Tools.drawAndSend = function (data, tool) {
|
||||
if (tool == null) tool = Tools.curTool;
|
||||
tool.draw(data, true);
|
||||
Tools.send(data, tool.name);
|
||||
};
|
||||
|
||||
//Object containing the messages that have been received before the corresponding tool
|
||||
|
@ -519,12 +544,12 @@ Tools.setSize = (function size() {
|
|||
|
||||
chooser.onchange = chooser.oninput = update;
|
||||
return function (value) {
|
||||
if (value !== null && value !== undefined) { chooser.value=value; update(); }
|
||||
if (value !== null && value !== undefined) { chooser.value = value; update(); }
|
||||
return parseInt(chooser.value);
|
||||
};
|
||||
})();
|
||||
|
||||
Tools.getSize = (function() {return Tools.setSize()});
|
||||
Tools.getSize = (function () { return Tools.setSize() });
|
||||
|
||||
Tools.getOpacity = (function opacity() {
|
||||
var chooser = document.getElementById("chooseOpacity");
|
||||
|
|
99
client-data/tools/cursor/cursor.js
Normal file
99
client-data/tools/cursor/cursor.js
Normal file
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* WHITEBOPHIR
|
||||
*********************************************************
|
||||
* @licstart The following is the entire license notice for the
|
||||
* JavaScript code in this page.
|
||||
*
|
||||
* Copyright (C) 2020 Ophir LOJKINE
|
||||
*
|
||||
*
|
||||
* The JavaScript code in this page is free software: you can
|
||||
* redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License (GNU GPL) as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option)
|
||||
* any later version. The code is distributed WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
|
||||
*
|
||||
* As additional permission under GNU GPL version 3 section 7, you
|
||||
* may distribute non-source (e.g., minimized or compacted) forms of
|
||||
* that code without the copy of the GNU GPL normally required by
|
||||
* section 4, provided you include this license notice and a URL
|
||||
* through which recipients can access the Corresponding Source.
|
||||
*
|
||||
* @licend
|
||||
*/
|
||||
|
||||
(function () { // Code isolation
|
||||
|
||||
// Allocate half of the maximum server updates to cursor updates
|
||||
var MAX_CURSOR_UPDATES_INTERVAL_MS = Tools.server_config.MAX_EMIT_COUNT_PERIOD / Tools.server_config.MAX_EMIT_COUNT * 2;
|
||||
|
||||
var CURSOR_DELETE_AFTER_MS = 1000 * 5;
|
||||
|
||||
var lastCursorUpdate = 0;
|
||||
var sending = true;
|
||||
|
||||
var cursorTool = {
|
||||
"name": "Cursor",
|
||||
"listeners": {
|
||||
"press": function () { sending = false },
|
||||
"move": handleMarker,
|
||||
"release": function () { sending = true },
|
||||
},
|
||||
"draw": draw,
|
||||
"mouseCursor": "crosshair",
|
||||
"icon": "tools/pencil/icon.svg",
|
||||
};
|
||||
Tools.register(cursorTool);
|
||||
Tools.addToolListeners(cursorTool);
|
||||
|
||||
function handleMarker(x, y) {
|
||||
if (!Tools.showMarker || !Tools.showMyCursor) return;
|
||||
|
||||
// throttle local cursor updates
|
||||
var cur_time = Date.now();
|
||||
var message = {
|
||||
type: "update",
|
||||
x: x,
|
||||
y: y,
|
||||
color: Tools.getColor(),
|
||||
size: Tools.getSize(),
|
||||
};
|
||||
|
||||
if (cur_time - lastCursorUpdate > MAX_CURSOR_UPDATES_INTERVAL_MS &&
|
||||
(sending || Tools.curTool.showMarker)) {
|
||||
Tools.drawAndSend(message, cursorTool);
|
||||
lastCursorUpdate = cur_time;
|
||||
} else {
|
||||
draw(message);
|
||||
}
|
||||
}
|
||||
|
||||
var cursorsElem = Tools.svg.getElementById("cursors");
|
||||
|
||||
function createCursor(id) {
|
||||
var cursor = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||||
cursor.setAttributeNS(null, "class", "opcursor");
|
||||
cursor.setAttributeNS(null, "id", id);
|
||||
cursor.setAttributeNS(null, "cx", 0);
|
||||
cursor.setAttributeNS(null, "cy", 0);
|
||||
cursor.setAttributeNS(null, "r", 10);
|
||||
cursorsElem.append(cursor);
|
||||
setTimeout(function () {
|
||||
cursorsElem.removeChild(cursor);
|
||||
}, CURSOR_DELETE_AFTER_MS);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
function getCursor(id) {
|
||||
return document.getElementById(id) || createCursor(id);
|
||||
}
|
||||
|
||||
function draw(message) {
|
||||
var cursor = getCursor("cursor-" + (message.socket || 'me'));
|
||||
cursor.style.transform = "translate(" + message.x + "px, " + message.y + "px)";
|
||||
cursor.setAttributeNS(null, "fill", message.color);
|
||||
cursor.setAttributeNS(null, "r", message.size / 2);
|
||||
}
|
||||
})()
|
|
@ -39,6 +39,22 @@
|
|||
"type": "delete",
|
||||
"id": ""
|
||||
};
|
||||
|
||||
function inDrawingArea(elem) {
|
||||
if (Tools.drawingArea.contains) {
|
||||
return Tools.drawingArea.contains(elem);
|
||||
} else {
|
||||
var node = elem.parentNode;
|
||||
while (node != null) {
|
||||
if (node === Tools.drawingArea) {
|
||||
return true;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function erase(x, y, evt) {
|
||||
// evt.target should be the element over which the mouse is...
|
||||
var target = evt.target;
|
||||
|
@ -48,7 +64,7 @@
|
|||
var touch = evt.touches[0];
|
||||
target = document.elementFromPoint(touch.clientX, touch.clientY);
|
||||
}
|
||||
if (erasing && target !== Tools.svg) {
|
||||
if (erasing && target !== Tools.svg && target !== Tools.drawingArea && inDrawingArea(target)) {
|
||||
msg.id = target.id;
|
||||
Tools.drawAndSend(msg);
|
||||
}
|
||||
|
@ -65,7 +81,7 @@
|
|||
case "delete":
|
||||
elem = svg.getElementById(data.id);
|
||||
if (elem === null) console.error("Eraser: Tried to delete an element that does not exist.");
|
||||
else svg.removeChild(elem);
|
||||
else Tools.drawingArea.removeChild(elem);
|
||||
break;
|
||||
default:
|
||||
console.error("Eraser: 'delete' instruction with unknown type. ", data);
|
||||
|
@ -86,6 +102,7 @@
|
|||
"draw": draw,
|
||||
"icon": "tools/eraser/icon.svg",
|
||||
"mouseCursor": "crosshair",
|
||||
"showMarker": true,
|
||||
});
|
||||
|
||||
})(); //End of code isolation
|
||||
|
|
|
@ -53,7 +53,8 @@
|
|||
"release": release
|
||||
},
|
||||
"icon": "tools/hand/icon.svg",
|
||||
"mouseCursor": "move"
|
||||
"mouseCursor": "move",
|
||||
"showMarker": true,
|
||||
});
|
||||
|
||||
//The hand tool is selected by default
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/**
|
||||
* WHITEBOPHIR
|
||||
*********************************************************
|
||||
* @licstart The following is the entire license notice for the
|
||||
|
@ -111,7 +111,7 @@
|
|||
line.setAttribute("stroke", lineData.color || "black");
|
||||
line.setAttribute("stroke-width", lineData.size || 10);
|
||||
line.setAttribute("opacity", Math.max(0.1, Math.min(1, lineData.opacity)) || 1);
|
||||
svg.appendChild(line);
|
||||
Tools.drawingArea.appendChild(line);
|
||||
return line;
|
||||
}
|
||||
|
||||
|
|
8
client-data/tools/pencil/cursor.svg
Normal file
8
client-data/tools/pencil/cursor.svg
Normal file
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 60 60">
|
||||
<defs/>
|
||||
<g fill="none" stroke="#000" stroke-width="2">
|
||||
<path d="M41.06 33.83c.6-2.8-4.52-5.04-4.65-5.1l-21-23.03L3.18 16.83l21 23.04v.02s1.84 5.48 4.65 5.09a17.85 17.85 0 0012.23-11.14z"/>
|
||||
<path fill="#000" stroke="none" d="M1.18 1.39l.68 5.97 4.97-4.73-5.69-1.26"/>
|
||||
<path d="M32.5 32.38L11.76 9.48M28.33 36.3L7.35 13.39M15.21 5.26L1.1 1.43l1.68 15.22M36.41 28.74l-11.8 11.62h0"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 494 B |
|
@ -1,4 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="60" width="60">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="60" width="60" viewBox="0 0 60 60">
|
||||
<g stroke-width="2" fill="none" stroke="#000">
|
||||
<path d="M34.26 4.96c-2.85.25-3.5 5.8-3.51 5.94L14.88 37.73l14.23 8.43 15.87-26.83H45s4.7-3.37 3.5-5.94a17.85 17.85 0 00-14.23-8.43z"/>
|
||||
<path d="M14.93 52.6l5.51-2.4-5.98-3.36.46 5.8" fill="#000" stroke="none"/>
|
||||
|
|
Before Width: | Height: | Size: 539 B After Width: | Height: | Size: 559 B |
|
@ -80,7 +80,7 @@
|
|||
renderingLine = createLine(data);
|
||||
break;
|
||||
case "child":
|
||||
var line = (renderingLine.id == data.parent) ? renderingLine : svg.getElementById(data.parent);
|
||||
var line = (renderingLine.id === data.parent) ? renderingLine : svg.getElementById(data.parent);
|
||||
if (!line) {
|
||||
console.error("Pencil: Hmmm... I received a point of a line that has not been created (%s).", data.parent);
|
||||
line = renderingLine = createLine({ "id": data.parent }); //create a new line in order not to loose the points
|
||||
|
@ -180,7 +180,7 @@
|
|||
line.setAttribute("stroke", lineData.color || "black");
|
||||
line.setAttribute("stroke-width", lineData.size || 10);
|
||||
line.setAttribute("opacity", Math.max(0.1, Math.min(1, lineData.opacity)) || 1);
|
||||
svg.appendChild(line);
|
||||
Tools.drawingArea.appendChild(line);
|
||||
return line;
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@
|
|||
"release": stopLine,
|
||||
},
|
||||
"draw": draw,
|
||||
"mouseCursor": "crosshair",
|
||||
"mouseCursor": "url('tools/pencil/cursor.svg'), crosshair",
|
||||
"icon": "tools/pencil/icon.svg",
|
||||
"stylesheet": "tools/pencil/pencil.css"
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/**
|
||||
* WHITEBOPHIR
|
||||
*********************************************************
|
||||
* @licstart The following is the entire license notice for the
|
||||
|
@ -115,7 +115,7 @@
|
|||
shape.setAttribute("stroke", data.color || "black");
|
||||
shape.setAttribute("stroke-width", data.size || 10);
|
||||
shape.setAttribute("opacity", Math.max(0.1, Math.min(1, data.opacity)) || 1);
|
||||
svg.appendChild(shape);
|
||||
Tools.drawingArea.appendChild(shape);
|
||||
return shape;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
*/
|
||||
|
||||
(function () { //Code isolation
|
||||
var board = Tools.board, svg = Tools.svg;
|
||||
var board = Tools.board;
|
||||
|
||||
var input = document.createElement("input");
|
||||
input.id = "textToolInput";
|
||||
|
@ -189,7 +189,7 @@
|
|||
elem.setAttribute("fill", fieldData.color);
|
||||
elem.setAttribute("opacity", Math.max(0.1, Math.min(1, fieldData.opacity)) || 1);
|
||||
if (fieldData.txt) elem.textContent = fieldData.txt;
|
||||
svg.appendChild(elem);
|
||||
Tools.drawingArea.appendChild(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
|
|
|
@ -169,6 +169,7 @@
|
|||
"mouseCursor": "zoom-in",
|
||||
"icon": "tools/zoom/icon.svg",
|
||||
"helpText": "click_to_zoom",
|
||||
"showMarker": true,
|
||||
};
|
||||
Tools.add(zoomTool);
|
||||
})(); //End of code isolation
|
||||
|
|
8
server/client_configuration.js
Normal file
8
server/client_configuration.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
const config = require("./configuration");
|
||||
|
||||
/** Settings that should be handed through to the clients */
|
||||
module.exports = {
|
||||
"MAX_BOARD_SIZE": config.MAX_BOARD_SIZE,
|
||||
"MAX_EMIT_COUNT": config.MAX_EMIT_COUNT,
|
||||
"MAX_EMIT_COUNT_PERIOD": config.MAX_EMIT_COUNT_PERIOD,
|
||||
};
|
|
@ -25,4 +25,10 @@ module.exports = {
|
|||
|
||||
/** Maximum value for any x or y on the board */
|
||||
MAX_BOARD_SIZE: parseInt(process.env['WBO_MAX_BOARD_SIZE']) || 65536,
|
||||
|
||||
/** Maximum messages per user over the given time period before banning them */
|
||||
MAX_EMIT_COUNT: parseInt(process.env['WBO_MAX_EMIT_COUNT']) || 128,
|
||||
|
||||
/** Duration after which the emit count is reset in miliseconds */
|
||||
MAX_EMIT_COUNT_PERIOD: parseInt(process.env['WBO_MAX_EMIT_COUNT_PERIOD']) || 4096,
|
||||
};
|
|
@ -1,9 +1,7 @@
|
|||
var iolib = require('socket.io')
|
||||
, log = require("./log.js").log
|
||||
, BoardData = require("./boardData.js").BoardData;
|
||||
|
||||
var MAX_EMIT_COUNT = 64; // Maximum number of draw operations before getting banned
|
||||
var MAX_EMIT_COUNT_PERIOD = 5000; // Duration (in ms) after which the emit count is reset
|
||||
, BoardData = require("./boardData.js").BoardData
|
||||
, config = require("./configuration");
|
||||
|
||||
/** Map from name to *promises* of BoardData
|
||||
@type {Object<string, Promise<BoardData>>}
|
||||
|
@ -66,19 +64,21 @@ function socketConnection(socket) {
|
|||
|
||||
socket.on("joinboard", noFail(joinBoard));
|
||||
|
||||
var lastEmitSecond = Date.now() / MAX_EMIT_COUNT_PERIOD | 0;
|
||||
var lastEmitSecond = Date.now() / config.MAX_EMIT_COUNT_PERIOD | 0;
|
||||
var emitCount = 0;
|
||||
socket.on('broadcast', noFail(function onBroadcast(message) {
|
||||
var currentSecond = Date.now() / MAX_EMIT_COUNT_PERIOD | 0;
|
||||
var currentSecond = Date.now() / config.MAX_EMIT_COUNT_PERIOD | 0;
|
||||
if (currentSecond === lastEmitSecond) {
|
||||
emitCount++;
|
||||
if (emitCount > MAX_EMIT_COUNT) {
|
||||
if (emitCount > config.MAX_EMIT_COUNT) {
|
||||
var request = socket.client.request;
|
||||
log('BANNED', {
|
||||
user_agent: request.headers['user-agent'],
|
||||
original_ip: request.headers['x-forwarded-for'] || request.headers['forwarded'],
|
||||
emit_count: emitCount
|
||||
});
|
||||
if (emitCount % 100 === 0) {
|
||||
log('BANNED', {
|
||||
user_agent: request.headers['user-agent'],
|
||||
original_ip: request.headers['x-forwarded-for'] || request.headers['forwarded'],
|
||||
emit_count: emitCount
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
@ -96,11 +96,12 @@ function socketConnection(socket) {
|
|||
return;
|
||||
}
|
||||
|
||||
//Send data to all other users connected on the same board
|
||||
socket.broadcast.to(boardName).emit('broadcast', data);
|
||||
|
||||
// Save the message in the board
|
||||
saveHistory(boardName, data);
|
||||
handleMessage(boardName, data, socket);
|
||||
|
||||
//Send data to all other users connected on the same board
|
||||
socket.broadcast.to(boardName).emit('broadcast', data);
|
||||
}));
|
||||
|
||||
socket.on('disconnecting', function onDisconnecting(reason) {
|
||||
|
@ -119,6 +120,14 @@ function socketConnection(socket) {
|
|||
});
|
||||
}
|
||||
|
||||
function handleMessage(boardName, message, socket) {
|
||||
if (message.tool === "Cursor") {
|
||||
message.socket = socket.id;
|
||||
} else {
|
||||
saveHistory(boardName, message);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveHistory(boardName, message) {
|
||||
var id = message.id;
|
||||
var board = await getBoard(boardName);
|
||||
|
|
|
@ -3,6 +3,7 @@ const fs = require("fs");
|
|||
const path = require("path");
|
||||
const url = require("url");
|
||||
const accept_language_parser = require('accept-language-parser');
|
||||
const client_config = require("./client_configuration");
|
||||
|
||||
/**
|
||||
* Associations from language to translation dictionnaries
|
||||
|
@ -31,9 +32,10 @@ class Template {
|
|||
const accept_languages = parsedUrl.query.lang || request.headers['accept-language'];
|
||||
const language = accept_language_parser.pick(languages, accept_languages) || 'en';
|
||||
const translations = TRANSLATIONS[language] || {};
|
||||
const configuration = client_config || {};
|
||||
const prefix = request.url.split("/boards/")[0].substr(1);
|
||||
const baseUrl = findBaseUrl(request) + (prefix ? prefix + "/" : "");
|
||||
return { baseUrl, languages, language, translations };
|
||||
return { baseUrl, languages, language, translations, configuration };
|
||||
}
|
||||
serve(request, response) {
|
||||
const parsedUrl = url.parse(request.url, true);
|
||||
|
|
|
@ -18,10 +18,8 @@ async function afterEach(browser, done) {
|
|||
done();
|
||||
}
|
||||
|
||||
function testBoard(browser) {
|
||||
browser
|
||||
.url('http://localhost:8487/boards/anonymous?lang=fr')
|
||||
.waitForElementVisible('.tool[title ~= Crayon]') // pencil
|
||||
function testPencil(browser) {
|
||||
return browser
|
||||
.assert.titleContains('WBO')
|
||||
.click('.tool[title ~= Crayon]')
|
||||
.assert.cssClassPresent('.tool[title ~= Crayon]', ['curTool'])
|
||||
|
@ -36,7 +34,28 @@ function testBoard(browser) {
|
|||
.assert.visible("path[d='M 100 200 C 100 200 300 400 300 400'][stroke='#123456']")
|
||||
.refresh()
|
||||
.assert.visible("path[d='M 100 200 C 100 200 300 400 300 400'][stroke='#123456']")
|
||||
.end();
|
||||
}
|
||||
|
||||
|
||||
function testCursor(browser) {
|
||||
return browser
|
||||
.execute(function (done) {
|
||||
Tools.setColor('#456123'); // Move the cursor over the board
|
||||
var e = new Event("mousemove")
|
||||
e.pageX = 150
|
||||
e.pageY = 200
|
||||
Tools.board.dispatchEvent(e)
|
||||
})
|
||||
.assert.cssProperty("#cursor-me", "transform", "matrix(1, 0, 0, 1, 150, 200)")
|
||||
.assert.attributeEquals("#cursor-me", "fill", "#456123")
|
||||
}
|
||||
|
||||
function testBoard(browser) {
|
||||
var page = browser.url('http://localhost:8487/boards/anonymous?lang=fr')
|
||||
.waitForElementVisible('.tool[title ~= Crayon]') // pencil
|
||||
page = testPencil(page);
|
||||
page = testCursor(page);
|
||||
page.end();
|
||||
}
|
||||
|
||||
module.exports = { beforeEach, testBoard, afterEach };
|
Loading…
Reference in a new issue