From e2ddcbf1628f9db88dcd7ca61eff430178248d7a Mon Sep 17 00:00:00 2001 From: Cracker Date: Wed, 6 May 2020 18:00:40 +0200 Subject: [PATCH 01/52] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 32877f3..45a8876 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Drop object | Ctrl + Enter | Command + Enter Add Image to backgroud | Shift + Enter | Shift + Enter Cancel all actions | Escape | Escape Delete selected object | Delete | Delete +Use Line tool when pen is active (Not changeable) | Shift (Hold) | Shift (Hold) ## URL Parameters Call your site with GET parameters to change the WhiteboardID or the Username From 6f68f9f21f76abc5a143b71e9114020fbccd138f Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Sat, 2 May 2020 12:51:17 +0200 Subject: [PATCH 02/52] feat(backend): no compression & volatile * Should enable for more performances when there are a lot of users * Messages are pretty small so compression might not be needed * Volatile reduces the number of exchanges between the server and the clients --- scripts/server-backend.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/server-backend.js b/scripts/server-backend.js index 6250068..ce2ad14 100644 --- a/scripts/server-backend.js +++ b/scripts/server-backend.js @@ -202,14 +202,14 @@ function startBackendServer(port) { if (smallestScreenResolutions && smallestScreenResolutions[whiteboardId] && socket && socket.id) { delete smallestScreenResolutions[whiteboardId][socket.id]; } - socket.broadcast.emit('refreshUserBadges', null); //Removes old user Badges + socket.compress(false).volatile.broadcast.emit('refreshUserBadges', null); //Removes old user Badges sendSmallestScreenResolution(); }); socket.on('drawToWhiteboard', function (content) { content = escapeAllContentStrings(content); if (accessToken === "" || accessToken == content["at"]) { - socket.broadcast.to(whiteboardId).emit('drawToWhiteboard', content); //Send to all users in the room (not own socket) + socket.compress(false).volatile.broadcast.to(whiteboardId).emit('drawToWhiteboard', content); //Send to all users in the room (not own socket) s_whiteboard.handleEventsAndData(content); //save whiteboardchanges on the server } else { socket.emit('wrongAccessToken', true); From db89dd819bb6461835fa141834330415953ff010 Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Sat, 2 May 2020 12:59:03 +0200 Subject: [PATCH 03/52] feat(frontend): track the number of messages received / sent --- src/index.html | 4 ++++ src/js/main.js | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/index.html b/src/index.html index 0e298f8..fe75bc7 100644 --- a/src/index.html +++ b/src/index.html @@ -124,6 +124,10 @@ +
+

# msg. sent to server: 0

+

# msg. received from server: 0

