From 985c44373f239302458e0f74465ea6178d039b0b Mon Sep 17 00:00:00 2001 From: Elijah Bodden <106613755+Elijah-Bodden@users.noreply.github.com> Date: Thu, 11 Aug 2022 10:08:29 -0500 Subject: [PATCH] Upload mobile-ready index.js --- src/source/frontend/src/index.js | 900 +++++++++++++++---------------- 1 file changed, 441 insertions(+), 459 deletions(-) diff --git a/src/source/frontend/src/index.js b/src/source/frontend/src/index.js index 8428b26..48af995 100644 --- a/src/source/frontend/src/index.js +++ b/src/source/frontend/src/index.js @@ -1,3 +1,5 @@ +/** @format */ + import Sigma from "sigma"; import Graph from "graphology"; import circular from "graphology-layout/circular"; @@ -8,77 +10,80 @@ import EdgeProgram from "./programs/edge.antialias"; import { animateNodes } from "sigma/utils/animate"; class modifierHandler { - constructor() { - this.SHIFT = false; - this.CTRL = false; - this.FUNCTION = false; - this.ALT = false; - this.toggleValue = async function (event, bool) { - switch (event.key) { - case "Alt": - this.ALT = bool; - break; - case "Control": - this.CTRL = bool; - break; - case "Fn": - this.FUNCTION = bool; - break; - case "Shift": - this.SHIFT = bool; - break; - } - }; - window.onkeydown = async (event) => { - this.toggleValue(event, true); - }; - window.onkeyup = async (event) => { - this.toggleValue(event, false); - }; - } + constructor() { + this.SHIFT = false; + this.CTRL = false; + this.FUNCTION = false; + this.ALT = false; + this.toggleValue = async function (event, bool) { + switch (event.key) { + case "Alt": + this.ALT = bool; + break; + case "Control": + this.CTRL = bool; + break; + case "Fn": + this.FUNCTION = bool; + break; + case "Shift": + this.SHIFT = bool; + break; + } + }; + window.onkeydown = async (event) => { + this.toggleValue(event, true); + }; + window.onkeyup = async (event) => { + this.toggleValue(event, false); + }; + } } const statusColors = { - Neutral: "#5D5A6D", - Selected: "#444155", - AuthNeutral: "#8B7FD3", - AuthSelected: "#5B4EA1", - ErrorNeutral: "#B41E06", - ErrorSelected: "#a30006", - SuccessNeutral: "#178A0E", - SuccessSelected: "#47A13F", + Neutral: "#5D5A6D", + Selected: "#444155", + AuthNeutral: "#8B7FD3", + AuthSelected: "#5B4EA1", + ErrorNeutral: "#B41E06", + ErrorSelected: "#a30006", + SuccessNeutral: "#178A0E", + SuccessSelected: "#47A13F", }; + +var lastClickTimestamp; +var recentlyClickedNode; const mod = new modifierHandler(); const container = document.getElementById("graphContainer"); const suggestionsList = document.querySelector("#search-opts"); const nodeDismissHandler = new eventHandlingMechanism(); const state = { - activeNode: undefined, - searchQuery: "", - suggestions: [], - errorNodes: [], - successNodes: [], - renderedNodes: [], - renderedLinks: [], + activeNode: undefined, + searchQuery: "", + suggestions: [], + errorNodes: [], + successNodes: [], + renderedNodes: [], + renderedLinks: [], }; const graphData = { - options: { - allowSelfLoops: false, - multi: false, - type: "mixed", - }, - nodes: [ - { - key: "server", - attributes: { - size: 15, - label: "Server", - type: "server", - color: statusColors.Neutral, - }, - }, - ], - edges: [], + options: { + allowSelfLoops: false, + multi: false, + type: "mixed", + }, + nodes: [ + { + key: "server", + attributes: { + size: 15, + label: "Server", + type: "server", + color: statusColors.Neutral, + }, + }, + ], + edges: [], }; const graph = new Graph(); @@ -86,449 +91,426 @@ graph.import(graphData); circular.assign(graph); const renderer = new Sigma(graph, container, { - renderLabels: false, - minCameraRatio: 0.1, - maxCameraRatio: 3, - nodeProgramClasses: { - related: relatedProgram, - unrelated: unrelatedProgram, - server: serverProgram, - }, - edgeProgramClasses: { - antialias: EdgeProgram, - }, + renderLabels: false, + minCameraRatio: 0.1, + maxCameraRatio: 3, + allowInvalidContainer: true, + nodeProgramClasses: { + related: relatedProgram, + unrelated: unrelatedProgram, + server: serverProgram, + }, + edgeProgramClasses: { + antialias: EdgeProgram, + }, }); const camera = renderer.getCamera(); camera.addListener("updated", async (_) => { - if (camera.x > camera.ratio + 1) camera.x = camera.ratio + 1; - if (camera.x < -camera.ratio) camera.x = -camera.ratio; - if (camera.y > camera.ratio + 1) camera.y = camera.ratio + 1; - if (camera.y < -camera.ratio) camera.y = -camera.ratio; + if (camera.x > camera.ratio + 1) camera.x = camera.ratio + 1; + if (camera.x < -camera.ratio) camera.x = -camera.ratio; + if (camera.y > camera.ratio + 1) camera.y = camera.ratio + 1; + if (camera.y < -camera.ratio) camera.y = -camera.ratio; }); async function blinkStatus(statusIdentifier, node) { - switch (statusIdentifier.toLowerCase()) { - case "error": - state.errorNodes.push(node); - setTimeout(() => { - state.errorNodes.splice(state.errorNodes.indexOf(node), 1); - renderer.refresh(); - }, 1500); - break; - case "success": - state.successNodes.push(node); - setTimeout(() => { - state.successNodes.splice(state.successNodes.indexOf(node), 1); - renderer.refresh(); - }, 1500); - break; - } - renderer.refresh(); + switch (statusIdentifier.toLowerCase()) { + case "error": + state.errorNodes.push(node); + setTimeout(() => { + state.errorNodes.splice(state.errorNodes.indexOf(node), 1); + renderer.refresh(); + }, 5000); + break; + case "success": + state.successNodes.push(node); + setTimeout(() => { + state.successNodes.splice(state.successNodes.indexOf(node), 1); + renderer.refresh(); + }, 5000); + break; + } + renderer.refresh(); } renderer.setSetting("nodeReducer", (node, data) => { - const res = { ...data }; - const active = state.activeNode === node; - const error = state.errorNodes.includes(node); - const success = state.successNodes.includes(node); - const prepend = - error && success - ? error - : !(success || error) - ? "" - : ["Success", "Error"][[success, error].indexOf(true)]; - const fullStatusString = - prepend + ["Selected", "Neutral"][[active, !active].indexOf(true)]; - res.color = statusColors[fullStatusString]; - return res; + const res = { ...data }; + const active = state.activeNode === node; + const error = state.errorNodes.includes(node); + const success = state.successNodes.includes(node); + const prepend = + error && success + ? error + : !(success || error) + ? "" + : ["Success", "Error"][[success, error].indexOf(true)]; + const fullStatusString = prepend + ["Selected", "Neutral"][[active, !active].indexOf(true)]; + res.color = statusColors[fullStatusString]; + return res; }); renderer.setSetting("edgeReducer", (edge, data) => { - const res = { ...data }; - var prependable = graph.extremities(edge).reduce((last, extremity) => { - return state.errorNodes.includes(extremity) - ? "Error" - : state.successNodes.includes(extremity) && last != "Error" - ? "Success" - : last; - }, ""); - if (!prependable) { - if ( - [1, 0] - .map((flippedIndex, trueIndex) => { - return ( - authPeers.includes(graph.extremities(edge)[flippedIndex]) && - graph.extremities(edge)[trueIndex] === - CONFIG.communication.hiddenAlias - ); - }) - .includes(true) - ) { - prependable = "Auth"; - } - } - if (state.activeNode) { - res.color = "#d9dde0"; - if (graph.hasExtremity(edge, state.activeNode)) { - res.color = statusColors[prependable + "Selected"]; - } - return res; - } - res.color = statusColors[prependable + "Neutral"]; - return res; + const res = { ...data }; + var prependable = graph.extremities(edge).reduce((last, extremity) => { + return state.errorNodes.includes(extremity) + ? "Error" + : state.successNodes.includes(extremity) && last != "Error" + ? "Success" + : last; + }, ""); + if (!prependable) { + if ( + [1, 0] + .map((flippedIndex, trueIndex) => { + return ( + authPeers.includes(graph.extremities(edge)[flippedIndex]) && + graph.extremities(edge)[trueIndex] === CONFIG.communication.hiddenAlias + ); + }) + .includes(true) + ) { + prependable = "Auth"; + } + } + if (state.activeNode) { + res.color = "#d9dde0"; + if (graph.hasExtremity(edge, state.activeNode)) { + res.color = statusColors[prependable + "Selected"]; + } + return res; + } + res.color = statusColors[prependable + "Neutral"]; + return res; }); function setSearchQuery(query) { - if (!CONFIG.UI.renderUnfamiliarPublicAliases) { - suggestionsList.innerHTML = ""; - return; - } - state.searchQuery = query; - if (query) { - const lcQuery = escapeHTML(query.toLowerCase()); - let suggestions = Object.keys(networkMap.nodes).filter((node) => { - try { - return hiddenAliasLookup[node].toLowerCase().indexOf(lcQuery) != -1; - } catch { - return false; - } - }); - let sortedSuggestions = suggestions.reduce((sum, suggestion) => { - const index = hiddenAliasLookup[suggestion] - .toLowerCase() - .indexOf(lcQuery); - sum[index] = sum[index] ? sum[index] : []; - sum[index].push(suggestion); - return sum; - }, {}); - var suggestionPriority = []; - var suggestionPairs = {}; - for (let len in sortedSuggestions) { - sortedSuggestions[len] = sortedSuggestions[len].sort((a, b) => { - a = hiddenAliasLookup[a].toLowerCase(); - b = hiddenAliasLookup[b].toLowerCase(); - return a === b ? 0 : a > b ? 1 : -1; - }); - for (let suggestion of sortedSuggestions[len]) { - suggestionPriority.push(suggestion); - let firstIndex = hiddenAliasLookup[suggestion] - .toLowerCase() - .indexOf(lcQuery); - const boldedSuggestion = - hiddenAliasLookup[suggestion].slice(0, firstIndex) + - `${hiddenAliasLookup[suggestion].slice( - firstIndex, - firstIndex + lcQuery.length - )}` + - hiddenAliasLookup[suggestion].slice(firstIndex + lcQuery.length); - suggestionPairs[suggestion] = boldedSuggestion; - } - } - renderQuerySuggestions(suggestionPairs, suggestionPriority, true); - if ( - suggestionPriority.length === 1 && - hiddenAliasLookup[suggestionPriority[0]].toLowerCase() === query - ) { - activateNode(suggestionPriority[0]); - } else { - state.selectedNode = undefined; - } - } else { - renderQuerySuggestions(); - } + if (!CONFIG.UI.renderUnfamiliarPublicAliases) { + suggestionsList.innerHTML = ""; + return; + } + state.searchQuery = query; + if (query) { + const lcQuery = escapeHTML(query.toLowerCase()); + let suggestions = Object.keys(networkMap.nodes).filter((node) => { + try { + return hiddenAliasLookup[node].toLowerCase().indexOf(lcQuery) != -1; + } catch { + return false; + } + }); + let sortedSuggestions = suggestions.reduce((sum, suggestion) => { + const index = hiddenAliasLookup[suggestion].toLowerCase().indexOf(lcQuery); + sum[index] = sum[index] ? sum[index] : []; + sum[index].push(suggestion); + return sum; + }, {}); + var suggestionPriority = []; + var suggestionPairs = {}; + for (let len in sortedSuggestions) { + sortedSuggestions[len] = sortedSuggestions[len].sort((a, b) => { + a = hiddenAliasLookup[a].toLowerCase(); + b = hiddenAliasLookup[b].toLowerCase(); + return a === b ? 0 : a > b ? 1 : -1; + }); + for (let suggestion of sortedSuggestions[len]) { + suggestionPriority.push(suggestion); + let firstIndex = hiddenAliasLookup[suggestion].toLowerCase().indexOf(lcQuery); + const boldedSuggestion = + hiddenAliasLookup[suggestion].slice(0, firstIndex) + + `${hiddenAliasLookup[suggestion].slice(firstIndex, firstIndex + lcQuery.length)}` + + hiddenAliasLookup[suggestion].slice(firstIndex + lcQuery.length); + suggestionPairs[suggestion] = boldedSuggestion; + } + } + renderQuerySuggestions(suggestionPairs, suggestionPriority, true); + if ( + suggestionPriority.length === 1 && + hiddenAliasLookup[suggestionPriority[0]].toLowerCase() === query + ) { + activateNode(suggestionPriority[0]); + } else { + state.selectedNode = undefined; + } + } else { + renderQuerySuggestions(); + } } -async function renderQuerySuggestions( - suggestionPairs, - suggestionPriority, - isInformed -) { - var suggestionPairs = - typeof suggestionPairs === "object" - ? suggestionPairs - : Object.assign(hiddenAliasLookup); - delete suggestionPairs[CONFIG.communication.hiddenAlias]; - var suggestionPriority = - typeof suggestionPriority === "object" - ? suggestionPriority - : Object.keys(suggestionPairs).sort((a, b) => { - a = hiddenAliasLookup[a].toLowerCase(); - b = hiddenAliasLookup[b].toLowerCase(); - return a === b ? 0 : a > b ? 1 : -1; - }); - suggestionPairs = - Object.keys(suggestionPairs).reduce((total, item) => { - total[item] = (isInformed ? "@" : "@") + suggestionPairs[item]; - return total; - }, {}) ?? {}; - suggestionsList.innerHTML = ""; - if (Object.keys(suggestionPairs) == "") return; - Object.keys(suggestionPairs).forEach((hidden, index) => { - suggestionsList.innerHTML += ` +async function renderQuerySuggestions(suggestionPairs, suggestionPriority, isInformed) { + var suggestionPairs = + typeof suggestionPairs === "object" ? suggestionPairs : Object.assign(hiddenAliasLookup); + delete suggestionPairs[CONFIG.communication.hiddenAlias]; + var suggestionPriority = + typeof suggestionPriority === "object" + ? suggestionPriority + : Object.keys(suggestionPairs).sort((a, b) => { + a = hiddenAliasLookup[a].toLowerCase(); + b = hiddenAliasLookup[b].toLowerCase(); + return a === b ? 0 : a > b ? 1 : -1; + }); + suggestionPairs = + Object.keys(suggestionPairs).reduce((total, item) => { + total[item] = (isInformed ? "@" : "@") + suggestionPairs[item]; + return total; + }, {}) ?? {}; + suggestionsList.innerHTML = ""; + if (Object.keys(suggestionPairs) == "") return; + Object.keys(suggestionPairs).forEach((hidden, index) => { + suggestionsList.innerHTML += `
  • `; - }); - document.querySelectorAll(".suggestion-button").forEach((btn) => { - btn.addEventListener("mouseup", async (e) => { - setTimeout(() => { - document.querySelector("#search-opts").style.visibility = "hidden"; - }, 1); - }); - btn.addEventListener("keydown", async (e) => { - if (e.key == "ArrowDown") { - e.preventDefault(); - if ( - document.querySelectorAll(".suggestion-button")[ - new Number(e.target.dataset.index) + 1 - ] == undefined - ) - return; - document - .querySelectorAll(".suggestion-button") - [new Number(e.target.dataset.index) + 1].focus(); - } - if ( - e.key == "ArrowUp" && - !e.target.classList.contains("search-opts-cap") - ) { - e.preventDefault(); - if ( - document.querySelectorAll(".suggestion-button")[ - new Number(e.target.dataset.index) - 1 - ] == undefined - ) - return; - document - .querySelectorAll(".suggestion-button") - [e.target.dataset.index - 1].focus(); - } - }); - }); - document - .querySelector(".search-opts-cap") - .addEventListener("keydown", async (e) => { - if (e.key == "ArrowUp") { - e.preventDefault(); - document.querySelector("#searchEntryField").focus(); - } - }); + }); + document.querySelectorAll(".suggestion-button").forEach((btn) => { + btn.addEventListener("mouseup", async (e) => { + setTimeout(() => { + document.querySelector("#search-opts").style.visibility = "hidden"; + }, 1); + }); + btn.addEventListener("keydown", async (e) => { + if (e.key == "ArrowDown") { + e.preventDefault(); + if ( + document.querySelectorAll(".suggestion-button")[new Number(e.target.dataset.index) + 1] == + undefined + ) + return; + document + .querySelectorAll(".suggestion-button") + [new Number(e.target.dataset.index) + 1].focus(); + } + if (e.key == "ArrowUp" && !e.target.classList.contains("search-opts-cap")) { + e.preventDefault(); + if ( + document.querySelectorAll(".suggestion-button")[new Number(e.target.dataset.index) - 1] == + undefined + ) + return; + document.querySelectorAll(".suggestion-button")[e.target.dataset.index - 1].focus(); + } + }); + }); + document.querySelector(".search-opts-cap").addEventListener("keydown", async (e) => { + if (e.key == "ArrowUp") { + e.preventDefault(); + document.querySelector("#searchEntryField").focus(); + } + }); } networkMap.onUpdate(async (_sig, externalDetail) => { - switch (externalDetail[0]) { - case "addNode": - graph.updateNode(externalDetail[1], () => { - return { - x: 0, - y: 0, - label: - externalDetail[1] === CONFIG.communication.hiddenAlias - ? "𝙈𝙚" - : CONFIG.UI.renderUnfamiliarPublicAliases - ? hiddenAliasLookup[externalDetail[1]] - : externalDetail[1], - size: externalDetail[1] === CONFIG.communication.hiddenAlias ? 10 : 5, - color: statusColors.Neutral, - type: - externalDetail[1] === CONFIG.communication.hiddenAlias - ? "server" - : "unrelated", - }; - }); - graph.updateEdge(externalDetail[1], "server", () => { - return { type: "antialias", size: 1 }; - }); - break; - case "addEdge": - graph.updateEdge(...externalDetail[1].sort(), () => { - return { type: "antialias", size: 1 }; - }); - break; - case "removeEdge": - graph.dropEdge(...externalDetail[1].sort()); - break; - case "removeNode": - graph.dropNode(externalDetail[1]); - break; - case "totalWipe": - graph.forEachNode((node) => { - if (node === CONFIG.communication.hiddenAlias || node === "server") - return; - try { - graph.dropNode(node); - } catch { - return; - } - }); - break; - } - const circularLocations = circular(graph); - animateNodes(graph, circularLocations, { - easing: "quadraticIn", - duration: 2000, - }); - renderer.refresh(); - setSearchQuery(document.querySelector("#searchEntryField").value); + switch (externalDetail[0]) { + case "addNode": + graph.updateNode(externalDetail[1], () => { + return { + x: 0, + y: 0, + label: + externalDetail[1] === CONFIG.communication.hiddenAlias + ? "𝙈𝙚" + : CONFIG.UI.renderUnfamiliarPublicAliases + ? hiddenAliasLookup[externalDetail[1]] + : externalDetail[1], + size: externalDetail[1] === CONFIG.communication.hiddenAlias ? 10 : 5, + color: statusColors.Neutral, + type: externalDetail[1] === CONFIG.communication.hiddenAlias ? "server" : "unrelated", + }; + }); + graph.updateEdge(externalDetail[1], "server", () => { + return { type: "antialias", size: 1 }; + }); + break; + case "addEdge": + graph.updateEdge(...externalDetail[1].sort(), () => { + return { type: "antialias", size: 1 }; + }); + break; + case "removeEdge": + graph.dropEdge(...externalDetail[1].sort()); + break; + case "removeNode": + graph.dropNode(externalDetail[1]); + break; + case "totalWipe": + graph.forEachNode((node) => { + if (node === CONFIG.communication.hiddenAlias || node === "server") return; + try { + graph.dropNode(node); + } catch { + return; + } + }); + break; + } + const circularLocations = circular(graph); + animateNodes(graph, circularLocations, { + easing: "quadraticIn", + duration: 2000, + }); + renderer.refresh(); + setSearchQuery(document.querySelector("#searchEntryField").value); }); async function activateNode(node) { - state.activeNode = node; - renderer.refresh(); - if (node) - if (graph.nodes().includes(node)) { - camera.animate(renderer.getNodeDisplayData(node), { - duration: 500, - }); - } - if (authPeers.includes(node)) fusedStream.loadCache(node); + state.activeNode = node; + renderer.refresh(); + if (node) + if (graph.nodes().includes(node)) { + camera.animate(renderer.getNodeDisplayData(node), { + duration: 500, + }); + } + if (authPeers.includes(node)) fusedStream.loadCache(node); } async function generateNodeContext(node, isKnownConnection, mouseUpPromise) { - let contextType; - let contextTypes = ["#node-context", "#connection-context"]; - if (node === "server" || node === CONFIG.communication.hiddenAlias) return; - else if (isKnownConnection) contextType = contextTypes[1]; - else contextType = contextTypes[0]; - const context = document.querySelector(contextType); - await mouseUpPromise; - context.style.visibility = "visible"; - contextTypes.splice(contextTypes.indexOf(contextType), 1); - contextTypes.forEach((type) => { - document.querySelector(type).style.visibility = "hidden"; - }); - context.setAttribute("data-caller", node); - let offset = 0.1 / camera.ratio; - context.style.left = 50 + offset + "%"; - context.style.bottom = 50 + offset + "%"; - raceNodeContextDismissEvents(context); + let contextType; + let contextTypes = ["#node-context", "#connection-context"]; + if (node === "server" || node === CONFIG.communication.hiddenAlias) return; + else if (isKnownConnection) contextType = contextTypes[1]; + else contextType = contextTypes[0]; + const context = document.querySelector(contextType); + await mouseUpPromise; + context.style.visibility = "visible"; + contextTypes.splice(contextTypes.indexOf(contextType), 1); + contextTypes.forEach((type) => { + document.querySelector(type).style.visibility = "hidden"; + }); + context.setAttribute("data-caller", node); + var offset = 0.2 / camera.ratio; + context.style.left = `${50 + offset}%`; + context.style.bottom = `${50 + offset}%`; + raceNodeContextDismissEvents(context); } async function raceNodeContextDismissEvents(context) { - let funcs = { - windowResizeEvent: async () => { - nodeDismissHandler.dispatch("windowResize"); - }, - rightClickCanvasEvent: async () => { - nodeDismissHandler.dispatch("nonContextClick"); - }, - clickContextEvent: async () => { - nodeDismissHandler.dispatch("contextButtonClicked"); - }, - clickCanvasEvent: async () => { - nodeDismissHandler.dispatch("nonContextClick"); - }, - clickNodeEvent: async () => { - nodeDismissHandler.dispatch("nonContextClick"); - }, - rightClickNodeEvent: async () => { - nodeDismissHandler.dispatch("nonContextClick"); - }, - wheelStageEvent: async () => { - nodeDismissHandler.dispatch("wheelStage"); - }, - doubleClickStageEvent: async () => { - nodeDismissHandler.dispatch("nonContextClick"); - }, - }; + let funcs = { + windowResizeEvent: async () => { + nodeDismissHandler.dispatch("windowResize"); + }, + rightClickCanvasEvent: async () => { + nodeDismissHandler.dispatch("nonContextClick"); + }, + clickContextEvent: async () => { + nodeDismissHandler.dispatch("contextButtonClicked"); + }, + clickCanvasEvent: async () => { + nodeDismissHandler.dispatch("nonContextClick"); + }, + clickNodeEvent: async () => { + nodeDismissHandler.dispatch("nonContextClick"); + }, + rightClickNodeEvent: async () => { + nodeDismissHandler.dispatch("nonContextClick"); + }, + wheelStageEvent: async () => { + nodeDismissHandler.dispatch("wheelStage"); + }, + doubleClickStageEvent: async () => { + nodeDismissHandler.dispatch("nonContextClick"); + }, + }; - window.onresize = funcs["windowResizeEvent"]; - context.onmouseup = funcs["clickContextEvent"]; - renderer.on("rightClickStage", funcs["rightClickCanvasEvent"]); - renderer.on("clickStage", funcs["clickCanvasEvent"]); - renderer.on("wheelStage", funcs["wheelStageEvent"]); - renderer.on("clickNode", funcs["clickNodeEvent"]); - renderer.on("rightClickNode", funcs["rightClickNodeEvent"]); - renderer.on("doubleClickStage", funcs["doubleClickStageEvent"]); + window.onresize = funcs["windowResizeEvent"]; + context.onmouseup = funcs["clickContextEvent"]; + renderer.on("rightClickStage", funcs["rightClickCanvasEvent"]); + renderer.on("clickStage", funcs["clickCanvasEvent"]); + renderer.on("wheelStage", funcs["wheelStageEvent"]); + renderer.on("clickNode", funcs["clickNodeEvent"]); + renderer.on("rightClickNode", funcs["rightClickNodeEvent"]); + renderer.on("doubleClickStage", funcs["doubleClickStageEvent"]); - let resizeCall = nodeDismissHandler.acquireExpectedDispatch( - "windowResize", - 60000 - ); - let contextClickCall = nodeDismissHandler.acquireExpectedDispatch( - "contextButtonClicked", - 60000 - ); - let nonContextClickCall = nodeDismissHandler.acquireExpectedDispatch( - "nonContextClick", - 60000 - ); - let wheelStageCall = nodeDismissHandler.acquireExpectedDispatch( - "wheelStage", - 60000 - ); - let raceTimeoutDefault = nodeDismissHandler.acquireExpectedDispatch( - "defaultRaceResolveTimer", - 59000 - ); + let resizeCall = nodeDismissHandler.acquireExpectedDispatch("windowResize", 60000); + let contextClickCall = nodeDismissHandler.acquireExpectedDispatch("contextButtonClicked", 60000); + let nonContextClickCall = nodeDismissHandler.acquireExpectedDispatch("nonContextClick", 60000); + let wheelStageCall = nodeDismissHandler.acquireExpectedDispatch("wheelStage", 60000); + let raceTimeoutDefault = nodeDismissHandler.acquireExpectedDispatch("defaultRaceResolveTimer", 59000); - const promiseConsequence = (_) => { - context.style.visibility = "hidden"; - activateNode(); - nodeDismissHandler.flushExpectedDispatches(); - window.removeEventListener("resize", funcs["windowResizeEvent"]); - context.removeEventListener("mouseup", funcs["clickContextEvent"]); - renderer.removeListener("rightClickStage", funcs["rightClickCanvasEvent"]); - renderer.removeListener("clickStage", funcs["clickCanvasEvent"]); - renderer.removeListener("wheelStage", funcs["wheelStageEvent"]); - renderer.removeListener("clickNode", funcs["clickNodeEvent"]); - renderer.removeListener("rightClickNode", funcs["rightClickNodeEvent"]); - renderer.removeListener("doubleClickStage", funcs["doubleClickStageEvent"]); - }; - Promise.race([ - resizeCall, - contextClickCall, - nonContextClickCall, - wheelStageCall, - raceTimeoutDefault, - ]).then(promiseConsequence, promiseConsequence); + const promiseConsequence = (_) => { + context.style.visibility = "hidden"; + activateNode(); + nodeDismissHandler.flushExpectedDispatches(); + window.removeEventListener("resize", funcs["windowResizeEvent"]); + context.removeEventListener("mouseup", funcs["clickContextEvent"]); + renderer.removeListener("rightClickStage", funcs["rightClickCanvasEvent"]); + renderer.removeListener("clickStage", funcs["clickCanvasEvent"]); + renderer.removeListener("wheelStage", funcs["wheelStageEvent"]); + renderer.removeListener("clickNode", funcs["clickNodeEvent"]); + renderer.removeListener("rightClickNode", funcs["rightClickNodeEvent"]); + renderer.removeListener("doubleClickStage", funcs["doubleClickStageEvent"]); + }; + Promise.race([ + resizeCall, + contextClickCall, + nonContextClickCall, + wheelStageCall, + raceTimeoutDefault, + ]).then(promiseConsequence, promiseConsequence); } -renderer.on("clickNode", async (event) => { - setTimeout(() => { - ["successNodes", "errorNodes"].forEach((nodeState) => { - if (state[nodeState].indexOf(event.node) != -1) - state[nodeState].splice(state[nodeState].indexOf(event.node), 1); - }); - renderer.refresh(); - activateNode(event.node); - if (mod.ALT) { - if ( - event.node === "server" || - event.node === CONFIG.communication.hiddenAlias - ) - return; - peerConnection.prototype.negotiateAgnosticAuthConnection(event.node); - } - }, 10); -}); +async function nodeClick(event) { + setTimeout(() => { + ["successNodes", "errorNodes"].forEach((nodeState) => { + if (state[nodeState].indexOf(event.node) != -1) + state[nodeState].splice(state[nodeState].indexOf(event.node), 1); + }); + renderer.refresh(); + activateNode(event.node); + if (mod.ALT) { + if (event.node === "server" || event.node === CONFIG.communication.hiddenAlias) return; + peerConnection.prototype.negotiateAgnosticAuthConnection(event.node); + } + }, 10); + if (recentlyClickedNode === event.node) { + rightClickNodeAction(event, true); + console.log("test", event); + recentlyClickedNode = null; + return; + } + recentlyClickedNode = event.node; + var currentClickTimestamp = +new Date(); + lastClickTimestamp = currentClickTimestamp; + setTimeout(() => { + if (currentClickTimestamp === lastClickTimestamp && recentlyClickedNode) { + currentClickTimestamp = null; + recentlyClickedNode = null; + } + }, 5000); +} -renderer.on("clickStage", async (event) => { - activateNode(); +renderer.on("clickNode", nodeClick); + +renderer.on("clickStage", async () => { + activateNode(); }); -renderer.on("rightClickNode", async (event) => { - activateNode(event.node); - if (event.node === "server") { - return; - } - window.onmouseup = async function () { - nodeDismissHandler.dispatch("mouseUpMenuSpawnable"); - }; - const contextPromise = nodeDismissHandler.acquireExpectedDispatch( - "mouseUpMenuSpawnable" - ); - generateNodeContext( - event.node, - authPeers.includes(event.node), - contextPromise - ); +async function rightClickNodeAction(event, doubleTapReferred) { + activateNode(event.node); + if (event.node === "server") { + return; + } + if (doubleTapReferred) { + setTimeout(() => { + nodeDismissHandler.dispatch("mouseUpMenuSpawnable"); + }, 0); + } else { + window.addEventListener("mouseup", async function () { + nodeDismissHandler.dispatch("mouseUpMenuSpawnable"); + }); + } + const contextPromise = nodeDismissHandler.acquireExpectedDispatch("mouseUpMenuSpawnable"); + generateNodeContext(event.node, authPeers.includes(event.node), contextPromise); +} + +renderer.on("rightClickNode", rightClickNodeAction); + +renderer.on("doubleClickNode", async (event) => { + event.preventSigmaDefault(); }); window.graphState = state;