UNPKG

create-gojs-kit

Version:

A CLI for downloading GoJS samples, extensions, and docs

550 lines (477 loc) 25 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/> <meta name="description" content="Test a diagram by simulating abstract input events." /> <meta itemprop="description" content="Test a diagram by simulating abstract input events." /> <meta property="og:description" content="Test a diagram by simulating abstract input events." /> <meta name="twitter:description" content="Test a diagram by simulating abstract input events." /> <link rel="preconnect" href="https://rsms.me/"> <link rel="stylesheet" href="../assets/css/style.css"> <!-- Copyright 1998-2025 by Northwoods Software Corporation. --> <meta itemprop="name" content="Simulating Input using Robot" /> <meta property="og:title" content="Simulating Input using Robot" /> <meta name="twitter:title" content="Simulating Input using Robot" /> <meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/robot.png" /> <meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/robot.png" /> <meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/robot.png" /> <meta property="og:url" content="https://gojs.net/latest/samples/Robot.html" /> <meta property="twitter:url" content="https://gojs.net/latest/samples/Robot.html" /> <meta name="twitter:card" content="summary_large_image" /> <meta property="og:type" content="website" /> <meta property="twitter:domain" content="gojs.net" /> <title> Simulating Input using Robot | GoJS Diagramming Library </title> </head> <body> <!-- This top nav is not part of the sample code --> <nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200"> <div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4"> <a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo" href="../"> GoJS </a> <div class="relative"> <button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation"> <svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> <div id="topnavList" class="hidden md:block"> <div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit"> <a href="../learn/">Learn</a> <a href="../samples/">Samples</a> <a href="../intro/">Intro</a> <a href="../api/">API</a> <a href="../download.html">Download</a> <a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a> <a id="tc" href="https://nwoods.com/contact.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a> <a id="tb" href="https://nwoods.com/sales/index.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a> </div> </div> </div> </div> </nav> <script> window.addEventListener("DOMContentLoaded", function () { // topnav var topButton = document.getElementById("topnavButton"); var topnavList = document.getElementById("topnavList"); if (topButton && topnavList) { topButton.addEventListener("click", function (e) { topnavList .classList .toggle("hidden"); e.stopPropagation(); }); document.addEventListener("click", function (e) { // if the clicked element isn't the list, close the list if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) { topButton.click(); } }); // set active <a> element var url = window .location .href .toLowerCase(); var aTags = topnavList.getElementsByTagName('a'); for (var i = 0; i < aTags.length; i++) { var lowerhref = aTags[i] .href .toLowerCase(); if (lowerhref.endsWith('.html')) lowerhref = lowerhref.slice(0, -5); if (url.startsWith(lowerhref)) { aTags[i] .classList .add('active'); break; } } } }); </script> <div class="flex flex-col prose"> <div class="w-full max-w-screen-xl mx-auto"> <!-- * * * * * * * * * * * * * --> <!-- Start of GoJS sample code --> <script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script> <div id="allSampleContent" class="p-4 w-full"> <script src="https://cdn.jsdelivr.net/npm/gojs/release/go-debug.js"></script> <script src="https://cdn.jsdelivr.net/npm/create-gojs-kit/dist/extensions/Robot.js"></script> <script id="code"> var myRobot; // this global variable will hold an instance of the Robot class for myDiagram function init() { function showProperties(e, obj) { // executed by ContextMenuButton var node = obj.part.adornedPart; var msg = 'Context clicked: ' + node.data.key + '. '; msg += 'Selection includes:'; myDiagram.selection.each(part => { msg += ' ' + part.toString(); }); document.getElementById('myStatus').textContent = msg; } function nodeClicked(e, obj) { // executed by click and doubleclick handlers var evt = e.copy(); var node = obj.part; var type = evt.clickCount === 2 ? 'Double-Clicked: ' : 'Clicked: '; var msg = type + node.data.key + '. '; document.getElementById('myStatus').textContent = msg; } myDiagram = new go.Diagram('myDiagramDiv', { // must name or refer to the DIV HTML element initialContentAlignment: go.Spot.Top, "InitialLayoutCompleted": e => { document.getElementById('myStatus').textContent = 'Initial Layout Completed'; }, nodeTemplate: new go.Node('Auto', { click: nodeClicked, doubleClick: nodeClicked, contextMenu: go.GraphObject.build('ContextMenu') .add( go.GraphObject.build('ContextMenuButton', { click: showProperties }) .add(new go.TextBlock('Properties')) ) }) .add( new go.Shape('Rectangle', { fill: 'lightgray', portId: '', fromLinkable: true, toLinkable: true, cursor: 'pointer' }), new go.TextBlock({ margin: 6 }) .bind('text', 'key') ), model: new go.GraphLinksModel({ copiesKey: true, // so that the newly dropped node will have key == "Alpha" nodeDataArray: [{ key: 'Lambda' }, { key: 'Mu' }], linkDataArray: [{ from: 'Lambda', to: 'Mu' }] }), 'undoManager.isEnabled': true }); // a shared Robot that can be used by all commands for this one Diagram myRobot = new Robot(myDiagram); // defined in Robot.js // initialize the Palette that is on the left side of the page myPalette = new go.Palette('myPaletteDiv', { nodeTemplate: myDiagram.nodeTemplate, model: new go.GraphLinksModel([ // specify the contents of the Palette { key: 'Alpha' }, { key: 'Beta' }, { key: 'Gamma' }, { key: 'Delta' } ]) }); } // These are the HTML button event handlers. function clickLambda() { var lambda = myDiagram.findNodeForKey('Lambda'); if (lambda === null) return; var loc = lambda.location; // click on Lambda myRobot.mouseDown(loc.x + 10, loc.y + 10, 0, {}); myRobot.mouseUp(loc.x + 10, loc.y + 10, 100, {}); // Clicking is just a sequence of input events. // There is no command in CommandHandler for such a basic gesture. // Alternatively, if the GraphObject.click property is non null, you could just call it: // const clickfunc = lambda.click; // if (clickfunc !== null) { // const ie = new go.InputEvent(); // ie.diagram = myDiagram; // ie.documentPoint = new go.Point(loc.x + 10, loc.y + 10); // ie.viewPoint = myDiagram.transformDocToView(ie.documentPoint); // clickfunc(ie, lambda); // } } function doubleClickLambda() { var lambda = myDiagram.findNodeForKey('Lambda'); if (lambda === null) return; var loc = lambda.location; // double-click on Lambda myRobot.mouseDown(loc.x + 10, loc.y + 10, 0, {}); myRobot.mouseUp(loc.x + 10, loc.y + 10, 100, {}); myRobot.mouseDown(loc.x + 10, loc.y + 10, 200, { clickCount: 2 }); myRobot.mouseUp(loc.x + 10, loc.y + 10, 300, { clickCount: 2 }); // Alternatively, if the GraphObject.doubleClick property is non null, you could just call it: // const clickfunc = lambda.doubleClick; // if (clickfunc !== null) { // const ie = new go.InputEvent(); // ie.diagram = myDiagram; // ie.documentPoint = new go.Point(loc.x + 10, loc.y + 10); // ie.viewPoint = myDiagram.transformDocToView(ie.documentPoint); // ie.clickCount = 2; // clickfunc(ie, lambda); // } } function dragFromPalette() { // simulate a drag-and-drop between Diagrams: var dragdrop = { sourceDiagram: myPalette, targetDiagram: myDiagram }; myRobot.mouseDown(5, 5, 0, dragdrop); // this should be where the Alpha node is in the source myPalette myRobot.mouseMove(60, 60, 100, dragdrop); myRobot.mouseUp(100, 100, 200, dragdrop); // this is where the node will be dropped in the target myDiagram // If successful in dragging a node from the Palette into the Diagram, // the DraggingTool will perform a transaction. } function copyNode() { var alpha = myDiagram.findNodeForKey('Alpha'); if (alpha === null) return; var loc = alpha.actualBounds.center; var options = { control: true, alt: true }; // Simulate a mouse drag to move the Alpha node: myRobot.mouseDown(loc.x, loc.y, 0, options); myRobot.mouseMove(loc.x + 80, loc.y + 50, 50, options); myRobot.mouseMove(loc.x + 20, loc.y + 100, 100, options); myRobot.mouseUp(loc.x + 20, loc.y + 100, 150, options); // If successful, will have made a copy of the "Alpha" node below it. // Alternatively you could copy the Node using commands: // myDiagram.commandHandler.copySelection(); // myDiagram.commandHandler.pasteSelection(new go.Point(loc.x+20, loc.y+100)); } function linkNodes() { var alpha = myDiagram.findNodeForKey('Alpha'); if (alpha === null) return; var ptA = alpha.getDocumentPoint(go.Spot.Bottom); var alpha2 = myDiagram.findNodeForKey('Alpha2'); if (alpha2 === null) return; var ptB = alpha.actualBounds.center; // Simulate dragging from the inside edge of the Alpha node, to start drawing a new link. myRobot.mouseDown(ptA.x, ptA.y-2, 0); myRobot.mouseMove(ptA.x+10, ptA.y+10, 100); myRobot.mouseMove((ptA.x+ptB.x)/2, (ptA.y+ptB.y)/2, 200); myRobot.mouseUp(ptB.x, ptB.y, 250); // Now should have drawn a new Link // Alternatively, you could draw a new Link via: // const tool = myDiagram.toolManager.linkingTool; // tool.insertLink(alpha, null, alpha2, null); } function dragSelectNodes() { var alpha = myDiagram.findNodeForKey('Alpha'); if (alpha === null) return; var alpha2 = myDiagram.findNodeForKey('Alpha2'); if (alpha2 === null) return; var coll = new go.Set(); coll.add(alpha); coll.add(alpha2); var area = myDiagram.computePartsBounds(coll); area.inflate(30, 30); // Simulate dragging in the background around the two Alpha nodes. // This uses timestamps to pretend to wait a while to avoid activating the PanningTool. // Hopefully this mouse down does not hit any Part, but in the Diagram's background: myRobot.mouseDown(area.x, area.y, 0); // NOTE that this mouseMove timestamp needs to be > myDiagram.toolManager.dragSelectingTool.delay: myRobot.mouseMove(area.x+10, area.y+10, 300); myRobot.mouseMove(area.right-10, area.bottom-10, 350); myRobot.mouseUp(area.right, area.bottom, 500); // Now should have selected both "Alpha" and "Alpha2", and any Link between them, using the DragSelectingTool. // Alternatively you could select the Nodes programmatically: // alpha.isSelected = true; // alpha2.isSelected = true; // Or you could select all Parts in the rectangular AREA: //myDiagram.selectCollection(myDiagram.findPartsIn(area)); } function clickContextMenu() { var alpha = myDiagram.findNodeForKey('Alpha'); if (alpha === null) return; var loc = alpha.location; // right click on Alpha myRobot.mouseDown(loc.x + 10, loc.y + 10, 0, { right: true }); myRobot.mouseUp(loc.x + 10, loc.y + 10, 100, { right: true }); // Alternatively you could invoke the Show Context Menu command directly: // myDiagram.commandHandler.showContextMenu(alpha); // move mouse over first context menu button myRobot.mouseMove(loc.x + 20, loc.y + 20, 200); // and click that button myRobot.mouseDown(loc.x + 20, loc.y + 20, 300); myRobot.mouseUp(loc.x + 20, loc.y + 20, 350); // This should have invoked the ContextMenuButton's click function, showProperties, // which should have put a green message in the myStatus DIV. } function deleteSelection() { // Simulate clicking the "Delete" key: myRobot.keyDown('Delete'); myRobot.keyUp('Delete'); // Now the selected Nodes are deleted. // Alternatively you could invoke the Delete command directly: // myDiagram.commandHandler.deleteSelection(); } function pan() { var pos1 = myDiagram.position.copy(); var pt = new go.Point(myDiagram.viewportBounds.x + 30, myDiagram.viewportBounds.centerY); myRobot.mouseDown(pt.x, pt.y, 0, {}); // Minimal wait after mouseDown when moving, else the PanningTool will be pre-empted // by the DragSelectingTool, which is controlled by the DragSelectingTool.delay property. // Remember that these are document coordinates, which are shifted by the panning motion. myRobot.mouseMove(pt.x + 20, pt.y + 10, 10, {}); myRobot.mouseMove(pt.x + 20, pt.y + 10, 30, {}); myRobot.mouseMove(pt.x + 20, pt.y + 10, 50, {}); myRobot.mouseUp(pt.x + 20, pt.y + 10, 70, {}); var pos2 = myDiagram.position.copy(); document.getElementById('myStatus').textContent = 'Document.position before: ' + pos1.toString() + ' ' + 'Document.position after: ' + pos2.toString(); // Alternatively, you could scroll programmatically: // const newpos = myDiagram.position.copy(); // newpos.offset(-80, -40); // myDiagram.position = newpos; } window.addEventListener('DOMContentLoaded', init); </script> <div id="sample"> <div style="width: 100%; display: flex; justify-content: space-between"> <div id="myPaletteDiv" style="width: 80px; height: 300px; margin-right: 2px; border: solid 1px black"></div> <div id="myDiagramDiv" style="flex-grow: 1; height: 300px; border: solid 1px black"></div> </div> <p>To simulate mouse events the buttons below use the <b>Robot</b> class that is defined in <a href="../extensions/Robot.js">Robot.js</a>.</p> Message: <span id="myStatus" style="color: green; font:bold 10pt sans-serif;">(none)</span> <p> Click these buttons in order from top to bottom:<br /> <p> <button onclick="clickLambda()">Click Lambda</button><br /> <button onclick="doubleClickLambda()">Double Click Lambda</button><br /> </p> <p> <button onclick="dragFromPalette()">Drag From Palette</button><br /> <button onclick="copyNode()">Copy Node</button><br /> <button onclick="linkNodes()">Link Nodes</button><br /> <button onclick="dragSelectNodes()">Drag Select Nodes</button><br /> <button onclick="clickContextMenu()">Context Menu Click Alpha</button><br /> <button onclick="deleteSelection()">Delete</button><br /> </p> <button onclick="pan()">Pan the Diagram</button><br /> </p> <p> The <a>UndoManager</a> has been enabled in the main Diagram. Give focus to the Diagram and you can undo everything and then redo everything to confirm what was executed by the Robot. </p> </div> </div> <!-- * * * * * * * * * * * * * --> <!-- End of GoJS sample code --> </div> <div id="allTagDescriptions" class="p-4 w-full max-w-screen-xl mx-auto"> <hr/> <h3 class="text-xl">GoJS Features in this sample</h3> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Collections</h4> <p> <b>GoJS</b> provides its own collection classes: <a href="../api/symbols/List.html" target="api">List</a>, <a href="../api/symbols/Set.html" target="api">Set</a>, and <a href="../api/symbols/Map.html" target="api">Map</a>. You can iterate over a collection by using an <a href="../api/symbols/Iterator.html" target="api">Iterator</a>. More information can be found in the <a href="../intro/collections.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#collections">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Context Menus</h4> <p> A GoJS context menu is an <a href="../api/symbols/Adornment.html" target="api">Adornment</a> that is shown when the user context-clicks (right mouse click or long touch hold) an object that has its <a href="../api/symbols/GraphObject.html#contextMenu" target="api">GraphObject.contextMenu</a> set. The context menu is bound to the same data as the part itself. </p> <p> It is typical to implement a context menu as a "ContextMenu" Panel containing "ContextMenuButton"s, as you can see in the code below in the assignment of the Node's <a href="../api/symbols/GraphObject.html#contextMenu" target="api">GraphObject.contextMenu</a> and <a href="../api/symbols/Diagram.html#contextMenu" target="api">Diagram.contextMenu</a> properties. Each "ContextMenu" is just a "Vertical" Panel <a href="../api/symbols/Adornment.html" target="api">Adornment</a> that is shadowed. Each "ContextMenuButton" is a Panel on which you can set the <a href="../api/symbols/GraphObject.html#click" target="api">GraphObject.click</a> event handler. In the event handler <code>obj.part</code> will be the whole context menu Adornment. <code>obj.part.adornedPart</code> will be the adorned Node or Link. The bound data is <code>obj.part.data</code>, which will be the same as <code>obj.part.adornedPart.data</code>. </p> <p> More information can be found in the <a href="../intro/contextmenus.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#contextmenus">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Palette</h4> <p> A <a href="../api/symbols/Palette.html" target="api">Palette</a> is a subclass of <a href="../api/symbols/Diagram.html" target="api">Diagram</a> that is used to display a number of <a href="../api/symbols/Part.html" target="api">Part</a>s that can be dragged into the diagram that is being modified by the user. The initialization of a <a href="../api/symbols/Palette.html" target="api">Palette</a> is just like the initialization of any <a href="../api/symbols/Diagram.html" target="api">Diagram</a>. Like Diagrams, you can have more than one Palette on the page at the same time. </p> <p> More information can be found in the <a href="../intro/palette.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#palette">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Buttons</h4> <p> GoJS defines several <a href="../api/symbols/Panel.html" target="api">Panel</a>s for common uses. These include "Button", "TreeExpanderButton", "SubGraphExpanderButton", "PanelExpanderButton", "ContextMenuButton", and "CheckBoxButton". "ContextMenuButton"s are typically used inside of "ContextMenu" Panels; "CheckBoxButton"s are used in the implementation of "CheckBox" Panels. </p> <p> These predefined panels can be used as if they were <a href="../api/symbols/Panel.html" target="api">Panel</a>-derived classes in calls to <a href="../api/symbols/GraphObject.html#build" target="api">GraphObject.build</a>. They are implemented as simple visual trees of <a href="../api/symbols/GraphObject.html" target="api">GraphObject</a>s in <a href="../api/symbols/Panel.html" target="api">Panel</a>s, with pre-set properties and event handlers. </p> <p> More information can be found in the <a href="../intro/buttons.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#buttons">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>GoJS Extensions</h4> <p> <b>GoJS</b> can be extended in a variety of ways. The most common way to change the standard behavior is to set properties on the <a href="../api/symbols/GraphObject.html" target="api">GraphObject</a>, <a href="../api/symbols/Diagram.html" target="api">Diagram</a>, <a href="../api/symbols/CommandHandler.html" target="api">CommandHandler</a>, <a href="../api/symbols/Tool.html" target="api">Tool</a>, or <a href="../api/symbols/Layout.html" target="api">Layout</a>. But when the desired property does not exist, you might need to override methods of CommandHandler, Tool, Layout, Link, or Node. Methods that you can override are documented in the API reference. Various features of GoJS can be overriden, either by replacing a method on an instance (a feature of JavaScript) or by defining a subclass. You should not modify the prototypes of any of the <b>GoJS</b> classes. </p> <p> In addition to our samples, <b>GoJS</b> provides an <strong><a href="../samples/#extensions">extensions gallery</a></strong>, showcasing the creation of custom tools and layouts. Those classes and samples are written in TypeScript, available at <code>../extensionsJSM/</code>, as ECMAScript/JavaScript modules -- these use the <code>../release/go-module.js</code> library. We recommend that you copy the files that you need into your project, so that you can adjust how they refer to the GoJS library that you choose and so that you can include them into your own building and packaging procedures. </p> <p> More information can be found in the <a href="../intro/extensions.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#extensions">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>HTML Interaction</h4> <p> GoJS Diagrams can be used alongside other HTML elements in a webapp. For custom Text Editors, Context Menus, and ToolTips, which are invoked and hidden via GoJS tool operations, it is best to use the <a href="../api/symbols/HTMLInfo.html" target="api">HTMLInfo</a> class. </p> <p> More information can be found in the <a href="../intro/HTMLinteraction.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#html">Related samples</a> </p> <hr> </div> </div> </body> <!-- This script is part of the gojs.net website, and is not needed to run the sample --> <script src="../assets/js/goSamples.js"></script> </html>