+
\ No newline at end of file diff --git a/src/js/main.js b/src/js/main.js index 8553eea..9d693e8 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -43,8 +43,10 @@ function main() { signaling_socket.on('connect', function () { console.log("Websocket connected!"); + let messageReceivedCount = 0; signaling_socket.on('drawToWhiteboard', function (content) { whiteboard.handleEventsAndData(content, true); + $('#messageReceivedCount')[0].innerText = String(messageReceivedCount++); }); signaling_socket.on('refreshUserBadges', function () { @@ -69,12 +71,15 @@ function main() { if (getQueryVariable("webdav") == "true") { $("#uploadWebDavBtn").show(); } + + let messageSentCount = 0; whiteboard.loadWhiteboard("#whiteboardContainer", { //Load the whiteboard whiteboardId: whiteboardId, username: btoa(myUsername), sendFunction: function (content) { content["at"] = accessToken; signaling_socket.emit('drawToWhiteboard', content); + $('#messageSentCount')[0].innerText = String(messageSentCount++); } }); From 8a1f1b42101fdac810b096122a6fd43cc1ab45c8 Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Sat, 2 May 2020 13:02:16 +0200 Subject: [PATCH 04/52] feat(frontend): reduce the number of cursor messages sent * require a minimum position / time variation between emitions --- src/js/whiteboard.js | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/js/whiteboard.js b/src/js/whiteboard.js index 735a516..4942af1 100644 --- a/src/js/whiteboard.js +++ b/src/js/whiteboard.js @@ -39,6 +39,9 @@ const whiteboard = { sendFunction: null, backgroundGridUrl: './images/KtEBa2.png' }, + lastPointerSentTime: 0, + lastPointerX: 0, + lastPointerY: 0, loadWhiteboard: function (whiteboardContainer, newSettings) { var svgns = "http://www.w3.org/2000/svg"; var _this = this; @@ -157,9 +160,19 @@ const whiteboard = { return; } - _this.currX = (e.offsetX || e.pageX - $(e.target).offset().left); - _this.currY = (e.offsetY || e.pageY - $(e.target).offset().top); - _this.sendFunction({ "t": "cursor", "event": "move", "d": [_this.currX, _this.currY], "username": _this.settings.username }); + var currX = (e.offsetX || e.pageX - $(e.target).offset().left); + var currY = (e.offsetY || e.pageY - $(e.target).offset().top); + + var pointerSentTime = (new Date()).getTime(); + if (pointerSentTime - _this.lastPointerSentTime > 100) { + var dist = Math.pow(_this.lastPointerX-currX,2)+Math.pow(_this.lastPointerY-currY,2); + if (dist>100) { + _this.lastPointerSentTime = pointerSentTime; + _this.lastPointerX = currX; + _this.lastPointerY = currY; + _this.sendFunction({ "t": "cursor", "event": "move", "d": [currX, currY], "username": _this.settings.username }); + } + } }) _this.mouseOverlay.on("mousemove touchmove", function (e) { @@ -384,7 +397,16 @@ const whiteboard = { _this.prevX = currX; _this.prevY = currY; }); + var pointerSentTime = (new Date()).getTime(); + if (pointerSentTime - _this.lastPointerSentTime > 100) { + var dist = Math.pow(_this.lastPointerX-currX,2)+Math.pow(_this.lastPointerY-currY,2); + if (dist>100) { + _this.lastPointerSentTime = pointerSentTime; + _this.lastPointerX = currX; + _this.lastPointerY = currY; _this.sendFunction({ "t": "cursor", "event": "move", "d": [currX, currY], "username": _this.settings.username }); + } + } }, triggerMouseOver: function () { var _this = this; @@ -638,7 +660,16 @@ const whiteboard = { if ($(e.target).hasClass("removeIcon")) { currX += textBox.width() - 4; } + var pointerSentTime = (new Date()).getTime(); + if (pointerSentTime - _this.lastPointerSentTime > 100) { + var dist = Math.pow(_this.lastPointerX-currX,2)+Math.pow(_this.lastPointerY-currY,2); + if (dist>100) { + _this.lastPointerSentTime = pointerSentTime; + _this.lastPointerX = currX; + _this.lastPointerY = currY; _this.sendFunction({ "t": "cursor", "event": "move", "d": [currX, currY], "username": _this.settings.username }); + } + } }) this.textContainer.append(textBox); textBox.draggable({ From b0501824cfecc64522a9fef30ec7b52fd3ec588a Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Sat, 2 May 2020 13:05:04 +0200 Subject: [PATCH 05/52] feat(frontend): default to mouse and don't emit cursor while drawing --- src/js/main.js | 6 ++++++ src/js/whiteboard.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/js/main.js b/src/js/main.js index 9d693e8..ab9829b 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -77,6 +77,9 @@ function main() { whiteboardId: whiteboardId, username: btoa(myUsername), sendFunction: function (content) { + if (content.t === 'cursor') { + if (whiteboard.drawFlag) return; + } content["at"] = accessToken; signaling_socket.emit('drawToWhiteboard', content); $('#messageSentCount')[0].innerText = String(messageSentCount++); @@ -631,6 +634,9 @@ function main() { whiteboard.setDrawColor(color.rgbaString); } }); + + // on startup select mouse + shortcutFunctions.setTool_mouse(); }); //Prevent site from changing tab on drag&drop diff --git a/src/js/whiteboard.js b/src/js/whiteboard.js index 4942af1..9a40f5c 100644 --- a/src/js/whiteboard.js +++ b/src/js/whiteboard.js @@ -4,7 +4,7 @@ const whiteboard = { canvas: null, ctx: null, drawcolor: "black", - tool: "pen", + tool: "mouse", thickness: 4, prevX: null, //prev Mouse position prevY: null, From 8117be3f52d06ddf4b9e9dfcdea89d7ec65402c6 Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Sat, 2 May 2020 13:09:36 +0200 Subject: [PATCH 06/52] feat(frontend): add read-only mode and default to it --- src/index.html | 18 ++++++++++++------ src/js/icons.js | 6 +++++- src/js/main.js | 21 +++++++++++++++++++++ src/js/whiteboard.js | 36 ++++++++++++++++++++++++++++++++---- 4 files changed, 70 insertions(+), 11 deletions(-) diff --git a/src/index.html b/src/index.html index fe75bc7..5ee8c33 100644 --- a/src/index.html +++ b/src/index.html @@ -14,22 +14,28 @@
- - -
+ + @@ -96,14 +102,14 @@
-
'); } }, + setViewOnly: function(what) { + var _this = this; + _this.viewOnly = what; + + if (what === true){ + $(".whiteboardTool[tool=mouse]").click(); // reset tool to prevent use of text + $(".whiteboardTool").prop("disabled", true); + $(".whiteboardEditBtn").prop("disabled", true); + $("#whiteboardUnlockBtn").hide(); + $("#whiteboardLockBtn").show(); + + } else { + $(".whiteboardTool").prop("disabled", false); + $(".whiteboardEditBtn").prop("disabled", false); + $("#whiteboardUnlockBtn").show(); + $("#whiteboardLockBtn").hide(); + } + }, handleEventsAndData: function (content, isNewData, doneCallback) { var _this = this; var tool = content["t"]; @@ -1029,4 +1057,4 @@ function lanczosInterpolate(xm1, ym1, x0, y0, x1, y1, x2, y2, a) { return [cm1 * xm1 + c0 * x0 + c1 * x1 + c2 * x2, cm1 * ym1 + c0 * y0 + c1 * y1 + c2 * y2]; } -export default whiteboard; \ No newline at end of file +export default whiteboard; From c454a23e717003952f871d1a3387e874130c3a95 Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Sat, 2 May 2020 14:29:58 +0200 Subject: [PATCH 07/52] fix(css): more straightforward handling of borders * don't rely on last child selector that might be incorrect due to display:none elements --- src/css/main.css | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/css/main.css b/src/css/main.css index d62a19d..f9493fd 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -13,7 +13,8 @@ body { .btn-group button { background: transparent; - border: 1px solid #636060; + border: 2px solid #636060; + margin: -1px; /* Green border */ color: black; /* White text */ @@ -25,16 +26,13 @@ body { /* Float the buttons side by side */ font-size: 1.2em; height: 45px; + width: 50px; } button::-moz-focus-inner { border: 0; } -.btn-group button:not(:last-child) { - border-right: none; - /* Prevent double borders */ -} /* Clear floats (clearfix hack) */ @@ -57,6 +55,7 @@ button { .btn-group { background-color: #808080ab; margin-left: 5px; + margin-bottom: 5px; float: left; position: relative; } From 7ec4ef0df06446e229f76ad08090f28ee4a56ed6 Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Sat, 2 May 2020 15:07:34 +0200 Subject: [PATCH 08/52] feat(frontend): cleaned read-only & style * cleaned unsued css classes * Reorganized buttons based on if they are edit related or not * meaningful css when locking for better ui experience * renamed viewOnly to more standard readOnly --- src/css/main.css | 24 +++++++++++-- src/index.html | 83 ++++++++++++++++++++++---------------------- src/js/main.js | 52 +++++++++++++-------------- src/js/whiteboard.js | 42 ++++++++++++---------- 4 files changed, 113 insertions(+), 88 deletions(-) diff --git a/src/css/main.css b/src/css/main.css index f9493fd..9ea45c6 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -33,6 +33,24 @@ button::-moz-focus-inner { border: 0; } +.whiteboard-edit-group.group-disabled { + background: repeating-linear-gradient( + 45deg, + rgba(255, 166, 0, 0.366) , + rgba(255, 166, 0, 0.366) 10px, + rgba(255, 166, 0, 0.666) 10px, + rgba(255, 166, 0, 0.666) 20px + ); + +} + +/* + * Deactivate all pointer events on all the children + * of a group when it's disabled. + */ +.whiteboard-edit-group.group-disabled > * { + pointer-events: none; +} /* Clear floats (clearfix hack) */ @@ -60,8 +78,8 @@ button { position: relative; } -.whiteboardTool.active { - background: #bfbfbf; +.whiteboard-tool.active:not(:disabled) { + background: #dfdfdf; } #whiteboardThicknessSlider { @@ -94,4 +112,4 @@ button { border: 0px; min-width: 50px; cursor: pointer; -} \ No newline at end of file +} diff --git a/src/index.html b/src/index.html index 5ee8c33..5c6031f 100644 --- a/src/index.html +++ b/src/index.html @@ -14,57 +14,60 @@
- + +
+ +
+ - -
-
- - - - - - - -
-
+
+
+ + + + + +
+
- - -
- -
- - - - ' + - '' + - '
' + - '
'); - var dragCanvas = $(imgDiv).find("canvas"); - var dragOutOverlay = $('
'); + const imgDiv = $(```
+ +
+ + +
+
```); + const dragCanvas = $(imgDiv).find("canvas"); + const dragOutOverlay = $(`
`); _this.mouseOverlay.append(dragOutOverlay); _this.mouseOverlay.append(imgDiv); - var destCanvasContext = dragCanvas[0].getContext('2d'); + const destCanvasContext = dragCanvas[0].getContext('2d'); destCanvasContext.drawImage(_this.canvas, left, top, width, height, 0, 0, width, height); imgDiv.find(".xCanvasBtn").click(function () { _this.imgDragActive = false; @@ -279,9 +269,9 @@ const whiteboard = { imgDiv.find(".addToCanvasBtn").click(function () { _this.imgDragActive = false; _this.refreshCursorAppearance(); - var p = imgDiv.position(); - var leftT = Math.round(p.left * 100) / 100; - var topT = Math.round(p.top * 100) / 100; + const p = imgDiv.position(); + const leftT = Math.round(p.left * 100) / 100; + const topT = Math.round(p.top * 100) / 100; _this.drawId++; _this.sendFunction({ "t": _this.tool, "d": [left, top, leftT, topT, width, height] }); _this.dragCanvasRectContent(left, top, leftT, topT, width, height); @@ -303,93 +293,96 @@ const whiteboard = { _this.triggerMouseOver(); }); - //On textcontainer click (Add a new textbox) + // On text container click (Add a new textbox) _this.textContainer.on("click", function (e) { - var currX = (e.offsetX || e.pageX - $(e.target).offset().left); - var currY = (e.offsetY || e.pageY - $(e.target).offset().top); - var fontsize = _this.thickness * 0.5; - var txId = 'tx' + (+new Date()); - _this.sendFunction({ "t": "addTextBox", "d": [_this.drawcolor, fontsize, currX, currY, txId] }); - _this.addTextBox(_this.drawcolor, fontsize, currX, currY, txId, true); + const currentPos = Point.fromEvent(e); + const fontsize = _this.thickness * 0.5; + const txId = 'tx' + (+new Date()); + _this.sendFunction({ "t": "addTextBox", "d": [_this.drawcolor, fontsize, currentPos.x, currentPos.y, txId] }); + _this.addTextBox(_this.drawcolor, fontsize, currentPos.x, currentPos.y, txId, true); }); }, - getRoundedAngles: function (currX, currY) { //For drawing lines at 0,45,90° .... - var _this = this; - var x = currX - _this.startCoords[0]; - var y = currY - _this.startCoords[1]; - var angle = Math.atan2(x, y) * (180 / Math.PI); - var angle45 = Math.round(angle / 45) * 45; - if (angle45 % 90 == 0) { - if (Math.abs(currX - _this.startCoords[0]) > Math.abs(currY - _this.startCoords[1])) { - currY = _this.startCoords[1] + /** + * For drawing lines at 0,45,90° .... + * @param {Point} currentPos + * @returns {Point} + */ + getRoundedAngles: function (currentPos) { + const _this = this; + const x = currentPos.x - _this.startCoords.x; + const y = currentPos.y - _this.startCoords.y; + const angle = Math.atan2(x, y) * (180 / Math.PI); + const angle45 = Math.round(angle / 45) * 45; + + let outX = currentPos.x; + let outY = currentPos.y; + if (angle45 % 90 === 0) { + if (Math.abs(currentPos.x - _this.startCoords.x) > Math.abs(currentPos.y - _this.startCoords.y)) { + outY = _this.startCoords.y } else { - currX = _this.startCoords[0] + outX = _this.startCoords.x } } else { - if ((currY - _this.startCoords[1]) * (currX - _this.startCoords[0]) > 0) { - currX = _this.startCoords[0] + (currY - _this.startCoords[1]); + if ((currentPos.y - _this.startCoords.y) * (currentPos.x - _this.startCoords.x) > 0) { + outX = _this.startCoords.x + (currentPos.y - _this.startCoords.y); } else { - currX = _this.startCoords[0] - (currY - _this.startCoords[1]); + outY = _this.startCoords.x - (currentPos.y - _this.startCoords.y); } } - return { "x": currX, "y": currY }; + + return new Point(outX, outY); }, triggerMouseMove: function (e) { - var _this = this; + const _this = this; if (_this.imgDragActive) { return; } - var currX = e.currX || (e.offsetX || e.pageX - $(e.target).offset().left); - var currY = e.currY || (e.offsetY || e.pageY - $(e.target).offset().top); + + let currentPos = Point.fromEvent(e); window.requestAnimationFrame(function () { - if ((!currX || !currY) && e.touches && e.touches[0]) { - var touche = e.touches[0]; - currX = touche.clientX - $(_this.mouseOverlay).offset().left; - currY = touche.clientY - $(_this.mouseOverlay).offset().top; - } - _this.latestTouchCoods = [currX, currY]; + // update position + currentPos = Point.fromEvent(e); if (_this.drawFlag) { if (_this.tool === "pen") { - _this.pushPointSmoothPen(currX, currY); - } else if (_this.tool == "eraser") { - _this.drawEraserLine(currX, currY, _this.prevX, _this.prevY, _this.thickness); - _this.sendFunction({ "t": _this.tool, "d": [currX, currY, _this.prevX, _this.prevY], "th": _this.thickness }); + _this.pushPointSmoothPen(currentPos.x, currentPos.y); + } else if (_this.tool === "eraser") { + _this.drawEraserLine(currentPos.x, currentPos.y, _this.prevPos.x, _this.prevPos.y, _this.thickness); + _this.sendFunction({ "t": _this.tool, "d": [currentPos.x, currentPos.y, _this.prevPos.x, _this.prevPos.y], "th": _this.thickness }); } } if (_this.tool === "eraser") { - var left = currX - _this.thickness; - var top = currY - _this.thickness; + const left = currentPos.x - _this.thickness; + const top = currentPos.y - _this.thickness; if (_this.ownCursor) _this.ownCursor.css({ "top": top + "px", "left": left + "px" }); } else if (_this.tool === "pen") { - var left = currX - _this.thickness / 2; - var top = currY - _this.thickness / 2; + const left = currentPos.x - _this.thickness / 2; + const top = currentPos.y - _this.thickness / 2; if (_this.ownCursor) _this.ownCursor.css({ "top": top + "px", "left": left + "px" }); } else if (_this.tool === "line") { if (_this.svgLine) { + let posToUse = currentPos; if (_this.pressedKeys.shift) { - var angs = _this.getRoundedAngles(currX, currY); - currX = angs.x; - currY = angs.y; + posToUse = _this.getRoundedAngles(currentPos); } - _this.svgLine.setAttribute('x2', currX); - _this.svgLine.setAttribute('y2', currY); + _this.svgLine.setAttribute('x2', posToUse.x); + _this.svgLine.setAttribute('y2', posToUse.y); } } else if (_this.tool === "rect" || (_this.tool === "recSelect" && _this.drawFlag)) { if (_this.svgRect) { - var width = Math.abs(currX - _this.startCoords[0]); - var height = Math.abs(currY - _this.startCoords[1]); + const width = Math.abs(currentPos.x - _this.startCoords.x); + let height = Math.abs(currentPos.y - _this.startCoords.y); if (_this.pressedKeys.shift) { height = width; - var x = currX < _this.startCoords[0] ? _this.startCoords[0] - width : _this.startCoords[0]; - var y = currY < _this.startCoords[1] ? _this.startCoords[1] - width : _this.startCoords[1]; + const x = currentPos.x < _this.startCoords.x ? _this.startCoords.x - width : _this.startCoords.x; + const y = currentPos.y < _this.startCoords.y ? _this.startCoords.y - width : _this.startCoords.y; _this.svgRect.setAttribute('x', x); _this.svgRect.setAttribute('y', y); } else { - var x = currX < _this.startCoords[0] ? currX : _this.startCoords[0]; - var y = currY < _this.startCoords[1] ? currY : _this.startCoords[1]; + const x = currentPos.x < _this.startCoords.x ? currentPos.x : _this.startCoords.x; + const y = currentPos.y < _this.startCoords.y ? currentPos.y : _this.startCoords.y; _this.svgRect.setAttribute('x', x); _this.svgRect.setAttribute('y', y); } @@ -398,20 +391,18 @@ const whiteboard = { _this.svgRect.setAttribute('height', height); } } else if (_this.tool === "circle") { - var a = currX - _this.startCoords[0]; - var b = currY - _this.startCoords[1]; - var r = Math.sqrt(a * a + b * b); + const r = currentPos.distTo(_this.startCoords); if (_this.svgCirle) { _this.svgCirle.setAttribute('r', r); } } - _this.prevX = currX; - _this.prevY = currY; + + _this.prevPos = currentPos; }); const pointerSentTime = getCurrentTimeMs(); if (pointerSentTime - _this.lastPointerSentTime > POINTER_EVENT_THRESHOLD_MIN_TIME_DELTA) { - const newPointerPosition = new Point(currX, currY); + const newPointerPosition = currentPos; if (_this.lastPointerPosition.distTo(newPointerPosition) > POINTER_EVENT_THRESHOLD_MIN_DIST_DELTA) { _this.lastPointerSentTime = pointerSentTime; _this.lastPointerPosition = newPointerPosition; @@ -453,10 +444,10 @@ const whiteboard = { _this.sendFunction({ "t": "cursor", "event": "out" }); }, redrawMouseCursor: function () { - var _this = this; + const _this = this; _this.triggerMouseOut(); _this.triggerMouseOver(); - _this.triggerMouseMove({ currX: whiteboard.prevX, currY: whiteboard.prevY }); + _this.triggerMouseMove({ offsetX: _this.prevPos.x, offsetY: _this.prevPos.y }); }, delKeyAction: function () { var _this = this; From 3dd889b80032fb853e24e689ce2308c4931d257e Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Wed, 6 May 2020 22:12:56 +0200 Subject: [PATCH 14/52] fix: restore upstream master changes --- src/js/main.js | 6 ++---- src/js/whiteboard.js | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/js/main.js b/src/js/main.js index 96164b4..695c02a 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -114,11 +114,9 @@ function main() { tempLineTool = true; whiteboard.ownCursor.hide(); if (whiteboard.drawFlag) { - whiteboard.mouseup({ offsetX: whiteboard.currX, offsetY: whiteboard.currY }) + whiteboard.mouseup({ offsetX: whiteboard.prevPos.x, offsetY: whiteboard.prevPos.y }) shortcutFunctions.setTool_line(); - whiteboard.prevX = whiteboard.currX; - whiteboard.prevY = whiteboard.currY; - whiteboard.mousedown({ offsetX: whiteboard.currX, offsetY: whiteboard.currY }) + whiteboard.mousedown({ offsetX: whiteboard.prevPos.x, offsetY: whiteboard.prevPos.y }) } else { shortcutFunctions.setTool_line(); } diff --git a/src/js/whiteboard.js b/src/js/whiteboard.js index 1ba3f0d..2ef79f1 100644 --- a/src/js/whiteboard.js +++ b/src/js/whiteboard.js @@ -212,9 +212,9 @@ const whiteboard = { _this.sendFunction({ "t": _this.tool, "d": [currentPos.x, currentPos.y, _this.startCoords.x, _this.startCoords.y], "c": _this.drawcolor, "th": _this.thickness }); _this.svgContainer.find("line").remove(); } else if (_this.tool === "pen") { - _this.drawId--; + _this.drawId--; _this.pushPointSmoothPen(currentPos.x, currentPos.y); - _this.drawId++; + _this.drawId++; } else if (_this.tool === "rect") { if (_this.pressedKeys.shift) { if ((currentPos.x - _this.startCoords.x) * (currentPos.y - _this.startCoords.y) > 0) { From c93d3e643aaabcfbea3b1b663f26684936fc2f94 Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Wed, 6 May 2020 22:21:09 +0200 Subject: [PATCH 15/52] feat(front): deactivate readOnly in dev by default --- src/js/main.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/js/main.js b/src/js/main.js index 695c02a..224bec3 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -70,7 +70,7 @@ function main() { }); $(document).ready(function () { - // start in readOnly mode + // by default set in readOnly mode ReadOnlyService.activateReadOnlyMode(); if (getQueryVariable("webdav") == "true") { @@ -539,6 +539,12 @@ function main() { shortcutFunctions.setTool_mouse(); // fix bug cursor not showing up whiteboard.refreshCursorAppearance(); + + if (process.env.NODE_ENV === "production") { + ReadOnlyService.activateReadOnlyMode(); + } else { + ReadOnlyService.deactivateReadOnlyMode(); + } }); //Prevent site from changing tab on drag&drop From 677f3d95a129daaf2f8031b87d6ca59482dd059a Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Wed, 6 May 2020 23:13:44 +0200 Subject: [PATCH 16/52] fix(front): definite fix to getRoundedAngles * Switched to pure math transformation --- src/js/whiteboard.js | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/js/whiteboard.js b/src/js/whiteboard.js index 2ef79f1..cad9701 100644 --- a/src/js/whiteboard.js +++ b/src/js/whiteboard.js @@ -4,6 +4,10 @@ import Point from "./classes/Point"; import {POINTER_EVENT_THRESHOLD_MIN_DIST_DELTA, POINTER_EVENT_THRESHOLD_MIN_TIME_DELTA} from "./const"; import ReadOnlyService from "./services/ReadOnlyService"; +const RAD_TO_DEG = 180.0 / Math.PI; +const DEG_TO_RAD = Math.PI / 180.0; +const _45_DEG_IN_RAD = 45 * DEG_TO_RAD; + const whiteboard = { canvas: null, ctx: null, @@ -308,27 +312,19 @@ const whiteboard = { * @returns {Point} */ getRoundedAngles: function (currentPos) { - const _this = this; - const x = currentPos.x - _this.startCoords.x; - const y = currentPos.y - _this.startCoords.y; - const angle = Math.atan2(x, y) * (180 / Math.PI); - const angle45 = Math.round(angle / 45) * 45; + const {startCoords} = this; - let outX = currentPos.x; - let outY = currentPos.y; - if (angle45 % 90 === 0) { - if (Math.abs(currentPos.x - _this.startCoords.x) > Math.abs(currentPos.y - _this.startCoords.y)) { - outY = _this.startCoords.y - } else { - outX = _this.startCoords.x - } - } else { - if ((currentPos.y - _this.startCoords.y) * (currentPos.x - _this.startCoords.x) > 0) { - outX = _this.startCoords.x + (currentPos.y - _this.startCoords.y); - } else { - outY = _this.startCoords.x - (currentPos.y - _this.startCoords.y); - } - } + // these transformations operate in the standard coordinate system + // y goes from bottom to up, x goes left to right + const dx = currentPos.x - startCoords.x; // browser x is reversed + const dy = startCoords.y - currentPos.y; + + const angle = Math.atan2(dy, dx); + const angle45 = Math.round(angle / _45_DEG_IN_RAD) * _45_DEG_IN_RAD; + + const dist = currentPos.distTo(startCoords); + let outX = startCoords.x + dist * Math.cos(angle45); + let outY = startCoords.y - dist * Math.sin(angle45); return new Point(outX, outY); }, From e3cec1a194c3a216dd8c52de21c3f47b1d53656e Mon Sep 17 00:00:00 2001 From: raphael Date: Sat, 9 May 2020 15:06:21 +0200 Subject: [PATCH 17/52] remove volatile because problems even on local connections (not drawn lines) --- scripts/server-backend.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/server-backend.js b/scripts/server-backend.js index ce2ad14..1877d27 100644 --- a/scripts/server-backend.js +++ b/scripts/server-backend.js @@ -202,14 +202,14 @@ function startBackendServer(port) { if (smallestScreenResolutions && smallestScreenResolutions[whiteboardId] && socket && socket.id) { delete smallestScreenResolutions[whiteboardId][socket.id]; } - socket.compress(false).volatile.broadcast.emit('refreshUserBadges', null); //Removes old user Badges + socket.compress(false).broadcast.emit('refreshUserBadges', null); //Removes old user Badges sendSmallestScreenResolution(); }); socket.on('drawToWhiteboard', function (content) { content = escapeAllContentStrings(content); if (accessToken === "" || accessToken == content["at"]) { - socket.compress(false).volatile.broadcast.to(whiteboardId).emit('drawToWhiteboard', content); //Send to all users in the room (not own socket) + socket.compress(false).broadcast.to(whiteboardId).emit('drawToWhiteboard', content); //Send to all users in the room (not own socket) s_whiteboard.handleEventsAndData(content); //save whiteboardchanges on the server } else { socket.emit('wrongAccessToken', true); From 684a7d80e01dcd008fa8fd1a6970bd52a9672192 Mon Sep 17 00:00:00 2001 From: raphael Date: Sat, 9 May 2020 15:07:54 +0200 Subject: [PATCH 18/52] make mouse movement smooth as default --- src/js/const.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/const.js b/src/js/const.js index 2b6ca19..9645fb8 100644 --- a/src/js/const.js +++ b/src/js/const.js @@ -1,2 +1,2 @@ -export const POINTER_EVENT_THRESHOLD_MIN_DIST_DELTA = 10; // 10px -export const POINTER_EVENT_THRESHOLD_MIN_TIME_DELTA = 100; // 100ms +export const POINTER_EVENT_THRESHOLD_MIN_DIST_DELTA = 1; // 1px +export const POINTER_EVENT_THRESHOLD_MIN_TIME_DELTA = 10; // 1ms From 9f9fca40b7e7e726f09403942d82ea2dbe475993 Mon Sep 17 00:00:00 2001 From: raphael Date: Sat, 9 May 2020 15:08:49 +0200 Subject: [PATCH 19/52] transmit cursor movement while drawing --- src/js/main.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/js/main.js b/src/js/main.js index 224bec3..7fa022c 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -83,9 +83,10 @@ function main() { username: btoa(myUsername), sendFunction: function (content) { if (ReadOnlyService.readOnlyActive) return; - if (content.t === 'cursor') { - if (whiteboard.drawFlag) return; - } + //ADD IN LATER THROUGH CONFIG + // if (content.t === 'cursor') { + // if (whiteboard.drawFlag) return; + // } content["at"] = accessToken; signaling_socket.emit('drawToWhiteboard', content); $('#messageSentCount')[0].innerText = String(messageSentCount++); From 4bd2ebacaf994a8b09c24bb71265197a393ddf10 Mon Sep 17 00:00:00 2001 From: raphael Date: Sat, 9 May 2020 15:09:54 +0200 Subject: [PATCH 20/52] hide log data --- src/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.html b/src/index.html index 5c6031f..9ef0286 100644 --- a/src/index.html +++ b/src/index.html @@ -131,7 +131,7 @@
-
+

# msg. sent to server: 0

# msg. received from server: 0

From 2cf6c78003a1d0769ce80378688ef40bf5a6110b Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Thu, 7 May 2020 21:34:43 +0200 Subject: [PATCH 21/52] feat: added prettier, pretty-quick & husky dependencies --- package-lock.json | 521 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 + 2 files changed, 524 insertions(+) diff --git a/package-lock.json b/package-lock.json index 12d94db..26d4ec3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1069,6 +1069,12 @@ "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "dev": true }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -1104,6 +1110,12 @@ "integrity": "sha512-WE4IOAC6r/yBZss1oQGM5zs2D7RuKR6Q+w+X2SouPofnWn+LbCqClRyhO3ZE7Ix8nmFgo/oVuuE01cJT2XB13A==", "dev": true }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -1469,6 +1481,12 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true + }, "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", @@ -1505,6 +1523,12 @@ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -2304,6 +2328,12 @@ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camel-case": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", @@ -2377,6 +2407,12 @@ "tslib": "^1.9.0" } }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -2491,6 +2527,12 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -2764,6 +2806,27 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", @@ -3365,6 +3428,15 @@ "prr": "~1.0.1" } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, "es-abstract": { "version": "1.17.5", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", @@ -3774,6 +3846,15 @@ "locate-path": "^2.0.0" } }, + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "requires": { + "semver-regex": "^2.0.0" + } + }, "findup-sync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", @@ -4899,6 +4980,140 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "husky": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz", + "integrity": "sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^6.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -4931,6 +5146,24 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -5073,6 +5306,12 @@ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", "dev": true }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", @@ -5460,6 +5699,12 @@ "type-check": "~0.3.2" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "loader-runner": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", @@ -5628,6 +5873,12 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -5775,6 +6026,12 @@ "run-queue": "^1.0.3" } }, + "mri": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.5.tgz", + "integrity": "sha512-d2RKzMD4JNyHMbnbWnznPaa8vbdlq/4pNZ3IgdaGrVbBhebBsGUUE/6qorTMYNS6TwuH3ilfOlD2bf4Igh8CKg==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -5796,6 +6053,27 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "multimatch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", + "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + } + } + }, "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", @@ -6078,6 +6356,21 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "opencollective-postinstall": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", + "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", + "dev": true + }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", @@ -6210,6 +6503,15 @@ "tslib": "^1.10.0" } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-asn1": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", @@ -6224,6 +6526,18 @@ "safe-buffer": "^5.1.1" } }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -6452,6 +6766,15 @@ "find-up": "^2.1.0" } }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -6576,6 +6899,12 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "dev": true + }, "pretty-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", @@ -6586,6 +6915,165 @@ "utila": "~0.4" } }, + "pretty-quick": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-2.0.1.tgz", + "integrity": "sha512-y7bJt77XadjUr+P1uKqZxFWLddvj3SKY6EU4BuQtMxmmEFSMpbN132pUWdSG1g1mtUfO0noBvn7wBf0BVeomHg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "execa": "^2.1.0", + "find-up": "^4.1.0", + "ignore": "^5.1.4", + "mri": "^1.1.4", + "multimatch": "^4.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", + "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-2.1.0.tgz", + "integrity": "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^3.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "npm-run-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", + "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -7200,6 +7688,18 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true + }, "send": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", @@ -7833,6 +8333,12 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "style-loader": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.1.4.tgz", @@ -9269,6 +9775,12 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -9377,6 +9889,15 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "yaml": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.9.2.tgz", + "integrity": "sha512-HPT7cGGI0DuRcsO51qC1j9O16Dh1mZ2bnXwsi0jrSpsLz0WxOLSLXfkABVl6bZO629py3CU+OMJtpNHDLB97kg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "yargs": { "version": "13.2.4", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", diff --git a/package.json b/package.json index 97a4131..f8b3168 100644 --- a/package.json +++ b/package.json @@ -45,9 +45,12 @@ "copy-webpack-plugin": "^5.1.1", "css-loader": "^3.5.2", "html-webpack-plugin": "^4.2.0", + "husky": "^4.2.5", "jquery": "^3.2.1", "jquery-ui": "^1.12.1", "keymage": "^1.1.3", + "prettier": "^2.0.5", + "pretty-quick": "^2.0.1", "style-loader": "^1.1.4", "vanilla-picker": "^2.10.1", "webpack": "^4.42.1", From c67b369d8363a0264d79bf3571c287534abfe1e9 Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Thu, 7 May 2020 21:40:52 +0200 Subject: [PATCH 22/52] feat: added .editorconfig --- .editorconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..76617ce --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[{*.js,*.css}] +indent_size = 4 From a61debebb401b4484f1314461544021e59f8066e Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Thu, 7 May 2020 21:43:13 +0200 Subject: [PATCH 23/52] feat: configured prettier, pretty-quick and husky --- .editorconfig | 2 +- .prettierrc.json | 3 +++ package.json | 10 +++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 .prettierrc.json diff --git a/.editorconfig b/.editorconfig index 76617ce..d41152b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,5 +8,5 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[{*.js,*.css}] +[{*.js,*.css,*.html}] indent_size = 4 diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..de753c5 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "printWidth": 100 +} diff --git a/package.json b/package.json index f8b3168..3a99b54 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,10 @@ "build": "webpack --config config/webpack.build.js", "start:dev": "node scripts/server.js --mode=development", "start:prod": "npm run build && node scripts/server.js --mode=production", - "test": "echo \"No tests needed!\" && exit 1" + "test": "echo \"No tests needed!\" && exit 1", + "pretty-quick": "pretty-quick", + "format": "prettier --write .", + "style": "prettier --check ." }, "repository": { "type": "git", @@ -19,6 +22,11 @@ "Sketchboard", "lightweight" ], + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged" + } + }, "dependencies": { "dompurify": "^2.0.7", "express": "4.*", From c675ed398e024175965bda65302e662601f86a5d Mon Sep 17 00:00:00 2001 From: FloChehab Date: Thu, 7 May 2020 22:02:08 +0200 Subject: [PATCH 24/52] feat: setup linting CI --- .github/workflows/linting-code.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/linting-code.yml diff --git a/.github/workflows/linting-code.yml b/.github/workflows/linting-code.yml new file mode 100644 index 0000000..f900057 --- /dev/null +++ b/.github/workflows/linting-code.yml @@ -0,0 +1,21 @@ +# This workflow will do a clean install of node dependencies and check the code style + +name: Linting code CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: 12.x + - run: npm ci + - run: npm run style From dafb9f464641b171012349ac8a605eb22847ba4c Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Thu, 7 May 2020 22:14:29 +0200 Subject: [PATCH 25/52] feat: build docker image in CI --- .github/workflows/build-docker.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/build-docker.yml diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml new file mode 100644 index 0000000..7731628 --- /dev/null +++ b/.github/workflows/build-docker.yml @@ -0,0 +1,18 @@ +# This workflow will do clean build of the docker image + +name: Docker Image CI (also tests build) + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag rofl256/whiteboard:$(date +%s) From d4a30ea3ed5ade2ccba0a4e946073ae0aa5d7cf9 Mon Sep 17 00:00:00 2001 From: Florent Chehab Date: Sat, 9 May 2020 15:40:26 +0200 Subject: [PATCH 26/52] style: formatted entire repo --- README.md | 151 ++++--- config/webpack.base.js | 104 +++-- config/webpack.build.js | 12 +- config/webpack.dev.js | 20 +- docker-compose.yml | 2 +- scripts/s_whiteboard.js | 53 ++- scripts/server-backend.js | 233 +++++----- scripts/server-frontend-dev.js | 51 ++- scripts/server.js | 10 +- scripts/utils.js | 36 +- src/css/main.css | 13 +- src/index.html | 367 ++++++++++------ src/js/classes/Point.js | 9 +- src/js/icons.js | 4 +- src/js/index.js | 8 +- src/js/keybinds.js | 80 ++-- src/js/main.js | 505 ++++++++++++--------- src/js/services/ReadOnlyService.js | 2 +- src/js/shortcutFunctions.js | 36 +- src/js/utils.js | 8 +- src/js/whiteboard.js | 683 +++++++++++++++++++++-------- 21 files changed, 1494 insertions(+), 893 deletions(-) diff --git a/README.md b/README.md index 45a8876..fc7afdc 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,33 @@ # whiteboard + This is a lightweight NodeJS collaborative Whiteboard/Sketchboard witch can easily be customized... ![start](./doc/start.png) -## Demowhiteboard ## +## Demowhiteboard + [HERE](https://cloud13.de/testwhiteboard/) (Reset every night) ## Some Features -* Shows remote user cursors while drawing -* Undo / Redo function for each user -* Drag+Drop / Copy+Paste Images or PDFs from PC and Browsers -* Resize, Move & Draw Images to Canvas or Background -* Write text -* Save Whiteboard to Image and JSON -* Draw angle lines by pressing "shift" while drawing (with line tool) -* Draw square by pressing "shift" while drawing (with rectangle tool) -* Indicator that shows the smallest screen participating -* Keybindings for ALL the functions -* Working on PC, Tablet & Mobile + +- Shows remote user cursors while drawing +- Undo / Redo function for each user +- Drag+Drop / Copy+Paste Images or PDFs from PC and Browsers +- Resize, Move & Draw Images to Canvas or Background +- Write text +- Save Whiteboard to Image and JSON +- Draw angle lines by pressing "shift" while drawing (with line tool) +- Draw square by pressing "shift" while drawing (with rectangle tool) +- Indicator that shows the smallest screen participating +- Keybindings for ALL the functions +- Working on PC, Tablet & Mobile ## Install the App + You can run this app with and without docker + ### Without Docker + 1. install the latest NodeJs 2. Clone the app 3. Run `npm ci` inside the folder @@ -29,6 +35,7 @@ You can run this app with and without docker 5. Surf to http://YOURIP:8080 ### With Docker + 1. `docker run -d -p 8080:8080 rofl256/whiteboard` 2. Surf to http://YOURIP:8080 @@ -37,63 +44,66 @@ You can run this app with and without docker After you have installed the app, run `npm run start:dev` to start the backend and a frontend development server. The website will be accessible on http://locahost:8080. ## Default keyboard shortcuts + Use keyboard shortcuts to become more productive while using Whiteboard. They are especially useful if you work with interactive displays such as XP-Pen Artist, Huion Kamvas and Wacom Cintiq. These devices have quick buttons (6-8 buttons and scrolling). By default, the buttons on these displays are mapped to standard Photoshop keyboard shortcuts. Keys can be configured to function effectively in other software. The following are predefined shortcuts that you can override in the file [./src/js/keybinds.js](./src/js/keybinds.js) -Result | Windows and Linux | macOS ------- | -------------------- | ------- -Clear the whiteboard | Ctrl + Shift + Z | Command + Shift + Z -Undo your last step | Ctrl + Z | Command + Z -Redo your last undo | Ctrl + Y | Command + Y -Select an area | Ctrl + X | Command + X -Take the mouse | Ctrl + M | Command + M -Take the pen | Ctrl + P | Command + P -Draw a line | Ctrl + L | Command + L -Draw a rectangle | Ctrl + R | Command + R -Draw a circle | Ctrl + C | Command + C -Toggle between line, rectangle and circle | Ctrl + Shift + F | Command + Shift + F -Toggle between pen and eraser | Ctrl + Shift + X | Command + Shift + X -Toggle between main clolors (black, blue, green, yellow and red) | Ctrl + Shift + R | Command + Shift + R -Write text | Ctrl + A | Command + A -Take the eraser | Ctrl + E | Command + E -Increase thickness | Ctrl + Up Arrow | Command + Up Arrow -Decrease thickness | Ctrl + Down Arrow | Command + Down Arrow -Colorpicker | Ctrl + Shift + C | Command + Shift + C -Set black color | Ctrl + Shift + 1 | Command + Shift + 1 -Set blue color | Ctrl + Shift + 2 | Command + Shift + 2 -Set green color | Ctrl + Shift + 3 | Command + Shift + 3 -Set yellow color | Ctrl + Shift + 4 | Command + Shift + 4 -Set red color | Ctrl + Shift + 5 | Command + Shift + 5 -Save whiteboard as image | Ctrl + S | Command + S -Save whiteboard as JSON | Ctrl + Shift + K | Command + Shift + K -Save whiteboard to WebDav | Ctrl + Shift + I (i) | Command + Shift + I (i) -Load saved JSON to whiteboard | Ctrl + Shift + J | Command + Shift + J -Share whiteboard | Ctrl + Shift + S | Command + Shift + S -Hide or show toolbar | Tab | Tab -Move selected object up | Up Arrow | Up Arrow -Move selected object down | Down Arrow | Down Arrow -Move selected object left | Left Arrow | Left Arrow -Move selected object right | Right Arrow | Right Arrow -Drop object | Ctrl + Enter | Command + Enter -Add Image to backgroud | Shift + Enter | Shift + Enter -Cancel all actions | Escape | Escape -Delete selected object | Delete | Delete -Use Line tool when pen is active (Not changeable) | Shift (Hold) | Shift (Hold) +| Result | Windows and Linux | macOS | +| ---------------------------------------------------------------- | -------------------- | ----------------------- | +| Clear the whiteboard | Ctrl + Shift + Z | Command + Shift + Z | +| Undo your last step | Ctrl + Z | Command + Z | +| Redo your last undo | Ctrl + Y | Command + Y | +| Select an area | Ctrl + X | Command + X | +| Take the mouse | Ctrl + M | Command + M | +| Take the pen | Ctrl + P | Command + P | +| Draw a line | Ctrl + L | Command + L | +| Draw a rectangle | Ctrl + R | Command + R | +| Draw a circle | Ctrl + C | Command + C | +| Toggle between line, rectangle and circle | Ctrl + Shift + F | Command + Shift + F | +| Toggle between pen and eraser | Ctrl + Shift + X | Command + Shift + X | +| Toggle between main clolors (black, blue, green, yellow and red) | Ctrl + Shift + R | Command + Shift + R | +| Write text | Ctrl + A | Command + A | +| Take the eraser | Ctrl + E | Command + E | +| Increase thickness | Ctrl + Up Arrow | Command + Up Arrow | +| Decrease thickness | Ctrl + Down Arrow | Command + Down Arrow | +| Colorpicker | Ctrl + Shift + C | Command + Shift + C | +| Set black color | Ctrl + Shift + 1 | Command + Shift + 1 | +| Set blue color | Ctrl + Shift + 2 | Command + Shift + 2 | +| Set green color | Ctrl + Shift + 3 | Command + Shift + 3 | +| Set yellow color | Ctrl + Shift + 4 | Command + Shift + 4 | +| Set red color | Ctrl + Shift + 5 | Command + Shift + 5 | +| Save whiteboard as image | Ctrl + S | Command + S | +| Save whiteboard as JSON | Ctrl + Shift + K | Command + Shift + K | +| Save whiteboard to WebDav | Ctrl + Shift + I (i) | Command + Shift + I (i) | +| Load saved JSON to whiteboard | Ctrl + Shift + J | Command + Shift + J | +| Share whiteboard | Ctrl + Shift + S | Command + Shift + S | +| Hide or show toolbar | Tab | Tab | +| Move selected object up | Up Arrow | Up Arrow | +| Move selected object down | Down Arrow | Down Arrow | +| Move selected object left | Left Arrow | Left Arrow | +| Move selected object right | Right Arrow | Right Arrow | +| Drop object | Ctrl + Enter | Command + Enter | +| Add Image to backgroud | Shift + Enter | Shift + Enter | +| Cancel all actions | Escape | Escape | +| Delete selected object | Delete | Delete | +| Use Line tool when pen is active (Not changeable) | Shift (Hold) | Shift (Hold) | ## URL Parameters + Call your site with GET parameters to change the WhiteboardID or the Username `http://YOURIP:8080?whiteboardid=MYID&username=MYNAME` -* whiteboardid => All people with the same ID are drawing on the same board -* username => The name witch is showing to others while drawing -* title => Change the name of the Browser Tab -* randomid => if set to true, a random whiteboardId will be generated if not given aswell +- whiteboardid => All people with the same ID are drawing on the same board +- username => The name witch is showing to others while drawing +- title => Change the name of the Browser Tab +- randomid => if set to true, a random whiteboardId will be generated if not given aswell ## Security - AccessToken (Optional) + To prevent clients who might know or guess the base URL from abusing the server to upload files and stuff..., you can set an accesstoken at server start. Server (Without docker): `node scripts/server.js --accesstoken="mySecToken"` @@ -107,6 +117,7 @@ Then set the same token on the client side as well: Done! ## WebDAV (Optional) + This function allows your users to save the whiteboard directly to a webdav server (Nextcloud) as image without downloading it. To enable it: @@ -125,21 +136,25 @@ Note: For the most owncloud/nextcloud setups you have to set the WebDav-Server U Done! - ## Things you may want to know -* Whiteboards are gone if you restart the Server, so keep that in mind (or save your whiteboard) -* You should be able to customize the layout without ever touching the whiteboard.js (take a look at index.html & main.js) + +- Whiteboards are gone if you restart the Server, so keep that in mind (or save your whiteboard) +- You should be able to customize the layout without ever touching the whiteboard.js (take a look at index.html & main.js) ## All server start parameters (also docker) -* accesstoken => take a look at "Security - AccessToken" for a full explanation -* disablesmallestscreen => set this to "true" if you don't want show the "smallest screen" indicator (A dotted gray line) to the users -* webdav => Enable the function to save to a webdav-server (Must also be enabled on the client; Take a look at the webdav section) + +- accesstoken => take a look at "Security - AccessToken" for a full explanation +- disablesmallestscreen => set this to "true" if you don't want show the "smallest screen" indicator (A dotted gray line) to the users +- webdav => Enable the function to save to a webdav-server (Must also be enabled on the client; Take a look at the webdav section) ## ToDo -* Make undo function more reliable on texts + +- Make undo function more reliable on texts ## Nginx Reverse Proxy configuration + Add this to your server part: + ``` location /whiteboard/ { proxy_set_header HOST $host; @@ -149,22 +164,22 @@ Add this to your server part: proxy_pass http://YOURIP:8080/; } ``` + To run it at /whiteboard. Don't forget to change -> YOURIP! ## Nextcloud integration + 1. Install this app on your server 2. Enable and go to "external sites" (app) on your Nextcloud -2. Add a link to your server: `https://YOURIP/whiteboard/?whiteboardid=WHITEBOARDNAME&username={uid}` -You can give each group its own whiteboard by changeing the WHITEBOARDNAME in the URL if you want. +3. Add a link to your server: `https://YOURIP/whiteboard/?whiteboardid=WHITEBOARDNAME&username={uid}` + You can give each group its own whiteboard by changeing the WHITEBOARDNAME in the URL if you want. Note: You might have to serve the app with https (If your nextcloud server runs https). To do so, its recommend to run this app behind a reverse proxy. (as shown above) #### (Optional) Set whiteboard icon in nextcloud + ![start](https://raw.githubusercontent.com/cracker0dks/whiteboard/master/doc/iconPrev.jpg) Upload both icons present at /doc/nextcloud_icons/ to your nextcloud at the "external sites" admin section. Then set it as symbol on your link. - - - -___ MIT License ___ +**_ MIT License _** diff --git a/config/webpack.base.js b/config/webpack.base.js index 936940f..8c586e7 100644 --- a/config/webpack.base.js +++ b/config/webpack.base.js @@ -1,61 +1,59 @@ const webpack = require("webpack"); -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); -const CopyPlugin = require('copy-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin') +const { CleanWebpackPlugin } = require("clean-webpack-plugin"); +const CopyPlugin = require("copy-webpack-plugin"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); const config = { - entry: { - main: ["./src/js/index.js"], - }, - output: { - path: path.join(__dirname, "..", "dist"), - filename: "[name]-[hash].js" - }, - resolve: { - extensions: ["*", ".json", ".js"] - }, - module: { - rules: [ - { - test: /\.(js)$/, - exclude: /node_modules/, - loader: "babel-loader", - options: { - compact: true - } - }, - { - test: /\.css$/, - use: ['style-loader', 'css-loader'] - }, - { - test: /\.(png|jpe?g|gif)$/i, - use: [ - { - loader: 'file-loader', - }, + entry: { + main: ["./src/js/index.js"], + }, + output: { + path: path.join(__dirname, "..", "dist"), + filename: "[name]-[hash].js", + }, + resolve: { + extensions: ["*", ".json", ".js"], + }, + module: { + rules: [ + { + test: /\.(js)$/, + exclude: /node_modules/, + loader: "babel-loader", + options: { + compact: true, + }, + }, + { + test: /\.css$/, + use: ["style-loader", "css-loader"], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [ + { + loader: "file-loader", + }, + ], + }, ], - } - ] - }, - plugins: [ - new CleanWebpackPlugin(), - new webpack.ProvidePlugin({ - $: 'jquery', - jQuery: 'jquery', - "window.jQuery": "jquery", - "window.$": "jquery", - }), - new CopyPlugin([ - { from: 'assets', to: '' }, - ]), - new HtmlWebpackPlugin({ - template: 'src/index.html', - minify: false, - inject: true - }) - ] + }, + plugins: [ + new CleanWebpackPlugin(), + new webpack.ProvidePlugin({ + $: "jquery", + jQuery: "jquery", + "window.jQuery": "jquery", + "window.$": "jquery", + }), + new CopyPlugin([{ from: "assets", to: "" }]), + new HtmlWebpackPlugin({ + template: "src/index.html", + minify: false, + inject: true, + }), + ], }; module.exports = config; diff --git a/config/webpack.build.js b/config/webpack.build.js index 9a21477..6e0d9c1 100644 --- a/config/webpack.build.js +++ b/config/webpack.build.js @@ -2,10 +2,10 @@ const merge = require("webpack-merge"); const baseConfig = require("./webpack.base"); module.exports = merge(baseConfig, { - mode: "production", - optimization: { - minimize: true, - nodeEnv: "production", - }, - devtool: false + mode: "production", + optimization: { + minimize: true, + nodeEnv: "production", + }, + devtool: false, }); diff --git a/config/webpack.dev.js b/config/webpack.dev.js index a919e81..7822170 100644 --- a/config/webpack.dev.js +++ b/config/webpack.dev.js @@ -3,16 +3,16 @@ const baseConfig = require("./webpack.base"); const webpack = require("webpack"); const devConfig = merge(baseConfig, { - mode: "development", - devtool: "eval-source-map", - optimization: { - minimize: false, - }, - plugins: [ - new webpack.HotModuleReplacementPlugin(), - new webpack.NamedModulesPlugin(), - new webpack.NoEmitOnErrorsPlugin(), - ].concat(baseConfig.plugins), + mode: "development", + devtool: "eval-source-map", + optimization: { + minimize: false, + }, + plugins: [ + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), + new webpack.NoEmitOnErrorsPlugin(), + ].concat(baseConfig.plugins), }); module.exports = devConfig; diff --git a/docker-compose.yml b/docker-compose.yml index bb10f3f..878bd00 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.1' +version: "3.1" services: whiteboard: image: rofl256/whiteboard diff --git a/scripts/s_whiteboard.js b/scripts/s_whiteboard.js index 72a9cf4..4299785 100644 --- a/scripts/s_whiteboard.js +++ b/scripts/s_whiteboard.js @@ -7,10 +7,12 @@ module.exports = { var tool = content["t"]; //Tool witch is used var wid = content["wid"]; //whiteboard ID var username = content["username"]; - if (tool === "clear") { //Clear the whiteboard + if (tool === "clear") { + //Clear the whiteboard delete savedBoards[wid]; delete savedUndos[wid]; - } else if (tool === "undo") { //Undo an action + } else if (tool === "undo") { + //Undo an action if (!savedUndos[wid]) { savedUndos[wid] = []; } @@ -19,7 +21,10 @@ module.exports = { if (savedBoards[wid][i]["username"] == username) { var drawId = savedBoards[wid][i]["drawId"]; for (var i = savedBoards[wid].length - 1; i >= 0; i--) { - if (savedBoards[wid][i]["drawId"] == drawId && savedBoards[wid][i]["username"] == username) { + if ( + savedBoards[wid][i]["drawId"] == drawId && + savedBoards[wid][i]["username"] == username + ) { savedUndos[wid].push(savedBoards[wid][i]); savedBoards[wid].splice(i, 1); } @@ -27,7 +32,7 @@ module.exports = { break; } } - if(savedUndos[wid].length > 1000) { + if (savedUndos[wid].length > 1000) { savedUndos[wid].splice(0, savedUndos[wid].length - 1000); } } @@ -42,7 +47,10 @@ module.exports = { if (savedUndos[wid][i]["username"] == username) { var drawId = savedUndos[wid][i]["drawId"]; for (var i = savedUndos[wid].length - 1; i >= 0; i--) { - if (savedUndos[wid][i]["drawId"] == drawId && savedUndos[wid][i]["username"] == username) { + if ( + savedUndos[wid][i]["drawId"] == drawId && + savedUndos[wid][i]["username"] == username + ) { savedBoards[wid].push(savedUndos[wid][i]); savedUndos[wid].splice(i, 1); } @@ -50,14 +58,36 @@ module.exports = { break; } } - } else if (["line", "pen", "rect", "circle", "eraser", "addImgBG", "recSelect", "eraseRec", "addTextBox", "setTextboxText", "removeTextbox", "setTextboxPosition", "setTextboxFontSize", "setTextboxFontColor"].includes(tool)) { //Save all this actions + } else if ( + [ + "line", + "pen", + "rect", + "circle", + "eraser", + "addImgBG", + "recSelect", + "eraseRec", + "addTextBox", + "setTextboxText", + "removeTextbox", + "setTextboxPosition", + "setTextboxFontSize", + "setTextboxFontColor", + ].includes(tool) + ) { + //Save all this actions if (!savedBoards[wid]) { savedBoards[wid] = []; } delete content["wid"]; //Delete id from content so we don't store it twice if (tool === "setTextboxText") { - for (var i = savedBoards[wid].length - 1; i >= 0; i--) { //Remove old textbox tex -> dont store it twice - if (savedBoards[wid][i]["t"] === "setTextboxText" && savedBoards[wid][i]["d"][0] === content["d"][0]) { + for (var i = savedBoards[wid].length - 1; i >= 0; i--) { + //Remove old textbox tex -> dont store it twice + if ( + savedBoards[wid][i]["t"] === "setTextboxText" && + savedBoards[wid][i]["d"][0] === content["d"][0] + ) { savedBoards[wid].splice(i, 1); } } @@ -65,7 +95,8 @@ module.exports = { savedBoards[wid].push(content); } }, - loadStoredData: function (wid) { //Load saved whiteboard + loadStoredData: function (wid) { + //Load saved whiteboard return savedBoards[wid] ? savedBoards[wid] : []; - } -} \ No newline at end of file + }, +}; diff --git a/scripts/server-backend.js b/scripts/server-backend.js index 1877d27..7d84260 100644 --- a/scripts/server-backend.js +++ b/scripts/server-backend.js @@ -5,26 +5,26 @@ function startBackendServer(port) { var accessToken = ""; //Can be set here or as start parameter (node server.js --accesstoken=MYTOKEN) var disableSmallestScreen = false; //Can be set to true if you dont want to show (node server.js --disablesmallestscreen=true) var webdav = false; //Can be set to true if you want to allow webdav save (node server.js --webdav=true) - + var fs = require("fs-extra"); - var express = require('express'); - var formidable = require('formidable'); //form upload processing - - const createDOMPurify = require('dompurify'); //Prevent xss - const { JSDOM } = require('jsdom'); - const window = (new JSDOM('')).window; + var express = require("express"); + var formidable = require("formidable"); //form upload processing + + const createDOMPurify = require("dompurify"); //Prevent xss + const { JSDOM } = require("jsdom"); + const window = new JSDOM("").window; const DOMPurify = createDOMPurify(window); - + const { createClient } = require("webdav"); - + var s_whiteboard = require("./s_whiteboard.js"); - + var app = express(); - app.use(express.static(path.join(__dirname, '..', 'dist'))); - app.use("/uploads", express.static(path.join(__dirname, '..', 'public', 'uploads'))); - var server = require('http').Server(app); + app.use(express.static(path.join(__dirname, "..", "dist"))); + app.use("/uploads", express.static(path.join(__dirname, "..", "public", "uploads"))); + var server = require("http").Server(app); server.listen(port); - var io = require('socket.io')(server, {path: "/ws-api", }); + var io = require("socket.io")(server, { path: "/ws-api" }); console.log("Webserver & socketserver running on port:" + port); if (process.env.accesstoken) { accessToken = process.env.accesstoken; @@ -35,7 +35,7 @@ function startBackendServer(port) { if (process.env.webdav) { webdav = true; } - + var startArgs = getArgs(); if (startArgs["accesstoken"]) { accessToken = startArgs["accesstoken"]; @@ -46,7 +46,7 @@ function startBackendServer(port) { if (startArgs["webdav"]) { webdav = true; } - + if (accessToken !== "") { console.log("AccessToken set to: " + accessToken); } @@ -56,8 +56,8 @@ function startBackendServer(port) { if (webdav) { console.log("Webdav save is enabled!"); } - - app.get('/api/loadwhiteboard', function (req, res) { + + app.get("/api/loadwhiteboard", function (req, res) { var wid = req["query"]["wid"]; var at = req["query"]["at"]; //accesstoken if (accessToken === "" || accessToken == at) { @@ -65,31 +65,32 @@ function startBackendServer(port) { res.send(ret); res.end(); } else { - res.status(401); //Unauthorized + res.status(401); //Unauthorized res.end(); } }); - - app.post('/api/upload', function (req, res) { //File upload + + app.post("/api/upload", function (req, res) { + //File upload var form = new formidable.IncomingForm(); //Receive form var formData = { files: {}, - fields: {} - } - - form.on('file', function (name, file) { + fields: {}, + }; + + form.on("file", function (name, file) { formData["files"][file.name] = file; }); - - form.on('field', function (name, value) { + + form.on("field", function (name, value) { formData["fields"][name] = value; }); - - form.on('error', function (err) { - console.log('File uplaod Error!'); + + form.on("error", function (err) { + console.log("File uplaod Error!"); }); - - form.on('end', function () { + + form.on("end", function () { if (accessToken === "" || accessToken == formData["fields"]["at"]) { progressUploadFormData(formData, function (err) { if (err) { @@ -104,22 +105,22 @@ function startBackendServer(port) { } }); } else { - res.status(401); //Unauthorized + res.status(401); //Unauthorized res.end(); } //End file upload }); form.parse(req); }); - + function progressUploadFormData(formData, callback) { console.log("Progress new Form Data"); var fields = escapeAllContentStrings(formData.fields); var files = formData.files; var whiteboardId = fields["whiteboardId"]; - + var name = fields["name"] || ""; - var date = fields["date"] || (+new Date()); + var date = fields["date"] || +new Date(); var filename = whiteboardId + "_" + date + ".png"; var webdavaccess = fields["webdavaccess"] || false; try { @@ -133,24 +134,33 @@ function startBackendServer(port) { return; } var imagedata = fields["imagedata"]; - if (imagedata && imagedata != "") { //Save from base64 data - imagedata = imagedata.replace(/^data:image\/png;base64,/, "").replace(/^data:image\/jpeg;base64,/, ""); + if (imagedata && imagedata != "") { + //Save from base64 data + imagedata = imagedata + .replace(/^data:image\/png;base64,/, "") + .replace(/^data:image\/jpeg;base64,/, ""); console.log(filename, "uploaded"); - fs.writeFile('./public/uploads/' + filename, imagedata, 'base64', function (err) { + fs.writeFile("./public/uploads/" + filename, imagedata, "base64", function (err) { if (err) { console.log("error", err); callback(err); } else { - if (webdavaccess) { //Save image to webdav + if (webdavaccess) { + //Save image to webdav if (webdav) { - saveImageToWebdav('./public/uploads/' + filename, filename, webdavaccess, function (err) { - if (err) { - console.log("error", err); - callback(err); - } else { - callback(); + saveImageToWebdav( + "./public/uploads/" + filename, + filename, + webdavaccess, + function (err) { + if (err) { + console.log("error", err); + callback(err); + } else { + callback(); + } } - }) + ); } else { callback("Webdav is not enabled on the server!"); } @@ -165,78 +175,92 @@ function startBackendServer(port) { } }); } - + function saveImageToWebdav(imagepath, filename, webdavaccess, callback) { if (webdavaccess) { var webdavserver = webdavaccess["webdavserver"] || ""; var webdavpath = webdavaccess["webdavpath"] || "/"; var webdavusername = webdavaccess["webdavusername"] || ""; var webdavpassword = webdavaccess["webdavpassword"] || ""; - - const client = createClient( - webdavserver, - { - username: webdavusername, - password: webdavpassword - } - ) - client.getDirectoryContents(webdavpath).then((items) => { - var cloudpath = webdavpath+ '' + filename; - console.log("webdav saving to:", cloudpath); - fs.createReadStream(imagepath).pipe(client.createWriteStream(cloudpath)); - callback(); - }).catch((error) => { - callback("403"); - console.log("Could not connect to webdav!") + + const client = createClient(webdavserver, { + username: webdavusername, + password: webdavpassword, }); + client + .getDirectoryContents(webdavpath) + .then((items) => { + var cloudpath = webdavpath + "" + filename; + console.log("webdav saving to:", cloudpath); + fs.createReadStream(imagepath).pipe(client.createWriteStream(cloudpath)); + callback(); + }) + .catch((error) => { + callback("403"); + console.log("Could not connect to webdav!"); + }); } else { - callback("Error: no access data!") + callback("Error: no access data!"); } } - + var smallestScreenResolutions = {}; - io.on('connection', function (socket) { + io.on("connection", function (socket) { var whiteboardId = null; - - socket.on('disconnect', function () { - if (smallestScreenResolutions && smallestScreenResolutions[whiteboardId] && socket && socket.id) { + + socket.on("disconnect", function () { + if ( + smallestScreenResolutions && + smallestScreenResolutions[whiteboardId] && + socket && + socket.id + ) { delete smallestScreenResolutions[whiteboardId][socket.id]; } - socket.compress(false).broadcast.emit('refreshUserBadges', null); //Removes old user Badges + socket.compress(false).broadcast.emit("refreshUserBadges", null); //Removes old user Badges sendSmallestScreenResolution(); }); - - socket.on('drawToWhiteboard', function (content) { + + socket.on("drawToWhiteboard", function (content) { content = escapeAllContentStrings(content); if (accessToken === "" || accessToken == content["at"]) { - socket.compress(false).broadcast.to(whiteboardId).emit('drawToWhiteboard', content); //Send to all users in the room (not own socket) + socket.compress(false).broadcast.to(whiteboardId).emit("drawToWhiteboard", content); //Send to all users in the room (not own socket) s_whiteboard.handleEventsAndData(content); //save whiteboardchanges on the server } else { - socket.emit('wrongAccessToken', true); + socket.emit("wrongAccessToken", true); } }); - - socket.on('joinWhiteboard', function (content) { + + socket.on("joinWhiteboard", function (content) { content = escapeAllContentStrings(content); if (accessToken === "" || accessToken == content["at"]) { whiteboardId = content["wid"]; socket.join(whiteboardId); //Joins room name=wid - smallestScreenResolutions[whiteboardId] = smallestScreenResolutions[whiteboardId] ? smallestScreenResolutions[whiteboardId] : {}; - smallestScreenResolutions[whiteboardId][socket.id] = content["windowWidthHeight"] || { w: 10000, h: 10000 }; + smallestScreenResolutions[whiteboardId] = smallestScreenResolutions[whiteboardId] + ? smallestScreenResolutions[whiteboardId] + : {}; + smallestScreenResolutions[whiteboardId][socket.id] = content[ + "windowWidthHeight" + ] || { w: 10000, h: 10000 }; sendSmallestScreenResolution(); } else { - socket.emit('wrongAccessToken', true); + socket.emit("wrongAccessToken", true); } }); - - socket.on('updateScreenResolution', function (content) { + + socket.on("updateScreenResolution", function (content) { content = escapeAllContentStrings(content); - if (smallestScreenResolutions[whiteboardId] && (accessToken === "" || accessToken == content["at"])) { - smallestScreenResolutions[whiteboardId][socket.id] = content["windowWidthHeight"] || { w: 10000, h: 10000 }; + if ( + smallestScreenResolutions[whiteboardId] && + (accessToken === "" || accessToken == content["at"]) + ) { + smallestScreenResolutions[whiteboardId][socket.id] = content[ + "windowWidthHeight" + ] || { w: 10000, h: 10000 }; sendSmallestScreenResolution(); } }); - + function sendSmallestScreenResolution() { if (disableSmallestScreen) { return; @@ -244,35 +268,44 @@ function startBackendServer(port) { var smallestWidth = 10000; var smallestHeight = 10000; for (var i in smallestScreenResolutions[whiteboardId]) { - smallestWidth = smallestWidth > smallestScreenResolutions[whiteboardId][i]["w"] ? smallestScreenResolutions[whiteboardId][i]["w"] : smallestWidth; - smallestHeight = smallestHeight > smallestScreenResolutions[whiteboardId][i]["h"] ? smallestScreenResolutions[whiteboardId][i]["h"] : smallestHeight; + smallestWidth = + smallestWidth > smallestScreenResolutions[whiteboardId][i]["w"] + ? smallestScreenResolutions[whiteboardId][i]["w"] + : smallestWidth; + smallestHeight = + smallestHeight > smallestScreenResolutions[whiteboardId][i]["h"] + ? smallestScreenResolutions[whiteboardId][i]["h"] + : smallestHeight; } - io.to(whiteboardId).emit('updateSmallestScreenResolution', { w: smallestWidth, h: smallestHeight }); + io.to(whiteboardId).emit("updateSmallestScreenResolution", { + w: smallestWidth, + h: smallestHeight, + }); } }); - + //Prevent cross site scripting (xss) function escapeAllContentStrings(content, cnt) { - if (!cnt) - cnt = 0; - - if (typeof (content) === "string") { + if (!cnt) cnt = 0; + + if (typeof content === "string") { return DOMPurify.sanitize(content); } for (var i in content) { - if (typeof (content[i]) === "string") { + if (typeof content[i] === "string") { content[i] = DOMPurify.sanitize(content[i]); - } if (typeof (content[i]) === "object" && cnt < 10) { + } + if (typeof content[i] === "object" && cnt < 10) { content[i] = escapeAllContentStrings(content[i], ++cnt); } } return content; } - - process.on('unhandledRejection', error => { + + process.on("unhandledRejection", (error) => { // Will print "unhandledRejection err is not defined" - console.log('unhandledRejection', error.message); - }) + console.log("unhandledRejection", error.message); + }); } -module.exports = startBackendServer; \ No newline at end of file +module.exports = startBackendServer; diff --git a/scripts/server-frontend-dev.js b/scripts/server-frontend-dev.js index 0e90065..ca45fee 100644 --- a/scripts/server-frontend-dev.js +++ b/scripts/server-frontend-dev.js @@ -1,34 +1,33 @@ const devServerConfig = { - hot: true, - inline: true, - stats: { - children: false, - maxModules: 0 - }, - proxy: { - // proxies for the backend - '/api': 'http://localhost:3000', - '/uploads': 'http://localhost:3000', - '/ws-api': { - target: 'ws://localhost:3000', - ws: true, - } - } -} + hot: true, + inline: true, + stats: { + children: false, + maxModules: 0, + }, + proxy: { + // proxies for the backend + "/api": "http://localhost:3000", + "/uploads": "http://localhost:3000", + "/ws-api": { + target: "ws://localhost:3000", + ws: true, + }, + }, +}; function startFrontendDevServer(port) { - // require here to prevent prod dependency to webpack - const webpack = require("webpack"); - const WebpackDevServer = require("webpack-dev-server"); - const config = require("../config/webpack.dev"); + // require here to prevent prod dependency to webpack + const webpack = require("webpack"); + const WebpackDevServer = require("webpack-dev-server"); + const config = require("../config/webpack.dev"); - new WebpackDevServer(webpack(config), devServerConfig) - .listen(port, (err) => { - if (err) { - console.log(err); - } + new WebpackDevServer(webpack(config), devServerConfig).listen(port, (err) => { + if (err) { + console.log(err); + } - console.log("Listening on port " + port); + console.log("Listening on port " + port); }); } diff --git a/scripts/server.js b/scripts/server.js index e5a52cc..58414ae 100644 --- a/scripts/server.js +++ b/scripts/server.js @@ -4,23 +4,23 @@ const startBackendServer = require("./server-backend"); const SERVER_MODES = { PRODUCTION: 1, - DEVELOPMENT: 2 -} + DEVELOPMENT: 2, +}; const args = getArgs(); -if ( typeof args.mode === "undefined") { +if (typeof args.mode === "undefined") { // default to production mode args.mode = "production"; } if (args.mode !== "production" && args.mode !== "development") { - throw new Error("--mode can only be 'development' or 'production'") + throw new Error("--mode can only be 'development' or 'production'"); } const server_mode = args.mode === "production" ? SERVER_MODES.PRODUCTION : SERVER_MODES.DEVELOPMENT; -if (server_mode === SERVER_MODES.DEVELOPMENT){ +if (server_mode === SERVER_MODES.DEVELOPMENT) { console.info("Starting server in development mode."); startFrontendDevServer(8080); // this time, it's the frontend server that is on port 8080 diff --git a/scripts/utils.js b/scripts/utils.js index dad47b3..d18a01b 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -1,22 +1,20 @@ function getArgs() { - const args = {} - process.argv - .slice(2, process.argv.length) - .forEach(arg => { - // long arg - if (arg.slice(0, 2) === '--') { - const longArg = arg.split('=') - args[longArg[0].slice(2, longArg[0].length)] = longArg[1] - } - // flags - else if (arg[0] === '-') { - const flags = arg.slice(1, arg.length).split('') - flags.forEach(flag => { - args[flag] = true - }) - } - }) - return args + const args = {}; + process.argv.slice(2, process.argv.length).forEach((arg) => { + // long arg + if (arg.slice(0, 2) === "--") { + const longArg = arg.split("="); + args[longArg[0].slice(2, longArg[0].length)] = longArg[1]; + } + // flags + else if (arg[0] === "-") { + const flags = arg.slice(1, arg.length).split(""); + flags.forEach((flag) => { + args[flag] = true; + }); + } + }); + return args; } -module.exports.getArgs = getArgs; \ No newline at end of file +module.exports.getArgs = getArgs; diff --git a/src/css/main.css b/src/css/main.css index 9ea45c6..a2d36f1 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -36,12 +36,11 @@ button::-moz-focus-inner { .whiteboard-edit-group.group-disabled { background: repeating-linear-gradient( 45deg, - rgba(255, 166, 0, 0.366) , + rgba(255, 166, 0, 0.366), rgba(255, 166, 0, 0.366) 10px, rgba(255, 166, 0, 0.666) 10px, rgba(255, 166, 0, 0.666) 20px ); - } /* @@ -90,19 +89,21 @@ button { background: transparent; outline: none; opacity: 1; - -webkit-transition: opacity .15s ease-in-out; - transition: opacity .15s ease-in-out; + -webkit-transition: opacity 0.15s ease-in-out; + transition: opacity 0.15s ease-in-out; } .textBox.active { border: 1px dashed gray; } -.textBox>.removeIcon, .textBox>.moveIcon { +.textBox > .removeIcon, +.textBox > .moveIcon { display: none; } -.textBox.active>.removeIcon, .textBox.active>.moveIcon { +.textBox.active > .removeIcon, +.textBox.active > .moveIcon { display: block; } diff --git a/src/index.html b/src/index.html index 9ef0286..f34965d 100644 --- a/src/index.html +++ b/src/index.html @@ -1,140 +1,255 @@ + + Whiteboard + + + - - Whiteboard - - - + + +
- - -
+ +
+
+ + +
- -
-
- - -
+
+ + + + +
-
- - - - -
+
+ + + + + + + + +
-
- - - - - - - - -
+
+ +
-
- -
+
+ -
- + - + +
- -
+
+ + + -
- - +
- - - - +
+ +
+
- -
- -
- -
-
- -
-

# msg. sent to server: 0

-

# msg. received from server: 0

-
- - - \ No newline at end of file +
+

# msg. sent to server: 0

+

# msg. received from server: 0

+
+ + diff --git a/src/js/classes/Point.js b/src/js/classes/Point.js index c7ec8cf..55f0717 100644 --- a/src/js/classes/Point.js +++ b/src/js/classes/Point.js @@ -1,4 +1,4 @@ -import {computeDist} from "../utils"; +import { computeDist } from "../utils"; class Point { /** @@ -42,7 +42,8 @@ class Point { let x = (e.offsetX || e.pageX - $(e.target).offset().left) + epsilon; let y = (e.offsetY || e.pageY - $(e.target).offset().top) + epsilon; - if (Number.isNaN(x) || Number.isNaN(y) || (x === epsilon && y === epsilon)) { // if it's a touch actually + if (Number.isNaN(x) || Number.isNaN(y) || (x === epsilon && y === epsilon)) { + // if it's a touch actually if (e.touches && e.touches.length && e.touches.length > 0) { const touch = e.touches[0]; x = touch.clientX - $("#mouseOverlay").offset().left; @@ -65,7 +66,7 @@ class Point { /** * Compute euclidean distance between points - * + * * @param {Point} otherPoint * @returns {number} */ @@ -74,4 +75,4 @@ class Point { } } -export default Point; \ No newline at end of file +export default Point; diff --git a/src/js/icons.js b/src/js/icons.js index 5947079..c6bbf98 100644 --- a/src/js/icons.js +++ b/src/js/icons.js @@ -17,7 +17,7 @@ import { faSortDown, faExpandArrowsAlt, faLock, - faLockOpen + faLockOpen, } from "@fortawesome/free-solid-svg-icons"; import { faSquare, @@ -53,4 +53,4 @@ library.add( faLockOpen ); -dom.i2svg() \ No newline at end of file +dom.i2svg(); diff --git a/src/js/index.js b/src/js/index.js index d9b980a..ccced5e 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -8,19 +8,19 @@ import "./icons"; import main from "./main"; $(document).ready(function () { - // Set correct width height on mobile browsers const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor); if (isChrome) { - $('head').append(''); + $("head").append( + '' + ); } else { - $('head').append(''); + $("head").append(''); } main(); }); - if (module.hot) { module.hot.accept(); } diff --git a/src/js/keybinds.js b/src/js/keybinds.js index 7274154..1a033db 100644 --- a/src/js/keybinds.js +++ b/src/js/keybinds.js @@ -1,48 +1,48 @@ /* ----------- KEYBINDINGS ------------ */ +----------- */ //> defmod is "command" on OS X and "ctrl" elsewhere -//Advanced Example: 'defmod-k j' -> For this to fire you have to first press both ctrl and k, and then j. +//Advanced Example: 'defmod-k j' -> For this to fire you have to first press both ctrl and k, and then j. const keybinds = { // 'key(s)' : 'function', - 'defmod-shift-z' : 'clearWhiteboard', - 'defmod-z' : 'undoStep', - 'defmod-y' : 'redoStep', - 'defmod-x' : 'setTool_recSelect', - 'defmod-m' : 'setTool_mouse', - 'defmod-p' : 'setTool_pen', - 'defmod-l' : 'setTool_line', - 'defmod-r' : 'setTool_rect', - 'defmod-c' : 'setTool_circle', - 'defmod-shift-f' : 'toggleLineRecCircle', - 'defmod-shift-x' : 'togglePenEraser', - 'defmod-shift-r' : 'toggleMainColors', - 'defmod-a' : 'setTool_text', - 'defmod-e' : 'setTool_eraser', - 'defmod-up' : 'thickness_bigger', - 'defmod-down' : 'thickness_smaller', - 'defmod-shift-c' : 'openColorPicker', - 'defmod-shift-1' : 'setDrawColorBlack', - 'defmod-shift-2' : 'setDrawColorBlue', - 'defmod-shift-3' : 'setDrawColorGreen', - 'defmod-shift-4' : 'setDrawColorYellow', - 'defmod-shift-5' : 'setDrawColorRed', - 'defmod-s' : 'saveWhiteboardAsImage', - 'defmod-shift-k' : 'saveWhiteboardAsJson', - 'defmod-shift-i' : 'uploadWhiteboardToWebDav', - 'defmod-shift-j' : 'uploadJsonToWhiteboard', - 'defmod-shift-s' : 'shareWhiteboard', - 'tab' : 'hideShowControls', - 'up' : 'moveDraggableUp', - 'down' : 'moveDraggableDown', - 'left' : 'moveDraggableLeft', - 'right' : 'moveDraggableRight', - 'defmod-enter' : 'dropDraggable', - 'shift-enter' : 'addToBackground', - 'escape' : 'cancelAllActions', - 'del' : 'deleteSelection' -} + "defmod-shift-z": "clearWhiteboard", + "defmod-z": "undoStep", + "defmod-y": "redoStep", + "defmod-x": "setTool_recSelect", + "defmod-m": "setTool_mouse", + "defmod-p": "setTool_pen", + "defmod-l": "setTool_line", + "defmod-r": "setTool_rect", + "defmod-c": "setTool_circle", + "defmod-shift-f": "toggleLineRecCircle", + "defmod-shift-x": "togglePenEraser", + "defmod-shift-r": "toggleMainColors", + "defmod-a": "setTool_text", + "defmod-e": "setTool_eraser", + "defmod-up": "thickness_bigger", + "defmod-down": "thickness_smaller", + "defmod-shift-c": "openColorPicker", + "defmod-shift-1": "setDrawColorBlack", + "defmod-shift-2": "setDrawColorBlue", + "defmod-shift-3": "setDrawColorGreen", + "defmod-shift-4": "setDrawColorYellow", + "defmod-shift-5": "setDrawColorRed", + "defmod-s": "saveWhiteboardAsImage", + "defmod-shift-k": "saveWhiteboardAsJson", + "defmod-shift-i": "uploadWhiteboardToWebDav", + "defmod-shift-j": "uploadJsonToWhiteboard", + "defmod-shift-s": "shareWhiteboard", + tab: "hideShowControls", + up: "moveDraggableUp", + down: "moveDraggableDown", + left: "moveDraggableLeft", + right: "moveDraggableRight", + "defmod-enter": "dropDraggable", + "shift-enter": "addToBackground", + escape: "cancelAllActions", + del: "deleteSelection", +}; -export default keybinds; \ No newline at end of file +export default keybinds; diff --git a/src/js/main.js b/src/js/main.js index 7fa022c..221ecfd 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,5 +1,5 @@ import keymage from "keymage"; -import io from 'socket.io-client'; +import io from "socket.io-client"; import whiteboard from "./whiteboard"; import keybinds from "./keybinds"; import Picker from "vanilla-picker"; @@ -9,13 +9,16 @@ import shortcutFunctions from "./shortcutFunctions"; import ReadOnlyService from "./services/ReadOnlyService"; function main() { - var whiteboardId = getQueryVariable("whiteboardid"); var randomid = getQueryVariable("randomid"); - if (randomid && !whiteboardId) { //set random whiteboard on empty whiteboardid - whiteboardId = Array(2).fill(null).map(() => Math.random().toString(36).substr(2)).join(''); + if (randomid && !whiteboardId) { + //set random whiteboard on empty whiteboardid + whiteboardId = Array(2) + .fill(null) + .map(() => Math.random().toString(36).substr(2)) + .join(""); const urlParams = new URLSearchParams(window.location.search); - urlParams.set('whiteboardid', whiteboardId); + urlParams.set("whiteboardid", whiteboardId); window.location.search = urlParams; } @@ -33,40 +36,44 @@ function main() { document.title = decodeURIComponent(title); } - var url = document.URL.substr(0, document.URL.lastIndexOf('/')); + var url = document.URL.substr(0, document.URL.lastIndexOf("/")); var signaling_socket = null; var urlSplit = url.split("/"); var subdir = ""; for (var i = 3; i < urlSplit.length; i++) { - subdir = subdir + '/' + urlSplit[i]; + subdir = subdir + "/" + urlSplit[i]; } - signaling_socket = io("", { "path": subdir + "/ws-api" }); // Connect even if we are in a subdir behind a reverse proxy + signaling_socket = io("", { path: subdir + "/ws-api" }); // Connect even if we are in a subdir behind a reverse proxy - signaling_socket.on('connect', function () { + signaling_socket.on("connect", function () { console.log("Websocket connected!"); let messageReceivedCount = 0; - signaling_socket.on('drawToWhiteboard', function (content) { + signaling_socket.on("drawToWhiteboard", function (content) { whiteboard.handleEventsAndData(content, true); - $('#messageReceivedCount')[0].innerText = String(messageReceivedCount++); + $("#messageReceivedCount")[0].innerText = String(messageReceivedCount++); }); - signaling_socket.on('refreshUserBadges', function () { + signaling_socket.on("refreshUserBadges", function () { whiteboard.refreshUserBadges(); }); - signaling_socket.on('wrongAccessToken', function () { + signaling_socket.on("wrongAccessToken", function () { if (!accessDenied) { accessDenied = true; - showBasicAlert("Access denied! Wrong accessToken!") + showBasicAlert("Access denied! Wrong accessToken!"); } }); - signaling_socket.on('updateSmallestScreenResolution', function (widthHeight) { + signaling_socket.on("updateSmallestScreenResolution", function (widthHeight) { whiteboard.updateSmallestScreenResolution(widthHeight["w"], widthHeight["h"]); }); - signaling_socket.emit('joinWhiteboard', { wid: whiteboardId, at: accessToken, windowWidthHeight: { w: $(window).width(), h: $(window).height() } }); + signaling_socket.emit("joinWhiteboard", { + wid: whiteboardId, + at: accessToken, + windowWidthHeight: { w: $(window).width(), h: $(window).height() }, + }); }); $(document).ready(function () { @@ -78,29 +85,35 @@ function main() { } let messageSentCount = 0; - whiteboard.loadWhiteboard("#whiteboardContainer", { //Load the whiteboard + whiteboard.loadWhiteboard("#whiteboardContainer", { + //Load the whiteboard whiteboardId: whiteboardId, username: btoa(myUsername), sendFunction: function (content) { if (ReadOnlyService.readOnlyActive) return; - //ADD IN LATER THROUGH CONFIG - // if (content.t === 'cursor') { + //ADD IN LATER THROUGH CONFIG + // if (content.t === 'cursor') { // if (whiteboard.drawFlag) return; // } content["at"] = accessToken; - signaling_socket.emit('drawToWhiteboard', content); - $('#messageSentCount')[0].innerText = String(messageSentCount++); - } + signaling_socket.emit("drawToWhiteboard", content); + $("#messageSentCount")[0].innerText = String(messageSentCount++); + }, }); // request whiteboard from server - $.get(subdir + "/api/loadwhiteboard", { wid: whiteboardId, at: accessToken }).done(function (data) { - whiteboard.loadData(data) - }); + $.get(subdir + "/api/loadwhiteboard", { wid: whiteboardId, at: accessToken }).done( + function (data) { + whiteboard.loadData(data); + } + ); $(window).resize(function () { - signaling_socket.emit('updateScreenResolution', { at: accessToken, windowWidthHeight: { w: $(window).width(), h: $(window).height() } }); - }) + signaling_socket.emit("updateScreenResolution", { + at: accessToken, + windowWidthHeight: { w: $(window).width(), h: $(window).height() }, + }); + }); /*----------------/ Whiteboard actions @@ -115,12 +128,18 @@ function main() { tempLineTool = true; whiteboard.ownCursor.hide(); if (whiteboard.drawFlag) { - whiteboard.mouseup({ offsetX: whiteboard.prevPos.x, offsetY: whiteboard.prevPos.y }) + whiteboard.mouseup({ + offsetX: whiteboard.prevPos.x, + offsetY: whiteboard.prevPos.y, + }); shortcutFunctions.setTool_line(); - whiteboard.mousedown({ offsetX: whiteboard.prevPos.x, offsetY: whiteboard.prevPos.y }) + whiteboard.mousedown({ + offsetX: whiteboard.prevPos.x, + offsetY: whiteboard.prevPos.y, + }); } else { shortcutFunctions.setTool_line(); - } + } } whiteboard.pressedKeys["shift"] = true; //Used for straight lines... } else if (e.which == 17) { @@ -147,9 +166,15 @@ function main() { if (associatedShortcutFunction) { keymage(key, associatedShortcutFunction, { preventDefault: true }); } else { - console.error("Function you want to keybind on key:", key, "named:", functionName, "is not available!") + console.error( + "Function you want to keybind on key:", + key, + "named:", + functionName, + "is not available!" + ); } - }) + }); // whiteboard clear button $("#whiteboardTrashBtn").click(function () { @@ -211,15 +236,18 @@ function main() { $("#saveAsImageBtn").click(function () { var imgData = whiteboard.getImageDataBase64(); - var w = window.open('about:blank'); //Firefox will not allow downloads without extra window - setTimeout(function () { //FireFox seems to require a setTimeout for this to work. - var a = document.createElement('a'); + var w = window.open("about:blank"); //Firefox will not allow downloads without extra window + setTimeout(function () { + //FireFox seems to require a setTimeout for this to work. + var a = document.createElement("a"); a.href = imgData; - a.download = 'whiteboard.png'; + a.download = "whiteboard.png"; w.document.body.appendChild(a); a.click(); w.document.body.removeChild(a); - setTimeout(function () { w.close(); }, 100); + setTimeout(function () { + w.close(); + }, 100); }, 0); }); @@ -227,15 +255,18 @@ function main() { $("#saveAsJSONBtn").click(function () { var imgData = whiteboard.getImageDataJson(); - var w = window.open('about:blank'); //Firefox will not allow downloads without extra window - setTimeout(function () { //FireFox seems to require a setTimeout for this to work. - var a = document.createElement('a'); - a.href = window.URL.createObjectURL(new Blob([imgData], { type: 'text/json' })); - a.download = 'whiteboard.json'; + var w = window.open("about:blank"); //Firefox will not allow downloads without extra window + setTimeout(function () { + //FireFox seems to require a setTimeout for this to work. + var a = document.createElement("a"); + a.href = window.URL.createObjectURL(new Blob([imgData], { type: "text/json" })); + a.download = "whiteboard.json"; w.document.body.appendChild(a); a.click(); w.document.body.removeChild(a); - setTimeout(function () { w.close(); }, 100); + setTimeout(function () { + w.close(); + }, 100); }, 0); }); @@ -244,57 +275,67 @@ function main() { return; } - var webdavserver = localStorage.getItem('webdavserver') || "" - var webdavpath = localStorage.getItem('webdavpath') || "/" - var webdavusername = localStorage.getItem('webdavusername') || "" - var webdavpassword = localStorage.getItem('webdavpassword') || "" - var webDavHtml = $('
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Server URL:
Path:path always have to start & end with "/"
Username:
Password:
Note: You have to generate and use app credentials if you have 2 Factor Auth activated on your dav/nextcloud server!
' + - '
'); + var webdavserver = localStorage.getItem("webdavserver") || ""; + var webdavpath = localStorage.getItem("webdavpath") || "/"; + var webdavusername = localStorage.getItem("webdavusername") || ""; + var webdavpassword = localStorage.getItem("webdavpassword") || ""; + var webDavHtml = $( + "
" + + "" + + "" + + "" + + '' + + "" + + "" + + "" + + "" + + '' + + '' + + "" + + "" + + "" + + '' + + '' + + "" + + "" + + "" + + '' + + '' + + "" + + "" + + '' + + "" + + "" + + "" + + '' + + "" + + "
Server URL:
Path:path always have to start & end with "/"
Username:
Password:
Note: You have to generate and use app credentials if you have 2 Factor Auth activated on your dav/nextcloud server!
" + + "
" + ); webDavHtml.find(".webdavUploadBtn").click(function () { var webdavserver = webDavHtml.find(".webdavserver").val(); - localStorage.setItem('webdavserver', webdavserver); + localStorage.setItem("webdavserver", webdavserver); var webdavpath = webDavHtml.find(".webdavpath").val(); - localStorage.setItem('webdavpath', webdavpath); + localStorage.setItem("webdavpath", webdavpath); var webdavusername = webDavHtml.find(".webdavusername").val(); - localStorage.setItem('webdavusername', webdavusername); + localStorage.setItem("webdavusername", webdavusername); var webdavpassword = webDavHtml.find(".webdavpassword").val(); - localStorage.setItem('webdavpassword', webdavpassword); + localStorage.setItem("webdavpassword", webdavpassword); var base64data = whiteboard.getImageDataBase64(); var webdavaccess = { webdavserver: webdavserver, webdavpath: webdavpath, webdavusername: webdavusername, - webdavpassword: webdavpassword - } + webdavpassword: webdavpassword, + }; webDavHtml.find(".loadingWebdavText").show(); webDavHtml.find(".webdavUploadBtn").hide(); saveWhiteboardToWebdav(base64data, webdavaccess, function (err) { @@ -305,12 +346,12 @@ function main() { webDavHtml.parents(".basicalert").remove(); } }); - }) + }); showBasicAlert(webDavHtml, { header: "Save to Webdav", okBtnText: "cancel", - headercolor: "#0082c9" - }) + headercolor: "#0082c9", + }); // render newly added icons dom.i2svg(); }); @@ -330,10 +371,15 @@ function main() { endSplit = endSplit.splice(1, 1); urlStart += "&" + endSplit.join("&"); } - $("