Index: lams_build/conf/whiteboard/src/js/main.js =================================================================== diff -u --- lams_build/conf/whiteboard/src/js/main.js (revision 0) +++ lams_build/conf/whiteboard/src/js/main.js (revision b4fd3143059f7c7c237c5a4631daefa29dd48ac4) @@ -0,0 +1,951 @@ +import keymage from "keymage"; +import io from "socket.io-client"; +import whiteboard from "./whiteboard"; +import keybinds from "./keybinds"; +import Picker from "vanilla-picker"; +import { dom } from "@fortawesome/fontawesome-svg-core"; +import shortcutFunctions from "./shortcutFunctions"; +import ReadOnlyService from "./services/ReadOnlyService"; +import InfoService from "./services/InfoService"; +import { getSubDir } from "./utils"; +import ConfigService from "./services/ConfigService"; +import { v4 as uuidv4 } from "uuid"; + +const pdfjsLib = require("pdfjs-dist"); + +const urlParams = new URLSearchParams(window.location.search); +let whiteboardId = urlParams.get("whiteboardid"); +const randomid = urlParams.get("randomid"); + +if (randomid) { + whiteboardId = uuidv4(); + urlParams.delete("randomid"); + window.location.search = urlParams; +} + +if (!whiteboardId) { + whiteboardId = "myNewWhiteboard"; +} + +whiteboardId = unescape(encodeURIComponent(whiteboardId)).replace(/[^a-zA-Z0-9\-]/g, ""); + +if (urlParams.get("whiteboardid") !== whiteboardId) { + urlParams.set("whiteboardid", whiteboardId); + window.location.search = urlParams; +} + +const myUsername = urlParams.get("username") || "unknown" + (Math.random() + "").substring(2, 6); +const accessToken = urlParams.get("accesstoken") || ""; +const copyfromwid = urlParams.get("copyfromwid") || ""; + +// Custom Html Title +const title = urlParams.get("title"); +if (title) { + document.title = decodeURIComponent(title); +} + +const subdir = getSubDir(); +let signaling_socket; + +function main() { + signaling_socket = io("", { path: subdir + "/ws-api" }); // Connect even if we are in a subdir behind a reverse proxy + + signaling_socket.on("connect", function () { + console.log("Websocket connected!"); + + signaling_socket.on("whiteboardConfig", (serverResponse) => { + ConfigService.initFromServer(serverResponse); + // Inti whiteboard only when we have the config from the server + initWhiteboard(); + }); + + signaling_socket.on("whiteboardInfoUpdate", (info) => { + InfoService.updateInfoFromServer(info); + whiteboard.updateSmallestScreenResolution(); + }); + + signaling_socket.on("drawToWhiteboard", function (content) { + whiteboard.handleEventsAndData(content, true); + InfoService.incrementNbMessagesReceived(); + }); + + signaling_socket.on("refreshUserBadges", function () { + whiteboard.refreshUserBadges(); + }); + + let accessDenied = false; + signaling_socket.on("wrongAccessToken", function () { + if (!accessDenied) { + accessDenied = true; + showBasicAlert("Access denied! Wrong accessToken!"); + } + }); + + signaling_socket.emit("joinWhiteboard", { + wid: whiteboardId, + at: accessToken, + windowWidthHeight: { w: $(window).width(), h: $(window).height() }, + }); + }); +} + +function showBasicAlert(html, newOptions) { + var options = { + header: "INFO MESSAGE", + okBtnText: "Ok", + headercolor: "#d25d5d", + hideAfter: false, + onOkClick: false, + }; + if (newOptions) { + for (var i in newOptions) { + options[i] = newOptions[i]; + } + } + var alertHtml = $( + '
' + + '
' + + '
' + + options["header"] + + '
x
' + + '
' + + '
" + + "
" + + "
" + ); + alertHtml.find(".htmlcontent").append(html); + $("body").append(alertHtml); + alertHtml + .find(".okbtn") + .off("click") + .click(function () { + if (options.onOkClick) { + options.onOkClick(); + } + alertHtml.remove(); + }); + alertHtml + .find(".closeAlert") + .off("click") + .click(function () { + alertHtml.remove(); + }); + + if (options.hideAfter) { + setTimeout(function () { + alertHtml.find(".okbtn").click(); + }, 1000 * options.hideAfter); + } +} + +function initWhiteboard() { + $(document).ready(function () { + // by default set in readOnly mode + ReadOnlyService.activateReadOnlyMode(); + + if (urlParams.get("webdav") === "true") { + $("#uploadWebDavBtn").show(); + } + + whiteboard.loadWhiteboard("#whiteboardContainer", { + //Load the whiteboard + whiteboardId: whiteboardId, + username: btoa(myUsername), + backgroundGridUrl: "./images/" + ConfigService.backgroundGridImage, + sendFunction: function (content) { + if (ReadOnlyService.readOnlyActive) return; + //ADD IN LATER THROUGH CONFIG + // if (content.t === 'cursor') { + // if (whiteboard.drawFlag) return; + // } + content["at"] = accessToken; + signaling_socket.emit("drawToWhiteboard", content); + InfoService.incrementNbMessagesSent(); + }, + }); + + // request whiteboard from server + $.get(subdir + "/api/loadwhiteboard", { wid: whiteboardId, at: accessToken }).done( + function (data) { + // modified for LAMS, allow cloning whiteboard even when it is not empty + // also force original data to be drawn first + if (copyfromwid) { + //Copy from witheboard if current is empty and get parameter is given + $.get(subdir + "/api/loadwhiteboard", { + wid: copyfromwid, + at: accessToken, + }).done(function (originalData) { + // make sure all images are in the background, otherwise they obscure learners' drawings + originalData.forEach((drawItem) => {drawItem.draw = 1}); + console.log(originalData); + console.log(data); + whiteboard.loadData(originalData); + whiteboard.loadData(data); + }); + } else { + console.log(data); + whiteboard.loadData(data); + } + + } + ); + + $(window).resize(function () { + signaling_socket.emit("updateScreenResolution", { + at: accessToken, + windowWidthHeight: { w: $(window).width(), h: $(window).height() }, + }); + }); + + /*----------------/ + Whiteboard actions + /----------------*/ + + var tempLineTool = false; + var strgPressed = false; + //Handle key actions + $(document).on("keydown", function (e) { + if (e.which == 16) { + if (whiteboard.tool == "pen" && !strgPressed) { + tempLineTool = true; + whiteboard.ownCursor.hide(); + if (whiteboard.drawFlag) { + whiteboard.mouseup({ + offsetX: whiteboard.prevPos.x, + offsetY: whiteboard.prevPos.y, + }); + shortcutFunctions.setTool_line(); + 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) { + strgPressed = true; + } + //console.log(e.which); + }); + $(document).on("keyup", function (e) { + if (e.which == 16) { + if (tempLineTool) { + tempLineTool = false; + shortcutFunctions.setTool_pen(); + whiteboard.ownCursor.show(); + } + whiteboard.pressedKeys["shift"] = false; + } else if (e.which == 17) { + strgPressed = false; + } + }); + + //Load keybindings from keybinds.js to given functions + Object.entries(keybinds).forEach(([key, functionName]) => { + const associatedShortcutFunction = shortcutFunctions[functionName]; + if (associatedShortcutFunction) { + keymage(key, associatedShortcutFunction, { preventDefault: true }); + } else { + console.error( + "Function you want to keybind on key:", + key, + "named:", + functionName, + "is not available!" + ); + } + }); + + // whiteboard clear button + $("#whiteboardTrashBtn") + .off("click") + .click(function () { + $("#whiteboardTrashBtnConfirm").show().focus(); + $(this).hide(); + }); + + $("#whiteboardTrashBtnConfirm").mouseout(function () { + $(this).hide(); + $("#whiteboardTrashBtn").show(); + }); + + $("#whiteboardTrashBtnConfirm") + .off("click") + .click(function () { + $(this).hide(); + $("#whiteboardTrashBtn").show(); + whiteboard.clearWhiteboard(); + }); + + // undo button + $("#whiteboardUndoBtn") + .off("click") + .click(function () { + whiteboard.undoWhiteboardClick(); + }); + + // redo button + $("#whiteboardRedoBtn") + .off("click") + .click(function () { + whiteboard.redoWhiteboardClick(); + }); + + // view only + $("#whiteboardLockBtn") + .off("click") + .click(() => { + ReadOnlyService.deactivateReadOnlyMode(); + }); + $("#whiteboardUnlockBtn") + .off("click") + .click(() => { + ReadOnlyService.activateReadOnlyMode(); + }); + $("#whiteboardUnlockBtn").hide(); + $("#whiteboardLockBtn").show(); + + // switch tool + $(".whiteboard-tool") + .off("click") + .click(function () { + $(".whiteboard-tool").removeClass("active"); + $(this).addClass("active"); + var activeTool = $(this).attr("tool"); + whiteboard.setTool(activeTool); + if (activeTool == "mouse" || activeTool == "recSelect") { + $(".activeToolIcon").empty(); + } else { + $(".activeToolIcon").html($(this).html()); //Set Active icon the same as the button icon + } + + if (activeTool == "text" || activeTool == "stickynote") { + $("#textboxBackgroundColorPickerBtn").show(); + } else { + $("#textboxBackgroundColorPickerBtn").hide(); + } + }); + + // upload image button + $("#addImgToCanvasBtn") + .off("click") + .click(function () { + if (ReadOnlyService.readOnlyActive) return; + showBasicAlert("Please drag the image into the browser."); + }); + + // save image as imgae + $("#saveAsImageBtn") + .off("click") + .click(function () { + whiteboard.getImageDataBase64( + { + imageFormat: ConfigService.imageDownloadFormat, + drawBackgroundGrid: ConfigService.drawBackgroundGrid, + }, + function (imgData) { + 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." + ConfigService.imageDownloadFormat; + w.document.body.appendChild(a); + a.click(); + w.document.body.removeChild(a); + setTimeout(function () { + w.close(); + }, 100); + }, 0); + } + ); + }); + + // save image to json containing steps + $("#saveAsJSONBtn") + .off("click") + .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"; + w.document.body.appendChild(a); + a.click(); + w.document.body.removeChild(a); + setTimeout(function () { + w.close(); + }, 100); + }, 0); + }); + + $("#uploadWebDavBtn") + .off("click") + .click(function () { + if ($(".webdavUploadBtn").length > 0) { + 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!
" + + "
" + ); + webDavHtml + .find(".webdavUploadBtn") + .off("click") + .click(function () { + var webdavserver = webDavHtml.find(".webdavserver").val(); + localStorage.setItem("webdavserver", webdavserver); + var webdavpath = webDavHtml.find(".webdavpath").val(); + localStorage.setItem("webdavpath", webdavpath); + var webdavusername = webDavHtml.find(".webdavusername").val(); + localStorage.setItem("webdavusername", webdavusername); + var webdavpassword = webDavHtml.find(".webdavpassword").val(); + localStorage.setItem("webdavpassword", webdavpassword); + whiteboard.getImageDataBase64( + { + imageFormat: ConfigService.imageDownloadFormat, + drawBackgroundGrid: ConfigService.drawBackgroundGrid, + }, + function (base64data) { + var webdavaccess = { + webdavserver: webdavserver, + webdavpath: webdavpath, + webdavusername: webdavusername, + webdavpassword: webdavpassword, + }; + webDavHtml.find(".loadingWebdavText").show(); + webDavHtml.find(".webdavUploadBtn").hide(); + saveWhiteboardToWebdav(base64data, webdavaccess, function (err) { + if (err) { + webDavHtml.find(".loadingWebdavText").hide(); + webDavHtml.find(".webdavUploadBtn").show(); + } else { + webDavHtml.parents(".basicalert").remove(); + } + }); + } + ); + }); + showBasicAlert(webDavHtml, { + header: "Save to Webdav", + okBtnText: "cancel", + headercolor: "#0082c9", + }); + // render newly added icons + dom.i2svg(); + }); + + // upload json containing steps + $("#uploadJsonBtn") + .off("click") + .click(function () { + $("#myFile").click(); + }); + + $("#shareWhiteboardBtn") + .off("click") + .click(() => { + function urlToClipboard(whiteboardId = null) { + const { protocol, host, pathname, search } = window.location; + const basePath = `${protocol}//${host}${pathname}`; + const getParams = new URLSearchParams(search); + + // Clear ursername from get parameters + getParams.delete("username"); + + if (whiteboardId) { + // override whiteboardId value in URL + getParams.set("whiteboardid", whiteboardId); + } + + const url = `${basePath}?${getParams.toString()}`; + $("