GoJS Class Index
ActionTool
The ActionTool is responsible for handling and dispatching mouse events on GraphObjects that have GraphObject.isActionable set to true.More... This is how one implements "controls", such as buttons or sliders or knobs, as GraphObjects that can be inside Parts without interfering with the standard tool behaviors.
This tool allows individual GraphObjects (usually Panels) to handle mouse-down-move-up events without having to define new Tools. It does this by calling GraphObject.actionDown, GraphObject.actionMove, GraphObject.actionUp, and GraphObject.actionCancel on objects that have GraphObject.isActionable set to true.
This tool is a standard mouse-down tool, the ToolManager.actionTool.
This tool does not utilize any Adornments or tool handles. This tool does not modify the model or conduct any transaction, although the actions that this invokes may do so.
It would be very unusual to want to customize this tool.
Adornment
An adornment is a special kind of Part that is associated with another Part, the Adornment.adornedPart.More...
Adornments are normally associated with a particular GraphObject in the adorned part -- that is the value of #adornedObject. However, the #adornedObject may be null, in which case the #adornedPart will also be null.
The area occupied by the adorned object is represented in the adornment's visual tree by a Placeholder. The placeholder is always the Part.locationObject, although you may specify any Spot as the Part.locationSpot. An adornment need not have a placeholder, but it may have at most one.
Adornments can be distinguished by their Part.category. This property can be an arbitrary string value determined by the code creating the adornment, typically a tool that wants to be able to tell various adornments apart from each other. Use the Part.findAdornment method to find an adornment for a part of a given category.
For example, one of the Adornments created by Part.updateAdornments when the part Part.isSelected has the Part.category of "Selection". Those created by ResizingTool.updateAdornments have a category of "Resize" and normally contain eight resize handles.
Besides the selection adornment and tool adornments, adornments are also used for context menus and tooltips. The #adornedObject in such cases refers to the GraphObject to which the the context menu or tooltip applies.
There cannot be any links connected to an adornment, nor can an adornment have members or be a member of a group.
An adornment cannot have its own adornments. An adornment cannot be selected.
Adornments are not positioned by a Layout because they are normally positioned according to the Part that they adorn.
For more discussion and examples, see Selection, ToolTips, Context Menus, and Tools.
AnimationManager
AnimationManager handles animations in a Diagram.More... Each Diagram has one, Diagram.animationManager. Layouts, Group expansion and Tree expansion automatically start animations.
Animation is enabled by default, setting the isEnabled property to false will turn off animations for a Diagram.
When an animations begins it raises the "AnimationStarting" diagram event, upon completion it raises the "AnimationFinished" diagram event.
Animation will stop if a new transaction is started, if an undo or redo is called, if a layout is invalidated, or if a model is replaced. When an animation is stopped, the Diagram immediately finishes the animation and draws the final state. Animations can be stopped programatically with the method AnimationManager.stopAnimation.
Binding
A Binding describes how to automatically set a property on a GraphObject to a value of a property of data in the model.More... The target property name and the data source property name are strings. All name matching is case-sensitive.
Register bindings by calling GraphObject.bind with a new Binding. Existing bindings become read-only, and no new bindings may be added, when a template (a Part) is copied. Bindings will be shared by all copies of the template's GraphObjects.
For example, your node data might be like:
{ key: 23, say: "hello!" }
Your simple node template might be like:
var template = new go.Node(go.Panel.Auto); // . . . define the rest of the Node's visual tree . . . var txt = new go.TextBlock(); txt.bind(new go.Binding("text", "say")); template.add(txt); myDiagram.nodeTemplate = template;Using GraphObject.make it might look like:
var $ = go.GraphObject.make; myDiagram.nodeTemplate = $(go.Node, "Auto", . . . $(go.TextBlock, new go.Binding("text", "say")) )
The data binding causes the TextBlock.text property of the TextBlock to be set to the value of the data's "say" property. If the value of the "say" property of a particular data object is undefined, the binding is not evaluated: the target property is not set. If there is an error with the binding, you may see a message in the console log. For this reason you may want to explicitly set the initial value for a property when defining the GraphObject, since that value will remain as the default value if the Binding is not evaluated.
Bindings are not necessarily evaluated in any particular order. Binding sources should not be (or depend in a conversion function on) the category of the data if you might be modifying the category (e.g. by calling Model.setCategoryForNodeData), because then some bindings might be evaluated before or after the category has been changed.
Conversions
Sometimes the data value needs to be modified or converted in order to be used as the new value of a GraphObject property. The most common conversion functions are provided for you -- they convert a string to a geometric class: Point.parse, Size.parse, Rect.parse, Margin.parse, Spot.parse, and Geometry.parse. But you can easily define your own conversion function.As an example of a conversion function, let's use a function that adds some text prefixing the data property value:
new go.Binding("text", "say", function(v) { return "I say: " + v; })Although simple conversions cover almost all binding cases, there are some infrequent uses that are covered by "Advanced Conversions", discussed below. Conversion functions must not have any side-effects. Conversion functions may be called frequently, so they should be fast and avoid allocating memory. The order in which conversion functions are called is not specified and may vary.
OneWay and TwoWay Bindings
By default bindings are Binding.OneWay. OneWay bindings are evaluated when the Panel.data property is set or when you call Panel.updateTargetBindings or Model.setDataProperty. OneWay bindings only transfer values from the source to the target.TwoWay bindings are evaluated in the source-to-target direction just as OneWay bindings are evaluated. However when the GraphObject target property is set, the TwoWay bindings are evaluated in the target-to-source direction. There is no point in having a TwoWay binding on a GraphObject property that cannot be set. For efficiency, avoid TwoWay bindings on GraphObject properties that do not change value in your app.
You should not have a TwoWay binding with a source that is a node data object's key property, i.e. on the data property whose name is the same as the value of Model.nodeKeyProperty. Unintentionally changing the node key value to be the same as another node data's key value may cause indeterminate behavior. Furthermore, changing a node data key without changing any references to that node using the key value will result in "dangling" references and inconsistent relationships. You can make that change safely by calling Model.setKeyForNodeData, but not via a data binding.
The target-to-source update can also go through a conversion function. The most common back-conversion functions are provided for you. They convert a geometric class to a string: Point.stringify, Size.stringify, Rect.stringify, Margin.stringify, Spot.stringify, and Geometry.stringify.
It is common to want to update some data properties based on changes to the diagram. For example, as the user changes the Part.location by dragging a Node, you can automatically keep the node's model data in sync using a TwoWay binding.
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)The call to Binding.makeTwoWay changes the Binding.mode to be Binding.TwoWay and specifies the Binding.backConverter function to be the Point.stringify static function.
Because the Binding is on the whole node (template
),
the target object is the whole Node and the target property is "location".
The value of data.loc
will be a string representation of the Node.location
value.
Binding Sources
The target of a Binding is always a property of a GraphObject or a RowColumnDefinition. The source of a Binding is normally a property of a data object in the model. But it is also possible to have the source of a Binding be the shared JavaScript object that is the value of Model.modelData. You can specify such a binding by calling Binding.ofModel, meaning "a binding of a source that is a property of the Model.modelData".As an example, you might want all Nodes to use the same color. It would be possible but not natural to bind to a property on the node data object, because that property would have to be duplicated on all of the node data objects in the model, and updating the property would mean calling Model.setDataProperty on each node data object with the same new value. Furthermore if there happened to be no nodes at all in the model, there would be no place to save the data. Hence using the shared Model.modelData object would be the sensible place for that shared information.
new go.Binding("stroke", "strokeColor").ofModel()and to set or modify that color one would just call, within a transaction:
model.setDataProperty(model.modelData, "strokeColor", "red");That would cause all nodes with that model data binding to be re-evaluated. It is not commonplace to have a TwoWay Binding on "ofModel" Bindings, but that should work. Converters also work with "ofModel" Bindings.
And it is also possible to have the source of a Binding be another GraphObject that is in the same Part. You can enable such a binding by calling Binding.ofObject, meaning "a binding of a source that is a property of a GraphObject". You just have to make sure that object has a unique GraphObject.name or is the Part itself. The source property on the GraphObject has to be settable, and the Part must have a value for Panel.data. (If the source property setter does not notify about property value changes, the binding mechanism will not be invoked. Similarly, if there is no Panel.data, the binding mechanism is not active.)
As a common kind of example of data binding between two properties of GraphObjects, consider this Binding on a Shape which changes the color of the Shape.stroke depending on whether the Node is selected (Part.isSelected):
new go.Binding("stroke", "isSelected", function(s) { return s ? "dodgerblue" : "gray"; }).ofObject()Note the call to Binding.ofObject, which tells the Binding that it should use as the source a GraphObject with a particular name. However that name argument is optional -- supplying no name (or supplying an empty string) will cause the binding to operate with the root GraphObject. In this case that would be the Node itself. Now with this binding whenever the value of Part.isSelected changes, this Shape's stroke changes color. The conversion function is what changes the boolean "isSelected" value to a brush color specifier.
Advanced Conversions
The binding functionality also has more advanced features for less common situations. The source property name may be an empty string, to convert the object as a whole. Conversion functions may take a second argument that takes the object that is bound. For source-to-target conversions, the second argument will be the GraphObject whose property is bound. For target-to-source (back-)conversions, the second argument will be the source data object and the third argument will be the Model.Here's an example of a two-way data-binding using two custom conversion functions working with two separate data properties. First we define the two conversion functions.
function toLocation(data, node) { return new go.Point(data.x, data.y); }; function fromLocation(loc, data, model) { model.setDataProperty(data, "x", loc.x); model.setDataProperty(data, "y", loc.y); };
Then to data-bind the default template's Part.location property to two separate data properties, "x" and "y":
new go.Binding("location", "", toLocation).makeTwoWay(fromLocation)
An empty string argument for the sourceprop parameter indicates
that the whole data object should be passed to the toLocation
function,
rather than the value of some property of that data.
The return value is used as the new value for the Part.location property.
In almost all cases the second argument is not used.
Caution: for efficiency reasons you should try to avoid using an empty source property name.
Such bindings will be evaluated much more frequently than ones whose source is a particular property name.
The binding works normally for the source-to-target direction.
But when the target property is modified it is the source property that is
set with the back-converted property value from the target object.
Because in this example the source property name is the empty string,
and because one cannot replace the whole source data object,
any return value from the conversion function is ignored.
Instead the conversion function has to modify the data object directly,
as this example fromLocation
function does.
Note that because the source property name is the empty string, the binding system will not know
which properties are modified in the call to fromLocation
.
Hence to support undo and redo, in order to make the data changes we have to call
Model.setDataProperty so that the UndoManager can record the change,
including the previous value.
Replacing Items in Arrays
However, although a TwoWay Binding cannot replace the node data object in the Model.nodeDataArray, it is possible to replace an item in an Panel.itemArray. So if your node data were:{ key: 1, items: ["one", "two", "three"] }And if your node template included something like:
$(go.Panel, "Vertical", new go.Binding("itemArray", "items"), { itemTemplate: $(go.Panel, $(go.TextBlock, { editable: true }, new go.Binding("text", "").makeTwoWay()) ) } )Then the user would be able to edit any of the TextBlocks, causing the item Array to be modified, for example resulting in this node data:
{ key: 1, items: ["one", "SOME NEW TEXT HERE", "three"] }
Brush
A Brush holds color information and describes how to draw the inside of a Shape or the stroke of a shape or a TextBlock or the background of any GraphObject.More...
A Brush must not be modified once it has been assigned to a GraphObject, such as the Shape.fill or TextBlock.stroke or GraphObject.background. However, a Brush may be shared by multiple GraphObjects.
ChangedEvent
A ChangedEvent represents a change to an object, typically a GraphObject, but also for model data, a Model, or a Diagram.More... The most common case is for remembering the name of a property and the before-and-after values for that property.
You can listen for changed events on the model using Model.addChangedListener and on the Diagram using Diagram.addChangedListener.
There are four kinds of changes, represented by enumerated values: ChangedEvent.Property (the most common), ChangedEvent.Insert and ChangedEvent.Remove (to represent inserting or removing objects from collections), and ChangedEvent.Transaction (to notify about beginning or ending transactions or undo or redo).
The most common kind of ChangedEvent is a Property change. The name of the property is given by #propertyName. The modified object is given by #object. Use the #oldValue and #newValue properties for the before and after property values.
For an Insert ChangedEvent, the modified collection (often an Array) is a property value on the #object. The #propertyName helps distinguish between different collections on the object. Use the #newValue property to indicate the value that was inserted. Use the #newParam property to indicate where or how, such as an array index or dictionary key.
For a Remove ChangedEvent, the modified collection is a property value on the #object. The #propertyName helps distinguish between different collections on the object. Use the #oldValue property to indicate the value that was removed. Use the #oldParam property to indicate where or how, such as an array index or dictionary key.
Transaction ChangedEvents are generated by the UndoManager. The #propertyName names the nature of the ChangedEvent. For the very first transaction, the property name is "StartingFirstTransaction". This ChangedEvent precedes a ChangedEvent whose property name is "StartedTransaction", which occurs for every top-level transaction.
When ending a transaction, there is first a ChangedEvent whose name is "ComittingTransaction". This is followed by one with either "CommittedTransaction" or "RolledBackTransaction", depending on how the transaction is ending. The #oldValue provides the transaction name and the #object is the Transaction being finished. (Note that the Transaction value may be null if no Transaction is available at that time, perhaps because there were no changes made during the transaction.) That Transaction can be scanned to look for ChangedEvents that you may wish to record in a database, all within a single database transaction.
There are also Transaction ChangedEvents corresponding to "StartingUndo", "FinishedUndo", "StartingRedo", and "FinishedRedo". The #object property provides the Transaction that is about-to-be or just-was undone or redone.
Non-Transaction ChangedEvents are remembered by the UndoManager, if UndoManager.isEnabled, and held in the UndoManager.history as Transactions which hold lists of ChangedEvents. That is why ChangedEvent implements undo and redo of the change that it remembers.
When the ChangedEvent represents a change to a Model, the value of #model is non-null and the value of #diagram is meaningless. If the change is a structural change to the model, the value of #modelChange indicates the kind of change. Currently defined model changed event names include:
- "nodeDataArray", after the model's Model.nodeDataArray is replaced, inserted into or removed from (setting Model.nodeDataArray or calling Model.addNodeData or Model.removeNodeData)
- "nodeKey", after changing a node data's unique key (Model.setKeyForNodeData)
- "nodeCategory", after changing a node data's category (Model.setCategoryForNodeData)
- "linkFromKey", after changing a link data's "from" node key (GraphLinksModel.setFromKeyForLinkData)
- "linkToKey", after changing a link data's "to" node key (GraphLinksModel.setToKeyForLinkData)
- "linkFromPortId", after changing a link data's "from" port (GraphLinksModel.setFromPortIdForLinkData)
- "linkToPortId", after changing a link data's "to" port (GraphLinksModel.setToPortIdForLinkData)
- "linkLabelKeys", after replacing, inserting into, or removing from a link data's array of keys to label nodes (calling GraphLinksModel.setLabelKeysForLinkData, GraphLinksModel.addLabelKeyForLinkData, or GraphLinksModel.removeLabelKeyForLinkData)
- "linkDataArray", after the model's GraphLinksModel.linkDataArray is replaced, inserted into or removed from (setting GraphLinksModel.linkDataArray or calling GraphLinksModel.addLinkData or GraphLinksModel.removeLinkData)
- "nodeGroupKey", after changing a node data's key for a containing group data (GraphLinksModel.setGroupKeyForNodeData)
- "linkCategory", after changing a link data's category (GraphLinksModel.setCategoryForLinkData)
- "nodeParentKey", after changing a node data's "parent" node key (TreeModel.setParentKeyForNodeData)
- "parentLinkCategory", after changing a node data's "parent" link's category(TreeModel.setParentLinkCategoryForNodeData)
- "SourceChanged", for internal implementation use only
When the ChangedEvent represents a change to a Diagram or a GraphObject within a diagram, the value of #diagram is non-null and the values of #model and #modelChange are meaningless.
CircularEdge
This holds CircularLayout-specific information about Links.More...
This class inherits from LayoutEdge.
CircularLayout
This layout positions nodes in a circular arrangement.More...
There are several samples that use CircularLayout.
The layout cannot guarantee that it provides optimal positioning of nodes when trying to minimize link crossings.
If you want to experiment interactively with most of the properties, try the Circular Layout sample.
See samples that make use of CircularLayout in the samples index.
This layout makes use of a LayoutNetwork of CircularVertexes and CircularEdges that normally correspond to the Nodes and Links of the Diagram.
CircularVertex
This holds CircularLayout-specific information about Nodes.More...
This class inherits from LayoutVertex.
ClickCreatingTool
The ClickCreatingTool lets the user create a node by clicking where they want the new node to be.More... By default a double-click is required to start this tool; set #isDoubleClick to false if you want a single-click to create a node.
This tool is a standard mouse-up tool, the ToolManager.clickCreatingTool. However, it will not be able to start running unless you have set the #archetypeNodeData property to an object that can be copied and added to the diagram's model.
This tool does not utilize any Adornments or tool handles. This tool does conduct a transaction when inserting the new node.
ClickSelectingTool
The ClickSelectingTool selects and deselects Parts when there is a click.More... It does this by calling Tool.standardMouseSelect. It is also responsible for handling and dispatching click events on GraphObjects by calling Tool.standardMouseClick.
Note that this tool avoids raising click events on objects that are in temporary layers. This is to prevent parts such as selection adornments from interfering with clicking on selected nodes or links. (Adornments are in the "Adornment" Layer, which Layer.isTemporary.) However this means that if you add a GraphObject.click event handler on a GraphObject in an Adornment, it will not be called. You can get it to be called by setting GraphObject.isActionable to true on that object in the adornment.
This tool is a standard mouse-up tool, the ToolManager.clickSelectingTool.
This tool does not utilize any Adornments or tool handles. This tool does not modify the model or conduct any transaction.
An example customization of this tool is shown in the Tree Map sample, where the Tool.standardMouseSelect method is overridden to permit the user to cycle through the chain of containing groups, changing the selection on each click to the next containing group.
CommandHandler
The Diagram.commandHandler implements various commands such as CommandHandler.deleteSelection or CommandHandler.redo.More... The CommandHandler includes keyboard event handling to interpret key presses as commands.
CommandHandlers cannot be shared amongst multiple Diagrams.
You may define a CommandHandler subclass and override methods. However you must seriously consider calling the base method in order to get its default behavior. There may be situations where not calling the base method may cause subtle bugs, but that depends on the method. Please read the Introduction page on Extensions for how to override methods and how to call a base method.
There is an example custom CommandHandler in the extensions directory: DrawCommandHandler.js, which provides alignment commands and additional behaviors for the arrow keys.
For additional discussion, please read the Introduction page on Commands.
Keyboard Shortcuts
The CommandHandler implements the following command bindings for keyboard input in #doKeyDown:Ctrl-X
&Shift-Del
invoke #cutSelectionCtrl-C
&Ctrl-Insert
invoke #copySelectionCtrl-V
&Shift-Insert
invoke #pasteSelectionDel
&Backspace
invoke #deleteSelectionCtrl-A
invokes #selectAllCtrl-Z
&Alt-Backspace
invoke undoCtrl-Y
&Alt-Shift-Backspace
invoke redoUp
&Down
&Left
&Right
(arrow keys) call Diagram.scrollPageUp
&PageDown
call Diagram.scrollHome
&End
call Diagram.scrollSpace
invokes #scrollToPartCtrl-- & Keypad--
(minus) invoke #decreaseZoomCtrl-+ & Keypad-+
(plus) invoke #increaseZoomCtrl-0
invokes #resetZoomShift-Z
invokes #zoomToFit; repeat to return to the original scale and positionCtrl-G
invokes #groupSelectionCtrl-Shift-G
invokes #ungroupSelectionF2
invokes #editTextBlockMenu Key
invokes #showContextMenuEsc
invokes #stopCommand
On a Macintosh the Command key is used as the modifier instead of the Control key.
On touch devices there is a default context menu that shows many commonly-used commands when you hold a finger down on the diagram.
ContextMenuTool
The ContextMenuTool is used to create and show a context menu.More... It automatically disables any browser context menu.
Define context menus on individual GraphObjects by setting GraphObject.contextMenu. Define a context menu for the diagram background by setting Diagram.contextMenu.
This tool is a standard mouse-down tool, the ToolManager.contextMenuTool.
This tool does not utilize any tool handles. This tool does not modify the model or conduct any transaction, although any code invoked by context menu commands might do so.
There are examples of customizing this tool in the Custom Context Menu and HTML LightBox Context Menu samples.
If you want to programmatically show a context menu for a particular GraphObject or for the whole diagram, call CommandHandler.showContextMenu. That command method is also invoked by the Menu key on the keyboard.
Normally this shows a context menu (if available) on a right-mouse-up event. If you want it to happen on a right-mouse-down event, you'll need to move this tool from the ToolManager.mouseUpTools list to the ToolManager.mouseDownTools list:
myDiagram.toolManager.mouseDownTools.add(myDiagram.toolManager.replaceTool("ContextMenu", null));
Diagram
A Diagram is associated with an HTML DIV element.More... Constructing a Diagram creates an HTML Canvas element which it places inside of the given DIV element, in addition to several helper DIVs. GoJS will manage the contents of this DIV -- you should not modify the contents of the DIV, although you may style the given DIV (background, border, etc) and position and size it as needed.
Minimal Diagram construction looks like this. HTML:
<div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div>
JavaScript:
var $ = go.GraphObject.make; // for conciseness myDiagram = $(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element { initialContentAlignment: go.Spot.Center, // center the content "undoManager.isEnabled": true // enable undo & redo });
The diagram will draw onto an HTML Canvas element, created inside the Diagram DIV.
Each Diagram holds a set of Layers each of which holds some number of Parts such as Nodes and Links. Each Part consists of GraphObjects such as TextBlocks and Shapes and Panels holding yet more GraphObjects.
A Diagram and its Parts provide the visual representation of a Model that holds JavaScript data objects for the nodes and the links. The model provides the way to recognize the relationships between the data.
Two Diagrams can display and manipulate the same Model. (Example)
A diagram will automatically create Nodes and Links corresponding to the model data. The diagram has a number of named templates it uses to create the actual parts: #nodeTemplateMap, #groupTemplateMap, and #linkTemplateMap. Each template may have some data Bindings that set the part's GraphObjects' properties based on the value of properties of the data.
A simple Node template and Model data (both nodes and links) may look like this:
var $ = go.GraphObject.make; // for conciseness // define a simple Node template myDiagram.nodeTemplate = $(go.Node, "Auto", // the Shape will go around the TextBlock $(go.Shape, "RoundedRectangle", // Shape.fill is bound to Node.data.color new go.Binding("fill", "color")), $(go.TextBlock, { margin: 3 }, // some room around the text // TextBlock.text is bound to Node.data.key new go.Binding("text", "key")) ); // create the model data that will be represented by Nodes and Links myDiagram.model = new go.GraphLinksModel( [ { key: "Alpha", color: "lightblue" }, { key: "Beta", color: "orange" }, { key: "Gamma", color: "lightgreen" }, { key: "Delta", color: "pink" } ], [ { from: "Alpha", to: "Beta" }, { from: "Alpha", to: "Gamma" }, { from: "Beta", to: "Beta" }, { from: "Gamma", to: "Delta" }, { from: "Delta", to: "Alpha" } ]);
The above code is used to make the Minimal sample, a simple example of creating a Diagram and setting its model.
Read about models on the Using Models page in the introduction. A diagram is responsible for scrolling (#position) and zooming (#scale) all of the parts that it shows. Each Part occupies some area given by its GraphObject.actualBounds.
The union of all of the parts' bounds constitutes the #documentBounds. The document bounds determines the area that the diagram can be scrolled to. There are several properties that you can set, such as #initialContentAlignment, that control the initial size and position of the diagram contents.
At any later time you can also explicitly set the #position and/or #scale to get the appearance that you want. But you may find it easier to call methods to get the desired effect. For example, if you want to make a particular Node be centered in the viewport, call either #centerRect or #scrollToRect with the Node's GraphObject.actualBounds, depending on whether or not you want the view to be scrolled if the node is already in view.
Read in the Introduction about Viewports and the Initial Viewport. You can have the diagram perform automatic layouts of its nodes and links by setting #layout to an instance of the Layout subclass of your choice. The default #layout is an instance of the Layout base class that ignores links and only positions Nodes that do not have a location. This default layout will allow you to programmatically position nodes (including by loading from a database) and will also allow the user to manually position nodes using the DraggingTool.
If you do supply a particular layout as the #layout, you can control which Parts it operates on by setting Part.isLayoutPositioned. Normally, of course, it works on all top-level nodes and links. The layout is performed both after the model is first loaded as well as after any part is added or removed or changes visibility or size. You can disable the initial layout by setting Layout.isInitial to false. You can disable later automatic layouts by setting Layout.isOngoing to false.
See the Layouts page in the Introduction for a summary of layout behavior.
A diagram maintains a collection of selected parts, the Diagram.selection. To select a Part you set its Part.isSelected property to true.
There are many properties, named "allow...", that control what operations the user may perform on the parts in the diagram. These correspond to the same named properties on Layer that govern the behavior for those parts in a particular layer. Furthermore for some of these properties there are corresponding properties on Part, named "...able", that govern the behavior for that individual part. For example, the #allowCopy property corresponds to Layer.allowCopy and to the property Part.copyable. The Part.canCopy predicate is false if any of these properties is false.
See the Permissions page for a more thorough discussion.
The #commandHandler implements various standard commands, such as the CommandHandler.deleteSelection method and the CommandHandler.canDeleteSelection predicate.
See the Commands page for a listing of keyboard commands and the use of commands in general.
The diagram supports modular behavior for mouse events by implementing "tools". All mouse and keyboard events are represented by InputEvents and redirected to the #currentTool. The default tool is an instance of ToolManager which keeps three lists of mode-less tools: ToolManager.mouseDownTools, ToolManager.mouseMoveTools, and ToolManager.mouseUpTools. The ToolManager searches these lists when a mouse event happens to find the first tool that can run. It then makes that tool the new #currentTool, where it can continue to process input events. When the tool is done, it stops itself, causing the #defaultTool to be the new #currentTool.
Mouse-down tools include:
- ToolManager.actionTool, to support objects like "buttons"
- ToolManager.relinkingTool, to reconnect an existing link
- ToolManager.linkReshapingTool, to modify the route of an existing link
- ToolManager.resizingTool, to change the size of an object
- ToolManager.rotatingTool, to change the angle of an object
- ToolManager.linkingTool, to draw a new link
- ToolManager.draggingTool, to move or copy the selection
- ToolManager.dragSelectingTool, to select parts within a rectangular area
- ToolManager.panningTool, to pan the diagram
- ToolManager.contextMenuTool, to manage context menus
- ToolManager.textEditingTool, to support in-place text editing
- ToolManager.clickCreatingTool, to create new parts where the user clicks
- ToolManager.clickSelectingTool, to select parts
You can also run a tool in a modal fashion by explicitly setting #currentTool. That tool will keep running until some code replaces the #currentTool/ This normally happens when the current tool calls Tool.stopTool, such as on a mouse-up event.
See the Tools page for a listing of predefined tools and how they operate.
A diagram raises various DiagramEvents when interesting things happen that may have affected the whole diagram. See the documentation for DiagramEvent for a complete listing.
DiagramEvent
A DiagramEvent represents a more abstract event than an InputEvent.More... They are raised on the Diagram class. One can receive such events by registering a DiagramEvent listener on a Diagram by calling Diagram.addDiagramListener. The listener function, when called, will be passed an instance of a DiagramEvent. Use the #name property to decide what kind of diagram event it is. The #diagram property refers to the Diagram, and you can get additional information from that, such as the Diagram.lastInput, which in turn provides information such as InputEvent.documentPoint that may be relevant for that kind of DiagramEvent.
The #subject and #parameter properties optionally provide additional information about the diagram event. The subject could be a collection of Parts or it could be an individual object such as a Link or a GraphObject within a Node. Everything depends on the kind of diagram event that it is.
Some DiagramEvents such as "ObjectSingleClicked" or "BackgroundDoubleClicked" are normally associated with InputEvents. Some DiagramEvents such as "SelectionMoved" or "PartRotated" are associated with the results of Tool-handled gestures or CommandHandler actions. Some DiagramEvents are not necessarily associated with any input events at all, such as "ViewportBoundsChanged", which can happen due to programmatic changes to the Diagram.position and Diagram.scale properties.
DiagramEvents that occur during a transaction may be called before the state of the whole diagram has settled down. This often means that such events occur before a layout, so nodes may not have their final positions, links may not have their final routes, and the Diagram.documentBounds and Diagram.viewportBounds may not yet have been updated. Such events may make additional changes to the diagram, which may in turn result in additional side-effects.
DiagramEvents that occur outside of a transaction require you to start and commit a transaction around any side-effects that you want to do. However, some DiagramEvents do not allow you to make any changes to the Diagram or Model.
Currently defined diagram event names include:
- "AnimationStarting", an animation is about to start;
do not modify the Diagram or its Model in the event listener. - "AnimationFinished", an animation just completed;
do not modify the Diagram or its Model in the event listener. - "BackgroundSingleClicked", a click that was not on any Part;
if you make any changes, start and commit your own transaction. - "BackgroundDoubleClicked", a double-click that was not on any Part;
if you make any changes, start and commit your own transaction. - "BackgroundContextClicked", a context-click that was not on any Part;
if you make any changes, start and commit your own transaction. - "ChangingSelection", an operation is about to change the Diagram.selection collection;
do not make any changes to the selection or the diagram in the event listener; note that just setting Part.isSelected will not raise this event, but tools and commands will. - "ChangedSelection", an operation has just changed the Diagram.selection collection;
do not make any changes to the selection or the diagram in the event listener; note that just setting Part.isSelected will not raise this event, but tools and commands will. - "ClipboardChanged", Parts have been copied to the clipboard by CommandHandler.copySelection;
the DiagramEvent.subject is the collection of Parts;
if you make any changes, start and commit your own transaction. - "ClipboardPasted", Parts have been copied from the clipboard into the Diagram by CommandHandler.pasteSelection;
the DiagramEvent.subject is the Diagram.selection,
and this is called within a transaction. - "DocumentBoundsChanged", the area of the diagram's Parts, Diagram.documentBounds, has changed;
the DiagramEvent.parameter is the old Rect - "ExternalObjectsDropped", Parts have been copied into the Diagram by drag-and-drop from outside of the Diagram;
the DiagramEvent.subject is the set of Parts that were dropped (which is also the Diagram.selection), the DiagramEvent.parameter is the source Diagram, and this is called within a transaction. - "InitialLayoutCompleted", the whole diagram layout has updated for the first time since a major change to the Diagram, such as replacing the Model;
if you make any changes, you do not need to perform a transaction. - "LayoutCompleted", the whole diagram layout has just been updated;
if you make any changes, you do not need to perform a transaction. - "LinkDrawn", the user has just created a new Link using LinkingTool;
the DiagramEvent.subject is the new Link,
and this is called within a transaction. - "LinkRelinked", the user has just reconnected an existing Link using RelinkingTool;
the DiagramEvent.subject is the modified Link,
the DiagramEvent.parameter is the GraphObject port that the link was disconnected from,
and this is called within a transaction. - "LinkReshaped", the user has just rerouted an existing Link using LinkReshapingTool;
the DiagramEvent.subject is the modified Link,
the DiagramEvent.parameter is the List of Points of the link's original route, and this is called within a transaction. - "Modified", the Diagram.isModified property has been set to a new value;
do not modify the Diagram or its Model in the event listener. - "ObjectSingleClicked", a click that occurred on a GraphObject;
the DiagramEvent.subject is the GraphObject;
if you make any changes, start and commit your own transaction. - "ObjectDoubleClicked", a double-click that occurred on a GraphObject;
the DiagramEvent.subject is the GraphObject;
if you make any changes, start and commit your own transaction. - "ObjectContextClicked", a context-click that occurred on a GraphObject;
the DiagramEvent.subject is the GraphObject;
if you make any changes, start and commit your own transaction. - "PartCreated", the user inserted a new Part by ClickCreatingTool;
the DiagramEvent.subject is the new Part,
and this is called within a transaction. - "PartResized", the user has changed the size of a GraphObject by ResizingTool;
the DiagramEvent.subject is the GraphObject,
the DiagramEvent.parameter is the original Size,
and this is called within a transaction. - "PartRotated", the user has changed the angle of a GraphObject by RotatingTool;
the DiagramEvent.subject is the GraphObject,
the DiagramEvent.parameter is the original angle in degrees,
and this is called within a transaction. - "SelectionMoved", the user has moved selected Parts by DraggingTool;
the DiagramEvent.subject is the moved Diagram.selection,
and this is called within a transaction. - "SelectionCopied", the user has copied selected Parts by DraggingTool;
the DiagramEvent.subject is the newly copied Diagram.selection,
and this is called within a transaction. - "SelectionDeleted", the user has deleted selected Parts by CommandHandler.deleteSelection;
the DiagramEvent.subject is the collection of Parts that were deleted,
and this is called within a transaction. - "SelectionGrouped", the user has made a new Group out of the selected Parts by CommandHandler.groupSelection;
the DiagramEvent.subject is the new Group,
and this is called within a transaction. - "SelectionUngrouped", the user has removed a selected Group but kept its members by CommandHandler.ungroupSelection;
the DiagramEvent.subject is the collection of Groups that were ungrouped,
the DiagramEvent.parameter is the collection of former member Parts that were ungrouped,
and this is called within a transaction. - "SubGraphCollapsed", the user has collapsed selected Groups by CommandHandler.collapseSubGraph;
the DiagramEvent.subject is the collection of Groups that were collapsed,
and this is called within a transaction. - "SubGraphExpanded", the user has expanded selected Groups by CommandHandler.expandSubGraph;
the DiagramEvent.subject is the collection of Groups that were expanded,
and this is called within a transaction. - "TextEdited", the user has changed the string value of a TextBlock by TextEditingTool;
the DiagramEvent.subject is the edited TextBlock,
the DiagramEvent.parameter is the original string,
and this is called within a transaction. - "TreeCollapsed", the user has collapsed selected Nodes with subtrees by CommandHandler.collapseTree;
the DiagramEvent.subject is the collection of Nodes that were collapsed,
and this is called within a transaction. - "TreeExpanded", the user has expanded selected Nodes with subtrees by CommandHandler.expandTree;
the DiagramEvent.subject is the collection of Nodes that were expanded,
and this is called within a transaction. - "ViewportBoundsChanged", the visible area of the Diagram, Diagram.viewportBounds, has changed;
the DiagramEvent.subject is an object whose "scale" property is the old Diagram.scale value, whose "position" property is the old Diagram.position value, and whose "bounds" property is the old Diagram.viewportBounds value; the DiagramEvent.parameter is also the old viewportBounds Rect.
DraggingTool
The DraggingTool is used to move or copy selected parts with the mouse.More... This sets the Part.location property; you may want to save the location to the model by using a TwoWay Binding on the "location" property in your Parts/Nodes/Groups templates.
Dragging the selection moves parts for which Part.canMove is true. If the user holds down the Control key (Option key on Mac), this tool will make a copy of the parts being dragged, for those parts for which Part.canCopy is true.
When the drag starts it calls #computeEffectiveCollection to find the actual collection of Parts to be dragged. Normally this collection includes not only the Diagram.selection, but also parts that belong to those selected parts, such as members of groups. If #dragsTree is true, the effective collection also includes all of the nodes and links that constitute the subtree starting from selected nodes. The result of #computeEffectiveCollection is not a Set but a Map which remembers the original Part.location for all of the dragged parts. This map is saved as the value of #draggedParts.
During the drag if the user holds down the Control/Option key this tool makes a copy of the #draggedParts and proceeds to drag it around. (It only copies the Diagram.selection, not the whole effective collection, if #copiesEffectiveCollection is false.) The collection of copied parts is held by #copiedParts. It too is a Map remembering the original locations of the parts. #copiedParts will be null when this tool is moving (not copying) at the moment.
Each Part's movement is limited by the #computeMove method. By default it limits the Part.location to be within the bounds given by Part.minLocation and Part.maxLocation. (Those default to minus Infinity to plus Infinity.) As a further convenience, the value of NaN in minLocation and maxLocation cause #computeMove to use the part's current location. So, for example, an easy way to declare that the user may only drag a node horizontally is to just set:
$(go.Node, . . . { minLocation: new go.Point(-Infinity, NaN), maxLocation: new go.Point(Infinity, NaN) }, . . . )
If you set #isGridSnapEnabled to true, dragged or copied parts will be snapped to points on a grid. The snapping occurs continuously during a drag unless you set #isGridSnapRealtime to false. Normally the grid points come from the Diagram.grid, even if that grid is not GraphObject.visible. However you can override those grid's properties for the snapping grid cell size and offset by setting the properties here: #gridSnapCellSize and #gridSnapOrigin. This computes the point to snap to for each dragged part. The resulting point is used as the new Part.location.
For the most general control over where a part may be dragged, either set the Part.dragComputation property or override #computeMove. For the common case of wanting to keep member nodes within the Group that they are members of, you can do something like:
// this is a Part.dragComputation function for limiting where a Node may be dragged function stayInGroup(part, pt, gridpt) { // don't constrain top-level nodes var grp = part.containingGroup; if (grp === null) return pt; // try to stay within the background Shape of the Group var back = grp.findObject("SHAPE"); if (back === null) return pt; // allow dragging a Node out of a Group if the Shift key is down //if (part.diagram.lastInput.shift) return pt; var p1 = back.getDocumentPoint(go.Spot.TopLeft); var p2 = back.getDocumentPoint(go.Spot.BottomRight); var b = part.actualBounds; var loc = part.location; // find the padding inside the group's placeholder that is around the member parts var m = (grp.placeholder !== null ? grp.placeholder.padding : new go.Margin(0)); // now limit the location appropriately var x = Math.max(p1.x + m.left, Math.min(pt.x, p2.x - m.right - b.width - 1)) + (loc.x-b.x); var y = Math.max(p1.y + m.top, Math.min(pt.y, p2.y - m.bottom - b.height - 1)) + (loc.y-b.y); return new go.Point(x, y); }Note that this expects there to be a "SHAPE" object within the Group's visual tree that delimits where the part may be dragged within the group. This also expects that Group.computesBoundsIncludingLinks is false. Then in your node template(s), just set:
$(go.Node, . . ., { dragComputation: stayInGroup }, . . . )
This tool does not utilize any Adornments or tool handles. If the drag is successful, it raises the "SelectionMoved" or "SelectionCopied" DiagramEvent and produces a "Move" or a "Copy" transaction.
If you want to programmatically start a new user mouse-gesture to drag a particular existing node, you can set the #currentPart property and then start and activate the tool.
var tool = myDiagram.toolManager.draggingTool; tool.currentPart = ...; myDiagram.currentTool = tool; tool.doActivate();
DragSelectingTool
The DragSelectingTool lets the user select multiple parts within a rectangular area drawn by the user.More... There is a temporary part, the #box, that shows the current area encompassed between the mouse-down point and the current mouse point. The default drag selection box is a magenta rectangle. You can change the #box to customize its appearance -- see its documentation for an example.
This tool is a standard mouse-move tool, the ToolManager.dragSelectingTool. However this cannot start running unless there has been a motionless delay after the mouse-down event of at least #delay milliseconds.
This tool does not utilize any Adornments or tool handles, but it does temporarily add the #box part to the diagram. This tool does not modify the model or conduct any transaction.
Selection occurs on a mouse-up when it calls #selectInRect with the value of #computeBoxBounds. Selectable parts are selected when their bounds fall entirely within the rectangle, unless #isPartialInclusion is set to true.
For customizing the DragSelectingTool, see Introduction to the DragSelectingTool.
If you implement your own drag-in-the-background-to-do-something tool, you may need to disable this tool or insert your new tool in the ToolManager.mouseMoveTools list before this tool, in order for your tool to run. There are examples of such tools defined in the extensions directory: Realtime Drag Selecting Tool, Drag Creating Tool, and Drag Zooming Tool.
ForceDirectedEdge
This holds ForceDirectedLayout-specific information about Links.More...
This class inherits from LayoutEdge.
ForceDirectedLayout
Force-directed layout treats the graph as if it were a system of physical
bodies with forces acting on them and between them.More...
The algorithm seeks a configuration of the bodies with locally minimal energy,
i.e. vertex positions such that the sum of the forces on each vertex is zero.
There are many samples that use ForceDirectedLayout.
The layout cannot guarantee that it provides optimal positioning of nodes.
Nodes will normally not overlap each other, but when there is a dense interconnectivity overlaps might not be avoidable.
If you want to experiment interactively with most of the properties, try the Force Directed Layout sample.
See samples that make use of ForceDirectedLayout in the samples index.
This layout makes use of a LayoutNetwork of ForceDirectedVertexes and ForceDirectedEdges that normally correspond to the Nodes and Links of the Diagram.
ForceDirectedVertex
This holds ForceDirectedLayout-specific information about Nodes.More...
This class inherits from LayoutVertex.
Geometry
The Geometry class is used to define the "shape" of a Shape.More... A Geometry can be simple straight lines, rectangles, or ellipses. A Geometry can also be an arbitrarily complex path, consisting of a list of PathFigures.
A Geometry must not be modified once it has been used by a Shape. However, a Geometry may be shared by multiple Shapes.
It is commonplace to create Geometries using geometry path string syntax: Geometry Path Strings. However it is much more efficient to create Geometries programmatically. One way to do that is illustrated by several of the samples that evaluate JavaScript such as:
new go.Geometry() .add(new go.PathFigure(p.x, p.y) .add(new go.PathSegment(go.PathSegment.Arc, -sweep/2, sweep, 0, 0, radius+layerThickness, radius+layerThickness)) .add(new go.PathSegment(go.PathSegment.Line, q.x, q.y)) .add(new go.PathSegment(go.PathSegment.Arc, sweep/2, -sweep, 0, 0, radius, radius).close()));See samples that make use of Geometries in the samples index.
GraphLinksModel
GraphLinksModels support links between nodes and grouping nodes and links into subgraphs.More... GraphLinksModels hold node data and link data in separate arrays. Node data is normally represented in a Diagram by instances of Node, but they could be represented by simple Parts or by Groups. Link data should be represented by instances of Link.
Each link data object is assumed to have two values, one referring to the node that the link is coming from and one that the link is going to. The #linkFromKeyProperty property names the property on the link data whose value is the key of the "from" node. The #linkToKeyProperty property names the property on the link data whose value is the key of the "to" node. The default values for these properties are "from" and "to" respectively.
For example, one can define a graph consisting of two nodes with one link connecting them:
model.nodeDataArray = [ { key: "Alpha" }, { key: "Beta" } ]; model.linkDataArray = [ { from: "Alpha", to: "Beta" } ];
If you want to have subgraphs in your diagram, where a group node contains some number of nodes and links, you need to declare that some node data actually represent groups, and you need to provide a reference from a member node data to its containing group node data. The #nodeIsGroupProperty property names the property on a node data that is true if that node data represents a group. The #nodeGroupKeyProperty property names the property on a node data whose value is the key of the containing group's node data. The default values for these properties are "isGroup" and "group" respectively.
For example, one can define a graph consisting of one group containing a subgraph of two nodes connected by a link, with a second link from that group to a third node that is not a member of that group:
model.nodeDataArray = [ { key: "Group1", isGroup: true}, { key: "Alpha", group: "Group1" }, { key: "Beta", group: "Group1" }, { key: "Gamma" } ]; model.linkDataArray = [ { from: "Alpha", to: "Beta" }, { from: "Group1", to: "Gamma" } ];
GraphLinksModels also support distinguishing the "port" element of a node to which a link can connect, at either end of the link. This identification is a string that names the "port" element in the node. However, you need to set the #linkFromPortIdProperty and/or #linkToPortIdProperty properties before the model is able to get the "port id" information from the link data.
For example, one can define a graph consisting of a "subtraction" node and two inputs and one output. The "subtraction" node has two distinct inputs called "subtrahend" and "minuend"; the output is called "difference".
model.linkFromPortIdProperty = "fromPort"; // necessary to remember portIds model.linkToPortIdProperty = "toPort"; model.nodeDataArray = [ { key: 1, constant: 5 }, // a constant input node { key: 2, constant: 2 }, // another constant node { key: 3, operation: "subtract" }, { key: 4, value: 3 } // the output node ]; model.linkDataArray = [ { from: 1, to: 3, toPort: "subtrahend" }, { from: 2, to: 3, toPort: "minuend" }, { from: 3, to: 4, fromPort: "difference" } ];In this case links connected to node 3 (which is the subtraction operation) are distinguished by port id. The connections to the other nodes do not have any port identification, presumably because there is only one port on those nodes, representing the node value.
Note that there is no requirement that the link data objects have any kind of unique identifier, unlike for node data. There is no expectation that there be references to link data in the model, so there is no need for such an identifier. When there are multiple links connecting two ports, the only way to distinguish the links in the model is by reference to the particular link data object. This is why there are two methods on the Diagram class for Nodes, Diagram.findNodeForKey and Diagram.findNodeForData, but there is only the one method for Links, Diagram.findLinkForData.
However you may wish to have the model maintain string or number identifiers on the link data just as all models do for node data. To get that behavior, so that you can call #findLinkDataForKey, you need to set #linkKeyProperty to be a non-empty string. Just as with the assignment of node keys, you can customize the assignment of link keys by setting #makeUniqueLinkKeyFunction to a function that returns a unique identifier.
This model does not support the modification of whether a node data object is a group.
This model cannot detect the modification of the #linkDataArray array or the modification of any link data object. If you want to add or remove link data from the #linkDataArray, call the #addLinkData or #removeLinkData methods. If you want to modify the node a link connects to, call the #setFromKeyForLinkData and/or #setToKeyForLinkData methods. If you want to change the membership of a node data in a group, call the #setGroupKeyForNodeData method.
GraphObject
This is the abstract base class for all graphical objects.More... Classes inheriting from GraphObject include: Shape, TextBlock, Picture, and Panel. From the Panel class the Part class is derived, from which the Node and Link classes derive.
It is very common to make use of the static function GraphObject.make in order to build up a visual tree of GraphObjects. You can see many examples of this throughout the Introduction, starting at Building Objects, and the Samples, starting with Minimal Sample.
Since GraphObject is an abstract class, programmers do not create GraphObjects themselves, but this class defines many properties used by all kinds of GraphObjects.
The only visual properties on GraphObject are #background and #areaBackground. However one can control whether the GraphObject is drawn at all by setting #visible, or by setting #opacity to zero if you still want the GraphObject to occupy space. Also, if you want to control whether any mouse or touch events "see" the GraphObject, you can set #pickable to false.
For more information about specifying how things get drawn, see the properties on the Shape, TextBlock, and Picture classes.
GraphObject Sizing
GraphObject defines most of the properties that cause objects to size themselves differently. The most prominent ones include:
- The #desiredSize, #minSize, and #maxSize properties are used to explicitly set or limit the size of visual elements. #width and #height are convenience properties that set the #desiredSize width and height, respectively.
- The #angle and #scale properties are used to transform visual elements.
- The #stretch property determines how a GraphObject will fill its visual space, contextually granted to it by its containing Panel. Top-level (Part) GraphObjects are not affected by this property because they are always granted infinite space.
All GraphObjects in a Diagram are measured and then arranged by their containing Panels in a tree-like fashion. After measuring and arranging, a GraphObject will have valid values for the read-only properties #naturalBounds, #measuredBounds, and #actualBounds.
- The #naturalBounds of a GraphObject describe its local size, without any transformations (#scale, #angle) affecting it.
- The #measuredBounds of a GraphObject describe its size relative to its containing Panel.
- The #actualBounds of a GraphObject describe its position and given size inside of its panel. This size may be smaller than #measuredBounds, for instance if a GraphObject with a large #desiredSize is placed in a Panel of a smaller #desiredSize. Smaller #actualBounds than #measuredBounds typically means an object will be cropped.
See the Introduction page on sizing for usage information and examples.
GraphObject Size and Position within Panel
Several GraphObject properties guide the containing Panel for how to size and position the object within the panel.- The #alignment specifies where the object should be relative to some area of the panel. For example, an alignment value of Spot.BottomRight means that the GraphObject should be at the bottom-right corner of the panel.
- The #alignmentFocus specifies precisely which point of the GraphObject should be aligned at the #alignment spot.
- The #column and #row properties are only used by Panel.Table panels, to indicate where the GraphObject should be.
- The #columnSpan and #rowSpan properties tell the Panel.Table panel how large the GraphObject should be.
- The #isPanelMain property indicates to some kinds of Panels that the GraphObject is the "primary" object that other panel children should be measured with or positioned in.
- The #margin property tells the containing Panel how much extra space to put around this GraphObject.
- The #position property is used to determine the relative position of GraphObjects when they are elements of a Panel.Position panel.
See the Introduction page on Panels and Table Panels for an overview of the capabilities.
Top-level GraphObjects are Parts
A Part is a derived class of GraphObject representing a top-level object. All top-level GraphObjects must be Parts, and Node, Link, Group, and Adornment derive from Part. The position of a Part determines the point of the Part's top-left corner in document coordinates. See also Part.location, which supports an way to specify the position based on a different spot of a different element within the Part.
There are several read-only properties that help navigate up the visual tree.
- #panel returns the Panel that directly contains this GraphObject
- #part returns the Part that this GraphObject is in, perhaps via intervening Panels; this is frequently used in order to get to the model data, Panel.data
- #layer returns the Layer that this GraphObject's Part is in
- #diagram returns the Diagram that this GraphObject's Part's Layer is in
See the Visual Tree sample for a diagram displaying the visual tree of a simple diagram.
User Interaction
GraphObjects have several properties enabling dynamic customizable interaction. There are several definable functions that execute on input events: #mouseDragEnter, #mouseDragLeave, #mouseDrop, #mouseEnter, #mouseHold, #mouseHover, #mouseLeave, and #mouseOver. For example, you could define mouse enter-and-leave event handlers to modify the appearance of a link as the mouse passes over it:
myDiagram.linkTemplate = $(go.Link, $(go.Shape, { strokeWidth: 2, stroke: "gray" }, // default color is "gray" { // here E is the InputEvent and OBJ is this Shape mouseEnter: function(e, obj) { obj.strokeWidth = 4; obj.stroke = "dodgerblue"; }, mouseLeave: function(e, obj) { obj.strokeWidth = 2; obj.stroke = "gray"; } }));
There are #click, #doubleClick, and #contextClick functions that execute when a user appropriately clicks the GraphObject. These click functions are called with the InputEvent as the first argument and this GraphObject as the second argument. For example, you could define a click event handler on a Node that goes to another page:
myDiagram.nodeTemplate = $(go.Node, "Auto", $(go.Shape, "RoundedRectangle", new go.Binding("fill", "color")), $(go.TextBlock, { name: "TB", margin: 3 }, new go.Binding("text", "key")), { // second arg will be this GraphObject, which in this case is the Node itself: click: function(e, node) { window.open("https://en.wikipedia.org/Wiki/" + node.data.key); } });
Note: you may prefer defining DiagramEvent listeners on the Diagram rather than on individual GraphObjects. DiagramEvents also include more general events that do not necessarily correspond to input events.
The properties #actionCancel, #actionDown, #actionMove, and #actionUp define functions to execute when the GraphObject's #isActionable property is set to true (default false). See the ActionTool for more detail.
See the Introduction page on Events for a more general discussion.
GraphObjects as Ports
In GoJS, Links can only connect to elements within a Node that are specified as "ports", and by default the only port is the Node itself. Setting the #portId of a GraphObject inside a Node allows that object to act as a port. Note: the only kind of model that can save which port a link is connected with, i.e. portIds that are not an empty string, is a GraphLinksModel whose GraphLinksModel.linkFromPortIdProperty and GraphLinksModel.linkToPortIdProperty have been set to name properties on the link data objects.
GraphObjects have several properties that are only relevant when they are acting as ports. These port-related properties are:
- #portId, which must be set to a string that is unique within the Node, in order for this GraphObject to be treated as a "port", rather than the whole node
- #fromSpot and #toSpot, where a link should connect with this port
- #fromEndSegmentLength and #toEndSegmentLength, the length of the link segment adjacent to this port
- #fromShortLength and #toShortLength, the distance the link should terminate before touching this port
- #fromLinkable and #toLinkable, whether the user may draw links connecting with this port
- #fromLinkableDuplicates and #toLinkableDuplicates, whether the user may draw multiple links between the same pair of ports
- #fromLinkableSelfNode and #toLinkableSelfNode, whether the user may draw a link between ports on the same node
- #fromMaxLinks and #toMaxLinks, to limit the number of links connecting with this port in a particular direction
See the Introduction page on ports and link connection points for port usage information and examples.
GraphObjects as labels on a Link
GraphObjects can also be used as "labels" on a Link. In addition to the #alignmentFocus property, these properties direct a Link Panel to position a "label" at a particular point along the route of the link, in a particular manner:
- #segmentIndex, which segment the label should be on
- #segmentFraction, how far along the segment the label should be
- #segmentOffset, where the label should be positioned relative to the segment
- #segmentOrientation, how the label should be rotated relative to the angle of the segment
See the Introduction page on link labels for examples of how to make use of labels on Links.
Interactive Behavior
There are several properties that specify fairly high-level interactive behavior:
For more information, please read the Introduction page about Context Menus and the page about ToolTips.
Also see the Basic sample for examples of how to show context menus and tooltips.
GridLayout
This simple layout places all of the Parts in a grid-like arrangement, ordered, spaced apart,
and wrapping as needed.More... It ignores any Links connecting the Nodes being laid out.
There are many samples that use GridLayout.
Every Palette uses a GridLayout by default.
If you want to experiment interactively with most of the properties, try the Grid Layout sample.
See samples that make use of GridLayout in the samples index.
By default this layout will sort all of the Parts alphabetically
(comparing Part.text values, respecting case)
and position them left-to-right, separated by #spacing.width
,
until they do not fit in the current row.
At that time it starts a new row, separated from the previous row by #spacing.height
.
There is a uniform cell size equal to the maximum Part width (plus spacing width)
and the maximum part height (plus spacing height).
At least one part is placed in each row, even if the part by itself is wider than the wrapping width.
You can specify values for the #cellSize width
and height
.
If a part is wider than the cell size, it spans more than one cell in the row.
You can also specify a value for the #wrappingWidth,
which will be used instead of the diagram's viewport width, to control when each row is considered "full".
The value of Layout.isViewportSized will be true when
the value of #wrappingWidth is NaN
.
This causes the layout to be performed again automatically as the viewport changes size.
You can also set #wrappingColumn to limit the number of items in each row. Both the #wrappingWidth and the #wrappingColumn are respected when deciding when to wrap to the next row.
This layout is sufficiently simple that it does not use a LayoutNetwork.
Group
A Group is a Node that can contain a subgraph of Nodes and Links,
which are members of the group.More...
For more discussion, see Introduction to Groups.
See samples that make use of Groups in the samples index.
Although you can create a Group and Diagram.add it to a Diagram, this does not update the Model. It is more common to create a group by adding a node data object to the model by calling Model.addNodeData. For example:
myDiagram.startTransaction("make new group"); myDiagram.model.addNodeData({ key: "Omega", isGroup: true }); myDiagram.commitTransaction("make new group");
This will cause a Group to be created (copying the template found in Diagram.groupTemplateMap),
added to the Diagram in some Layer (based on Part.layerName), and bound to the group data
(resulting in Panel.data referring to that group data object).
Note that the JavaScript object includes setting isGroup
to true,
to indicate that the object represents a Group rather than a regular Node or simple Part.
The member Parts of a Group, which you can access as the #memberParts collection, belong to the group but are not in the visual tree of the group. All Parts are directly in Layers -- they cannot be inside a Panel. This allows group member parts to be in layers different from the group's layer.
You can change the membership of a Node or a simple Part in a Group by setting
its Part.containingGroup property.
This is done automatically for you by the diagram if you initialize the group
property on the node data
in the model to be the key of the containing group node data.
Thus you should do something like:
myDiagram.startTransaction("add new member"); myDiagram.model.addNodeData({ group: someexistinggroup.data.key, ... }); myDiagram.commitTransaction("add new member");
where you would make sure the node data object included all of the properties you need. You can also change the relationship dynamically by calling GraphLinksModel.setGroupKeyForNodeData.
The membership of Links is computed automatically for you by the diagram based on the membership of the connected Nodes. For example, if the Link.fromNode is a top-level node but the Link.toNode is a member of a group, the link is a top-level link. If the two connected nodes both belong to the same group, the link is a member of that group. If the two connected nodes belong to different groups, the link belongs to the common container group, if there is any. Note that if a link connects a member of a group with the group itself, the link is a member of that group.
All of the group-member relationships effectively form a tree structure. These properties and methods are useful in navigating these relationships:
- Part.containingGroup
- Part.isTopLevel
- Part.findTopLevelPart
- Part.findSubGraphLevel
- Part.findCommonContainingGroup
- #memberParts
- #findSubGraphParts
- #findExternalLinksConnected
- #findExternalNodesConnected
As the membership of a group changes, you may want to update the appearance of the group. You can set the #memberAdded and #memberRemoved properties to be functions that are called. These functions must not modify any membership relationships -- these function properties just exist to update the appearance of the Group.
You can control whether certain Nodes are added to a Group by CommandHandler.groupSelection or #addMembers or CommandHandler.addTopLevelParts by affecting the result of CommandHandler.isValidMember, which is responsible for deciding whether it is OK to add a Node to a Group or to remove a Node from a Group to be a top-level node. You can override that predicate on CommandHandler, but it is easier to set the #memberValidation or CommandHandler.memberValidation functional property.
For a more general discussion of validation, see Introduction to Validation.
The area occupied by the subgraph is represented in the group's visual tree by a Placeholder. As the group #placeholder grows and shrinks based on the sizes and positions of the member nodes and links, the group will grow and shrink accordingly. The placeholder is always the Part.locationObject, although you may specify any Spot as the Part.locationSpot. A Group need not have a placeholder, but it may have at most one.
A group has its own #layout property that is used to position the member nodes and route the member links.
The Group class also supports the notion of expanding and collapsing the subgraph, causing the member nodes and links to be shown or hidden. Principally this is a matter of setting #isSubGraphExpanded. Changes to this property will result in calls to #collapseSubGraph or #expandSubGraph, as appropriate.
If you want to change the appearance of the group you can do so in a function that you assign to the #subGraphExpandedChanged property. This function must not modify any member relationships or expand or collapse any groups -- the functional property just exists to update the appearance of the Group.
For more discussion and examples, see SubGraphs.
If you want the user to be able to create a Group out of the currently
selected Parts using the CommandHandler.groupSelection command,
you need to first set the CommandHandler.archetypeGroupData property
to a data object with isGroup
set to true.
If you want the user to be able to ungroup a Group,
using the CommandHandler.ungroupSelection command,
you need to set #ungroupable to true.
For more discussion and examples, see Groups, SubGraphs, and Sized Groups.
Only Groups that are in Diagrams can have member Parts or connections via Links. Templates should not be connected with Links, be labels of Links, be members of Groups, have any member Parts, or have any Adornments.
HTMLInfo
HTMLInfo is used to show and hide custom HTML page elements, such as a context menu, tooltip, or text editor made of HTML.More...
Properties that can be set to an HTMLInfo include:
- TextEditingTool.defaultTextEditor
- TextBlock.textEditor
- GraphObject.contextMenu
- Diagram.contextMenu
- GraphObject.toolTip
- Diagram.toolTip
When a context menu is set to an instance of HTMLInfo, ContextMenuTool.showContextMenu and ContextMenuTool.hideContextMenu call #show and #hide respectively. You may define #mainElement instead of #hide in order to automatically use a default hide method.
When a tooltip is set to an instance of HTMLInfo, ToolManager.showToolTip and ToolManager.hideToolTip call #show and #hide respectively.
When a text editor is set to an instance of HTMLInfo, TextEditingTool.doActivate calls #show and TextEditingTool.doDeactivate calls #hide.
For HTMLInfo to work, you must define #show
and either #hide or #mainElement.
Typical usage will also stop the ContextMenuTool once the desired context action occurs,
typically by calling diagram.currentTool.stopTool();
.
Example usage of HTMLInfo can be found in the Custom Context Menu and HTML LightBox Context Menu samples, the Custom TextEditingTool sample, and the Text Editor implementation extension.
Here is the outline for typical usage of HTMLInfo as a context menu:
// Assign an HTMLInfo to the Diagram: myDiagram.contextMenu = $(go.HTMLInfo, { show: showContextMenu, hide: hideContextMenu }); function showContextMenu(obj, diagram, tool) { // Show the context menu HTML element: SomeDOMElement.style.display = "block"; // Also show relevant buttons given the current state // and the GraphObject obj; if null, the context menu is for the whole Diagram } function hideContextMenu() { SomeDOMElement.style.display = "none"; } function buttonClick() { // do some action when a context menu button is clicked // then: myDiagram.currentTool.stopTool(); }
By default, TextEditingTool.defaultTextEditor is an instance of HTMLInfo. You can see its default implementation details here.
InputEvent
An InputEvent represents a mouse or keyboard or touch event.More... The principal properties hold information about a particular input event. These properties include the #documentPoint at which a mouse event occurred in document coordinates, the corresponding point in view/element coordinates, #viewPoint, the #key for keyboard events, and the #modifiers and #button at the time. Additional descriptive properties include #clickCount, #delta, #timestamp, and the source event #event (if available).
Many of its properties are provided for convenient access to the state of the input event, such as asking whether the #control key was held down at the time, or the #targetObject (a GraphObject) that the mouse was over .
When real events fire on the Diagram, InputEvents are created automatically set update the value of Diagram.lastInput. These events set the value of #event with the backing browser-defined Event, which may be a MouseEvent, KeyboardEvent, PointerEvent, TouchEvent, and so on.
InputEvents backed by MouseEvents set both #button, the button that caused the action, and #buttons, the set of buttons currently pressed after the action has happened. By default a user-created InputEvent sets #button and #buttons as if the event was a left-click.
You can create InputEvents and set the value of Diagram.lastInput in order to simulate user actions in tools. This can be useful for testing. See the Robot extension sample for an example of creating InputEvents to simulate user input.
Iterable
This interface is implemented by the List, Set, and Map classes; it provides the #iterator read-only property that returns an Iterator.More...
Typical usage is:
var it = anIterableCollection.iterator; while (it.next()) { var item = it.value; }
Iterator
This interface defines properties and methods for iterating over a collection;
it provides the #next predicate and the #value read-only property.More...
Some Iterators also provide key
property values along with each value
.
Typical usage is:
var it = anIterableCollection.iterator; while (it.next()) { var item = it.value; }
Many iterators will signal an error if #next is called after the underlying collection has been modified.
To avoid confusion when dealing with Iterables, iterators implement the Iterable.iterator property by just returning themselves.
Layer
Layers are how named collections of Parts are drawn in front or behind other collections of Parts in a Diagram.More... Layers can only contain Parts, such as Nodes and Links. They cannot hold GraphObjects directly.
You put a Part into a Layer by assigning Part.layerName with the name of the Layer. You can use data binding to initialize and remember a Part's layer's name. You can change a Part's layer by modifying its Part.layerName, which changes its Part.layer.
Each Diagram starts off with the following list of Layers: "Grid", "Background", "" (the default layer), "Foreground", "Adornment", "Tool". Parts are normally put in the default layer. The "Grid", "Adornment", and "Tool" layers are considered #isTemporary. Changes to objects in temporary layers are not recorded by the UndoManager. Parts in temporary layers are not selected and are not considered to be part of the document. The "Grid" layer is the furthest back; it also contains "temporary" parts that cannot be selected. Furthermore the "Grid" layer has #pickable set to false so that mouse or touch events and calls to the "find..." methods do not even consider any parts in that layer.
Layers have many properties that control what actions users are permitted to perform involving the parts in the layer. These properties are very much like the similarly named properties on Diagram.
Z-ordering
Layers are drawn and presented in order. You can add your own layers by calling Diagram.addLayerBefore or Diagram.addLayerAfter to insert a new layer at a particular place in the Z-order, or to re-order existing layers. Use Diagram.findLayer to get the Layer with a particular name. Parts can be individually z-ordered within a layer by setting Part.zOrder.
LayeredDigraphEdge
This holds LayeredDigraphLayout-specific information about Links.More...
This class inherits from LayoutEdge.
LayeredDigraphLayout
This arranges nodes of directed graphs into layers (rows or columns).More...
There are many samples that use LayeredDigraphLayout.
If you want to experiment interactively with most of the properties, try the Layered Digraph Layout sample.
See samples that make use of LayeredDigraphLayout in the samples index.
The #layerSpacing property controls the distance between layers. The #columnSpacing property controls the breadth of each "column" -- this affects the distance between nodes within a layer, although the exact distance also depends on the breadth of each node. The #layeringOption property determines whether nodes without links coming in or without links going out are lined up at the edge of the graph, or whether they are positioned close to their connected nodes.
By default the layout will route the links in a manner that is consistent with the #direction.
So, for example, if the #direction is 90 degrees (i.e. downward), the links are expected to go from the top towards the bottom.
That means the links should come out from the bottom of the ports and should go into the top of the ports.
Basically the layout will set Link.fromSpot to Spot.Bottom
and Link.toSpot to Spot.Top
.
If you want to the links to use the spots that are given by the ports or by the links themselves, you will need to set
#setsPortSpots to false to prevent this layout from setting the spots on the links.
For example, if each node only has one port that is the whole node, and if you want the links to be spread out along the sides
of the nodes, then you should set #setsPortSpots to false and set the node's GraphObject.fromSpot to
Spot.BottomSide
and GraphObject.toSpot to Spot.TopSide
.
This layout handles links that form cycles better than TreeLayout does.
The normal routing behavior for "backwards" links is to route them "around" the source node and "around" the destination node,
so that all links come in one side and go out the other side.
However if you want "backwards" links to go more directly between nodes, set #setsPortSpots to false and
the node's GraphObject.fromSpot and GraphObject.toSpot both to Spot.TopBottomSides
.
(Of course if the #direction is zero or 180, you'll want to use Spot.LeftRightSides
.
If the diagram is structured in a tree-like fashion, it may be better to use TreeLayout, which has more options specific to trees. TreeLayout is much faster than LayeredDigraphLayout, and can handle a limited number of links that would prevent the graph structure from being a true tree (i.e. some nodes having multiple parents).
This layout makes use of a LayoutNetwork of LayeredDigraphVertexes and LayeredDigraphEdges that normally correspond to the Nodes and Links of the Diagram.
The layout algorithm consists of four-major steps: Cycle Removal, Layer Assignment, Crossing Reduction, and Straightening and Packing. The layout cannot guarantee that it provides optimal positioning of nodes or routing of links.
LayeredDigraphVertex
This holds LayeredDigraphLayout-specific information about Nodes.More...
This class inherits from LayoutVertex.
Layout
This is the base class for all of the predefined diagram layout implementations.More... They only arrange Parts (primarily Nodes and Links) in a Diagram, not to GraphObjects in Panels (i.e. panel layout).
The layout classes include TreeLayout, ForceDirectedLayout, LayeredDigraphLayout, CircularLayout, and GridLayout. This base class is not abstract -- in fact an instance of this base class is the default value for Diagram.layout and for Group.layout.
An instance of a Layout class will be the value of Diagram.layout. That layout positions the graph of top-level nodes and links. Nodes and links that belong to a Group are laid out by that group's Group.layout. The Diagram will automatically perform all nested group layouts before laying out the whole diagram.
If you have position information for all of the nodes when you load a model, you will typically have data bound the Part.location to some property on your node data. In order to avoid an initial layout causing those saved node positions to be discarded, you can either not set the Diagram.layout to a predefined layout or you can set #isInitial to false.
Because performing layouts can be expensive in space and time, automatic layouts are performed only on "invalid" layouts, and only well after a layout has been invalidated. This state is held by the #isValidLayout property. Many standard operations, such as adding or removing nodes or links, will cause the layout that is responsible for positioning those nodes or routing those links to be invalidated. Such invalidation is performed by calling #invalidateLayout, which not only clears the #isValidLayout state but also requests that the diagram do an automatic layout soon. You can avoid such invalidations by setting #isOngoing to false.
Layouts will ignore parts that have Part.isLayoutPositioned set to false or parts that are not GraphObject.visible. Layouts will also ignore parts that are in layers that are Layer.isTemporary.
Various operations on Parts will cause the responsible Layout to be invalidated. This includes adding or removing parts, changing their visibility, and changing their size. You can disable such automatic layout invalidations by setting Part.layoutConditions to the combination of Part flags named "Layout..." that you want.
But operations on parts are not the only way in which layouts become invalidated.
Setting most properties on the layouts, thereby changing their behavior, will invalidate that layout.
Replacing the Diagram.layout or Group.layout will automatically invalidate the new layout.
If #isViewportSized is true, when a diagram's Diagram.viewportBounds changes size,
the Diagram.layout is invalidated.
(This is normally only true for GridLayouts when its GridLayout.wrappingWidth is NaN
.
Most layouts do not care about the size of the viewport.)
You can also explicitly call Diagram.layoutDiagram, which can invalidate all layouts and then perform them all. But we recommend that you avoid doing so, to allow the normal updating process perform layouts as needed.
If an automatic layout is the first time that a layout has been performed for the model, the diagram first raises the DiagramEvent named "InitialLayoutCompleted". Whenever a Diagram finishes an automatic layout, it raises the DiagramEvent named "LayoutCompleted".
It is also possible to call #doLayout explicitly, but this is uncommon and only used with instances of Layout that are not the Diagram.layout or Group.layout. It should only be needed when you want to layout a collection of nodes and links that is not the normal graph of top-level parts of a Diagram or a subgraph of a Group.
More complicated layouts make use of a separate LayoutNetwork, consisting of LayoutVertexes and LayoutEdges, that normally holds a graph that is isomorphic to the graph consisting of Nodes and Links in the Diagram or Group. The implementation of #doLayout will call #makeNetwork and remember the result as the #network. #makeNetwork will call #createNetwork and initialize it by adding new instances of LayoutVertexes and LayoutEdges corresponding to the given collection of Nodes and Links.
When #doLayout is finished with its work it will call #updateParts, which will call #commitLayout to set new node locations and route links. It then normally discards the #network.
The LayoutVertex and LayoutEdge instances allow the layout to work with more information about each Node and Link without actually modifying those Nodes and Links until #commitLayout is called to actually set the Node locations and route the Links. The use of a LayoutNetwork also allows the Layout to work with a graph that is not isomorphic to the given collection of Nodes and Links. This is useful when needing to use dummy vertexes and/or edges to achieve certain layout behaviors, or when one wants to ignore certain vertexes or edges, without actually modifying or adding or removing the diagram's nodes or links.
An instance of this base class provides a rudimentary default layout that will position all of the parts that have no position (i.e. the Part.location is (NaN,NaN). Parts that already have a position are ignored. This primitive layout class does not make use of a LayoutNetwork because it ignores all links.
To implement your own custom layouts, you can inherit from either this class or from one of the other predefined layout classes. If you inherit from this base class, you will want to override the #doLayout method. You can call the Part.move method to re-position a part, including whole groups. Please read the Introduction page on Extensions for how to override methods and how to call a base method.
LayoutEdge
An edge represents a Link in a LayoutNetwork, along with its #fromVertex and #toVertex.More... The #link property may be null for edges that represent "dummy" links, when the layout wants to work with a network that is not isomorphic with the collection of Nodes and Links being laid out.
It holds layout-specific data for the link. For each kind of layout that uses a LayoutNetwork there is a subclass of LayoutVertex and a subclass of LayoutEdge:
- CircularLayout: CircularVertex and CircularEdge
- ForceDirectedLayout: ForceDirectedVertex and ForceDirectedEdge
- LayeredDigraphLayout: LayeredDigraphVertex and LayeredDigraphEdge
- TreeLayout: TreeVertex and TreeEdge
Modifying a LayoutNetwork or a LayoutVertex or a LayoutEdge does not invalidate the Layout or raise any changed events.
LayoutNetwork
This provides an abstract view of a diagram as a network (graph) of vertexes and directed edges.More... The network contains vertexes and edges corresponding to Nodes and Links.
This class provides a framework for manipulating the state of nodes and links without modifying the structure of the diagram. Having a separate representation also permits adding or removing vertexes or edges from the network so that the graph that is laid out is not isomorphic to the diagram's graph.
For each kind of layout that uses a LayoutNetwork there is a subclass of LayoutVertex and a subclass of LayoutEdge:
- CircularLayout: CircularVertex and CircularEdge
- ForceDirectedLayout: ForceDirectedVertex and ForceDirectedEdge
- LayeredDigraphLayout: LayeredDigraphVertex and LayeredDigraphEdge
- TreeLayout: TreeVertex and TreeEdge
Modifying a LayoutNetwork or a LayoutVertex or a LayoutEdge does not invalidate the Layout or raise any changed events.
LayoutVertex
A vertex represents a Node in a LayoutNetwork, along with its #bounds and #focus and collections of LayoutEdges that come into and go out of the vertex.More... The #node property may be null for vertexes that represent "dummy" nodes, when the layout wants to work with a network that is not isomorphic with the collection of Nodes and Links being laid out.
This holds layout-specific data for the node. For each kind of layout that uses a LayoutNetwork there is a subclass of LayoutVertex and a subclass of LayoutEdge:
- CircularLayout: CircularVertex and CircularEdge
- ForceDirectedLayout: ForceDirectedVertex and ForceDirectedEdge
- LayeredDigraphLayout: LayeredDigraphVertex and LayeredDigraphEdge
- TreeLayout: TreeVertex and TreeEdge
Modifying a LayoutNetwork or a LayoutVertex or a LayoutEdge does not invalidate the Layout or raise any changed events.
Link
A Link is a Part that connects Nodes.More...
The link relationship is directional, going from Link.fromNode to Link.toNode.
A link can connect to a specific port element in a node, as named by the Link.fromPortId
and Link.toPortId properties.
For more discussion, see Introduction to Links.
To add a Link to a Diagram when using a GraphLinksModel you should do something like:
myDiagram.startTransaction("make new link"); myDiagram.model.addLinkData({ from: "Alpha", to: "Beta" }); myDiagram.commitTransaction("make new link");
where you would substitute the keys of the actual nodes that you want to connect with a link. This will cause a Link to be created (copying the template found in Diagram.linkTemplateMap), added to the Diagram in some Layer (based on Part.layerName), and bound to the link data (resulting in Panel.data referring to that link data object). Note that link data objects, unlike Node data, do not have their own unique keys or identifiers, because other Parts do not refer to them.
If you are using a TreeModel, there are no link data objects, so you just need to call TreeModel.setParentKeyForNodeData to specify the "parent" node's key for a "child" node data.
To find a Link given a link data object in the GraphLinksModel, call Diagram.findLinkForData. When using a TreeModel, call either Diagram.findNodeForData or Diagram.findNodeForKey to get a Node, and then call Node.findTreeParentLink to get the Link, if any exists.
To find a link that connects two nodes, call Node.findLinksTo or Node.findLinksBetween. With the former method, the direction matters; with the latter method it returns links in either direction.
A link's position and size are determined by the two nodes that it connects. Normally there should be a Shape as the main element in this Link. This shape is what users will see as the "line" or "wire" -- you can set its Shape.stroke and other "stroke..." properties to control its appearance.
The link will compute a route (a sequence of points) going from the #fromNode's port element to the #toNode's port element. That route is used to generate the path of the main shape. Properties that affect the nature of the route and the geometry of the path include:
- #curve
- #curviness
- #corner
- #routing
- #smoothness
- #adjusting
For more discussion and examples, see Links.
There are additional properties that affect how the end of the link connects to a port element of a node. There are duplicate properties, ones for the "to" end and ones for the "from" end:
- #fromSpot, #toSpot
- #fromEndSegmentLength, #toEndSegmentLength
- #fromShortLength, #toShortLength
These properties normally have "default" values, causing the link's routing and path-geometry generating to get the corresponding values from the connected port element. This scheme permits an individual link to have its own specific connection to a port, taking precedence over how the port normally expects links to connect to it. For example, several of the Layout classes sets these properties on each Link as part of their route computation for links.
For more discussion and examples, see Link Points.
Elements other than the main Shape in the Link may act as decorations on the link, including arrowheads and labels. You can control where they are located along the link route and how they are oriented. Because these decorations may be any GraphObject, they are all properties of that class. The properties include:
- GraphObject.segmentIndex
- GraphObject.segmentFraction
- GraphObject.segmentOffset
- GraphObject.segmentOrientation
For more discussion and examples, see Link Labels.
GoJS makes it easy to add arrowheads to your link template. Just add a Shape with the appearance properties that you want, and also set the Shape.toArrow or Shape.fromArrow property to the name of the kind of arrowhead that you want. Doing so automatically sets the "segment..." properties that are appropriate for the chosen arrowhead.
More than one shape may automatically get the route geometry. This is useful when you want to have multiple link shapes with different thicknesses to create a gradient effect across the path of the link or to produce parallel lines along the path. Just set GraphObject.isPanelMain to true on each such Shape.
If you want the user to be able to reconnect a link, using the RelinkingTool, you need to set one or both of #relinkableFrom and #relinkableTo to true. The RelinkingTool shows a RelinkingTool.fromHandleArchetype and/or a RelinkingTool.toHandleArchetype when the link is selected. Such a relink handle can be dragged by the user to start a relinking operation.
If you want the user to be able to change the path of the link, using the LinkReshapingTool, set Part.reshapable to true. The LinkReshapingTool shows reshape handles that the user can drag to shift the position of a point in the link's route. The LinkReshapingTool.handleArchetype is copied for each reshape handle.
Often if a Link is reshapable, you will want to save the route in the model so that it can be restored
upon load. To save the route automatically, add a TwoWay Binding on the #points property:
new go.Binding("points").makeTwoWay()
. Model.toJson will automatically
convert the List of Points into an Array of numbers in the JSON representation,
if the property is named "points".
If a Link is Part.reshapable, it is also possible to allow the user to add and remove segments from the link's route by setting #resegmentable to true. This causes the LinkReshapingTool to add resegmenting handles at the midpoints of each segment. The LinkReshapingTool.midHandleArchetype is copied for each resegment handle. When the user drags such a resegmenting handle, a new segment is inserted into the route. Also, when the user drags a reshape handle such that two adjacent segments end up in a straight line, a segment is removed from the route.
For more discussion and examples, see Links, Link Labels, and Link Points.
To control what links a user may draw or reconnect, please read about Validation.
To customize linking and relinking behavior, please read Introduction to the Linking Tools and Introduction to the RelinkingTool. For customizing the reshaping of Links, see Introduction to the LinkReshapingTool.
Only Links that are in Diagrams can have connections with Nodes. Templates should not be connected with Nodes, be members of Groups, or have any Adornments.
LinkingBaseTool
This abstract class is the base class for the LinkingTool and RelinkingTool classes.More...
This class includes properties for defining and accessing any temporary nodes and temporary link that are used during any linking operation, as well as access to the existing diagram's nodes and link (if any) that are involved with the linking operation.
For a general discussion of link routing, see: Introduction to Links, Introduction to Link Labels, and Introduction to Link Connection Points. For customizing the linking tools, see Introduction to the Linking Tools. For customizing the reshaping of Links, see Introduction to the LinkReshapingTool. For a general discussion of validation, see Introduction to Validation.
LinkingTool
The LinkingTool lets a user draw a new Link between two ports, using a mouse-drag operation.More...
By default an instance of this tool is installed as a mouse-move tool in the Diagram.toolManager as the ToolManager.linkingTool. However this tool may be used modally, as described below.
#canStart calls #findLinkablePort to find a valid "port" element from which (or to which) the user may interactively draw a new link. #doActivate sets up a temporary link and two temporary nodes, one at the start port and one following the mouse.
For a general discussion of validation, see Introduction to Validation.
This tool does not utilize any Adornments or tool handles.
This tool conducts a transaction while the tool is active. A successful linking will result in a "LinkDrawn" DiagramEvent and a "Linking" transaction.
If you want to programmatically start a new user mouse-gesture to draw a new link from a given GraphObject that may be a "port" or may be within the visual tree of a "port", set the #startObject property to let #findLinkablePort find the real "port" element. Then start and activate this tool:
var tool = myDiagram.toolManager.linkingTool; tool.startObject = ...; myDiagram.currentTool = tool; tool.doActivate();
LinkReshapingTool
The LinkReshapingTool is used to interactively change the route of a Link by setting its Link.points list.More... You may want to save the route to the model by using a TwoWay Binding on the "points" property of the Link.
This tool makes use of an Adornment, shown when the adorned Link is selected, that includes some number of reshape handles. This tool conducts a transaction while the tool is active. A successful reshaping will result in a "LinkReshaped" DiagramEvent and a "LinkReshaping" transaction.
For a general discussion of link routing, see: Introduction to Links, Introduction to Link Labels, and Introduction to Link Connection Points. For customizing the linking tools, see Introduction to the Linking Tools. For a general discussion of validation, see Introduction to Validation.
List
An ordered iterable collection.More... It optionally enforces the type of elements that may be added to the List.
An example usage:
var list = new go.List(go.Point); // make a list of Points list.add(new go.Point(0, 0)); list.add(new go.Point(20, 10)); list.add(new go.Point(10, 20)); // now list.length === 3 // and list.elt(1) instanceof go.Point
You can iterate over the items in a List:
var it = aList.iterator; while (it.next()) { window.console.log("#" + it.key + " is " + it.value); }Or:
aList.each(function(val) { window.console.log(val); });The key will range from zero to #count-1.
For convenience this GoJS List class has synonyms for the following methods and property:
- get(idx): #elt
- set(idx,val): #setElt
- has(val): #contains
- delete(val): #remove
- clear(): clear
- size: #count
Map
An unordered iterable collection of key/value pairs that cannot contain two instances of the same key.More... It optionally enforces the type of the key and the type of the associated value.
To create a Map:
var map = new go.Map("string", "number"); map.add("one", 1); map.add("two", 2); map.add("three", 3); // now map.count === 3 // and map.getValue("two") === 2 // and map.contains("zero") === false
You can iterate over the key/value pairs in a Map:
var it = aMap.iterator; while (it.next()) { window.console.log(it.key + ": " + it.value); }Or:
aMap.each(function(kvp) { window.console.log(kvp.key + ": " + kvp.value); });But note that there is no guaranteed ordering amongst the key/value pairs.
Call #toKeySet to get a read-only Set that holds all of the keys of a Map. Iterating over that Set will produce values that are the keys in the Map.
Although not precisely implementing the features and semantics of the EcmaScript 6 Map class, this GoJS Map class has synonyms for the following methods and property:
- get(key): #getValue
- set(key,val): #add
- has(key): #contains
- delete(key): #remove
- clear(): clear
- size: #count
Margin
A Margin represents a band of space outside or inside a rectangular area, with possibly different values on each of the four sides.More...
Example uses include GraphObject.margin, Panel.padding, and Diagram.padding.
Use the static functions Margin.parse and Margin.stringify to convert to and from a standard string representation that is independent of the current locale.
When an instance of this class is the value of a property of a GraphObject class or Diagram or CommandHandler or a Tool class, you should treat the object as if it were frozen or read-only -- you cannot modify its properties. This allows the property to return a value without allocating a new instance. If you need to do your own calculations with the value, call #copy to make a new instance with the same values that you can modify.
Many methods modify the object's properties and then return a reference to "this" object. The only instance method to allocate a new object is the #copy method. The static Margin.parse method also allocates a new object.
The "Debug" implementation of this class is significantly slower than the "Release" implementation, mostly due to additional error checking.
You cannot inherit from this class.
Model
Models hold the essential data of a diagram, describing the basic entities and their properties and relationships without specifying the appearance and behavior of the Nodes and Links and Groups that represent them visually.More... Models tend to hold only relatively simple data, making them easy to persist by serialization as JSON or XML formatted text.
Models hold simple data objects, not Parts such as Nodes or Links. Node data is normally represented in a Diagram by instances of Node, but they could be represented by simple Parts or by Groups. A Diagram constructs Parts for its Diagram.model's data by copying templates. Templates are Panels of GraphObjects that get some property values from the model data, accessible via the Panel.data property, using data Binding. See Using Models and Data Binding for an introduction.
This Model class only supports holding an array of node data and interpreting properties on that data to be able to refer to them using unique key values. To support simple tree-structured graphs, use a TreeModel, which inherits from this class. To support links and grouping, use a GraphLinksModel.
Each node data object is assumed to have a unique key value. The #nodeKeyProperty property names the property on the node data whose value is the unique key for that node data object. The default value for this property is "key". You should not have a TwoWay data binding on the node key property, because that might cause the property value to be set to a duplicate key value.
The key values must be either strings or numbers or undefined. If the key is undefined, or if there are duplicate key values, the model will automatically try to assign a new unique key value. Caution: if your keys are numbers, do not try to use string representations of those numbers as keys. Conversely, if your keys are strings that happen to have number syntax, do not try to use those number values. Sometimes JavaScript will automatically convert from string to number or vice-versa, but sometimes it won't.
For example, one can define a graph consisting of just two nodes:
model.nodeDataArray = [ { key: "Alpha" }, { key: "Beta" } ];
This model cannot detect the modification of the #nodeDataArray array or the modification of any node data object. If you want to add or remove node data from the #nodeDataArray, call the #addNodeData or #removeNodeData methods.
If you want to modify a node data object, it depends on whether the property you want to change is a structural property that the model needs to know about, or whether it is a property that is only used for data binding or other application-specific purposes.
For the former case, call the appropriate method, such as #setKeyForNodeData, #setCategoryForNodeData, GraphLinksModel.setToKeyForLinkData, or GraphLinksModel.setGroupKeyForNodeData. These methods have names that start with "set", "add", "insert", or "remove".
For the latter case, when setting an application-specific property, typically for data binding, and to support undo/redo, call #setDataProperty.
The #copyNodeData method can be called to make a shallow copy of a node data object. However, if some of those property values are Arrays that want not to be shared but to be copied, you can set #copiesArrays to true. This is typically very useful when dealing with data bound item arrays. Furthermore if the items in those copied Arrays are in fact Objects that need to be copied, you can also set #copiesArrayObjects to true, causing a copied Array to refer to newly shallow-copied objects of the original array.
Each model raises ChangedEvents that you can follow by registering a listener via #addChangedListener. Read more at the Introduction page: Changed Events.
Each model comes with its own UndoManager that is initially not enabled. You will need to set UndoManager.isEnabled to true in order for the UndoManager to record model changes and for your users to perform undo and redo.
You can temporarily turn off the recording of changes by setting #skipsUndoManager to true. A number of places within the system do that routinely in order to avoid recording temporary changes, so be sure to remember the original value beforehand and restore it afterwards.
One normally saves a diagram by just saving its model. If you can use JSON-formatted text, this is easy to do -- just call #toJson to get the string representation of the model, and save that string. Load the diagram by replacing the Diagram.model with one created by calling the static function Model.fromJson:
myDiagram.model = go.Model.fromJson(loadedString);Note that JSON and other textual data formats cannot faithfully store all JavaScript functions. #toJson and Model.fromJson do not try to save and load functional property values. You should arrange that all such functions, including event handlers, are established by your app. #toJson and Model.fromJson also cannot handle circular references; any sharing of references will be lost too. They also skip properties that are not enumerable, those whose names start with an underscore, and those whose values are undefined.
Note that models also do not store the templates used by diagrams, nor any transient or temporary parts such as Adornments, nor any tools, nor any UndoManager state, nor any event listeners. These objects and all other properties of diagrams must be established by your app.
You can add any number of properties to the #modelData object, which is serialized and deserialized into JSON just like any other model data for nodes or links. However #modelData is associated with the model as a whole and does not depend on the existence of any node data or link data.
It is also easy to save the changes that were recorded in the most recent transaction. Call #toIncrementalJson to generate a JSON-format string that holds the current state of modified data plus the keys of inserted or removed data. That method requires as an argument a ChangedEvent that represents a transaction that completed or an undo or a redo that just finished.
It is also possible to use such "incremental" JSON to modify an existing model. Call #applyIncrementalJson, giving it a string generated by #toIncrementalJson, to modify this model by making all of the changes recorded in the JSON text. Note how this method is a regular instance method, whereas Model.fromJson is a static function.
Node
A Node is a Part that may connect to other nodes with Links, or that may be a member of a Group.More...
Group inherits from Node, enabling nodes to logically contain other nodes and links.
For a more general discussion of how to define nodes, see Introduction to Nodes.
Although you can create a Node and Diagram.add it to a Diagram, this does not update the Model. It is more common to create a node by adding a node data object to the model by calling Model.addNodeData. For example:
myDiagram.startTransaction("make new node"); myDiagram.model.addNodeData({ key: "Omega" }); myDiagram.commitTransaction("make new node");
This will cause a Node or simple Part to be created (copying the template found in Diagram.nodeTemplateMap), added to the Diagram in some Layer (based on Part.layerName), and bound to the node data (resulting in Panel.data referring to that node data object). If you do not keep a reference to that JavaScript object, as the above code does not, you can retrieve it later by calling Model.findNodeDataForKey.
It is very common to initialize a Diagram by setting Model.nodeDataArray to a JavaScript Array of JavaScript objects holding the properties that you need in your model. Nearly all of the samples do this kind of initialization.
You can delete a Node by either calling Diagram.remove or by calling Model.removeNodeData. The latter obviously will modify the Model; the former does so if the Node was created from model data. Commands such as CommandHandler.deleteSelection call these methods within a transaction.
You can find all of the Links that are connected with a Node by calling #findLinksConnected. Because links normally have a direction, you can find all of the links that have their Link.toNode be a given Node by calling #findLinksInto. Similarly, you can call #findLinksOutOf to find all of the links coming out from a node; such links have their Link.fromNode be that node. For tree-structured graphs, use #findTreeChildrenLinks or #findTreeParentLink.
If you are not so interested in the links but are interested in the nodes at the other end of the links connecting with a node, there are other methods that you can call. #findNodesConnected returns all of the nodes that are at the other end of the links that connect with a given node. #findNodesInto and #findNodesOutOf return the subsets of those nodes considering only those links that go into or come out of the given node. For tree-structured graphs, use #findTreeChildrenNodes or #findTreeParentNode.
For example, to operate on the data of all of the destination nodes:
var it = somenode.findNodesOutOf(); while (it.next()) { var child = it.value; if (child.data.text.indexOf("special") >= 0) { ... } }
You can link two nodes by creating a new Link, setting its Link.toNode and Link.fromNode (in either order), and Diagram.adding it to the diagram. But it is more common to add a link data object to the Diagram.model by calling GraphLinksModel.addLinkData. Just creating and adding a Link will not update the model.
Thus to add a link when using a GraphLinksModel you should do something like:
myDiagram.startTransaction("make new link"); myDiagram.model.addLinkData({ from: "Alpha", to: "Beta" }); myDiagram.commitTransaction("make new link");
Where you would substitute the keys of the actual nodes that you want to connect with a link. If you are using a TreeModel, there are no link data objects, so you just need to call TreeModel.setParentKeyForNodeData to specify the "parent" node's key for a "child" node data.
To find a Link given a link data object in the GraphLinksModel, call Diagram.findLinkForData. When using a TreeModel, call either Diagram.findNodeForData or Diagram.findNodeForKey to get a Node, and then call #findTreeParentLink to get the Link, if any exists.
To find a link that connects two nodes, call #findLinksTo or #findLinksBetween. With the former method, the direction matters; with the latter method it returns links in either direction.
As links connect with a node or are disconnected, you may want to update the appearance of the node. You can set the #linkConnected and #linkDisconnected properties to be functions that are called. These functions must not modify any link relationships -- the properties just exist to update the appearance of the node. A typical usage would be to change the color or figure of a shape.
You can control whether the user may draw a new link or reconnect a link between a pair of Nodes by affecting the result of LinkingBaseTool.isValidLink. You can override that predicate on LinkingTool and RelinkingTool, but it is easier to set the #linkValidation or LinkingBaseTool.linkValidation functional property.
For a more general discussion of validation, see Introduction to Validation.
Nodes also support the ability to provide logical and physical distinctions in the connection points that links use at a node. These connection objects are called "ports". By default the port object will be the whole Node. However, you can set the GraphObject.portId property on any GraphObject in the visual tree of a node to cause that element to be treated as a "port". The "port id" is just a string that ought to be unique amongst all of the port elements in the node.
In the case of a node only having a single port, you should set the GraphObject.portId as an empty string. When there is no such element declared as the default port, it uses the whole node. You can use the #port property to get the only port element.
When a node should have multiple ports, i.e. multiple GraphObjects acting as separate connection points for links, you should set each port's GraphObject.portId to a string value that is unique for the node. When there may be multiple ports on a node, you can get a collection of elements representing ports by using the #ports property. Use the #findPort method to find a particular port element by name.
Note: the only kind of model that can save port information, i.e. portIds that are not an empty string, for links is a GraphLinksModel whose GraphLinksModel.linkFromPortIdProperty and GraphLinksModel.linkToPortIdProperty have been set to name properties on the link data objects.
For a more general discussion of ports, see Introduction to Ports.
All of the "findLinks..." and "findNodes..." methods mentioned above take an optional port id argument. When no argument is passed, these methods consider all links connecting with the node. When a port id argument is provided, these methods only consider links that connect with that port in the given node. Thus when navigating through the diagram, you can easily look at all of the nodes that links coming out of a given node go to. Or you can just look at those nodes at the ends of links coming out of a particular port.
You can also control the default connecting behavior of Links at each port. Because a port can be any GraphObject, they are all properties on GraphObject. The properties are duplicated so that you can guide the "from" ends of links differently from the "to" ends of links. The properties include:
- GraphObject.fromSpot, GraphObject.toSpot
- GraphObject.fromEndSegmentLength, GraphObject.toEndSegmentLength
- GraphObject.fromShortLength, GraphObject.toShortLength
- GraphObject.fromLinkable, GraphObject.toLinkable
- GraphObject.fromLinkableDuplicates, GraphObject.toLinkableDuplicates
- GraphObject.fromLinkableSelfNode, GraphObject.toLinkableSelfNode
- GraphObject.fromMaxLinks, GraphObject.toMaxLinks
The "...Spot" and "...Length" and "...Direction" properties control the position and routing of links at a port. The "...Linkable..." and "...MaxLinks" properties control whether or not users can draw a new link or reconnect an existing link from or to a port. (The "...Spot" and "...Length" and "...Direction" properties also exist on Link, to override for a particular link the default values that come from a port element.)
For a more general discussion of link points, see Introduction to Link Connection Points.
When the graph is tree-structured, you can use several functions for traversing the tree:
- #findTreeParentNode
- #findTreeChildrenNodes
- #findTreeParentLink
- #findTreeChildrenLinks
- #findTreeRoot
- #findTreeParentChain
- #findTreeParts
- #findCommonTreeParent
- #isInTreeOf
- #findTreeLevel
Determining whether a tree grows from the root via links that go out to the children or vice-versa is controlled for the whole diagram by the Diagram.isTreePathToChildren property. However an individual link will be ignored by the above functions if Link.isTreeLink is false.
The Node class also supports the notion of expanding and collapsing a subtree of nodes and links, causing those nodes and links to be shown or hidden. Principally this is a matter of setting Node.isTreeExpanded. Of course if the diagram's graph is not tree-structured, these concepts and properties might not apply.
If you want to change the appearance of the node you can do so in a function that you assign to the #treeExpandedChanged property. This function must not modify any link relationships or expand or collapse any subtrees -- the property just exists to update the appearance of the node.
There is an option for link routing to try to avoid crossing over nodes: Link.routing = Link.AvoidsNodes. You can control whether such links should avoid or ignore a node by setting #avoidable. Set #avoidableMargin to control the area beyond the GraphObject.actualBounds where AvoidsNodes links should not go.
For more discussion and examples, see Nodes, Ports, and Link Points.
For more about trees, see Trees, and SubTrees.
To customize user-resizing behavior, please read Introduction to the ResizingTool. To customize user-rotating behavior, please read Introduction to the RotatingTool.
Only Nodes that are in Diagrams can have connections via Links. Templates should not be connected with Links, be labels of Links, be members of Groups, or have any Adornments.
Overview
An Overview is a Diagram that displays all of a different diagram,
with a rectangular box showing the viewport displayed by that other diagram.More...
For more discussion, see Introduction to Overviews.
See samples that make use of Overviews in the samples index.
All you need to do is set Overview.observed. For example:
var myDiagram = new go.Diagram("myDiagramDIV"); . . . other initialization . . . // create and initialize the Overview: new go.Overview("myOverviewDIV").observed = myDiagram;
The Overview draws what the observed Diagram is displaying, so setting or modifying any diagram templates or template Maps has no effect. At the current time methods such as Diagram.makeImage, Diagram.makeImageData and Diagram.makeSvg do not work on Overviews.
Palette
Palette extends the Diagram class to allow objects to be dragged and placed onto other Diagrams.More...
Its Diagram.layout is a GridLayout.
The Palette is Diagram.isReadOnly but to support drag-and-drop its Diagram.allowDragOut is true.
For more discussion, see Introduction to Palettes.
See samples that make use of Palettes in the samples index.
You can control the order of Parts in the palette in several manners:
- If you want to keep the order given by
myPalette.model.nodeDataArray
, just setmyPalette.layout.sorting = go.GridLayout.Forward
(GridLayout.sorting). - If you want to sort based on some text string associated with each Part, just bind Part.text.
- If you want to sort more generally, set
myPalette.layout.comparer
(GridLayout.comparer).
Panel
A Panel is a GraphObject that holds other GraphObjects as its elements.More... A Panel is responsible for sizing and positioning its elements. The elements of a panel are drawn in the order in which they appear in the #elements collection.
The Part class inherits from Panel; Part in turn is the base class of Node and Link.
Every Panel has a #type and establishes its own coordinate system. The type of a Panel determines how it will size and arrange its elements:
- Panel.Position is used to arrange elements based on their absolute positions within the Panel's local coordinate system.
- Panel.Vertical and Panel.Horizontal are used to create linear "stacks" of elements.
- Panel.Auto is used to size the main element to fit around other elements in the Panel.
- Panel.Spot is used to arrange elements based on the Spot properties GraphObject.alignment and GraphObject.alignmentFocus, relative to a main element of the panel. Spot panels can align relative to other elements by using Panel.alignmentFocusName.
- Panel.Table is used to arrange elements into rows and columns, typically employing the different elements' GraphObject.row, GraphObject.rowSpan, GraphObject.column, and GraphObject.columnSpan properties. This Panel type also makes use of RowColumnDefinition.
- Panel.TableRow and Panel.TableColumn can only be used immediately within a Panel.Table Panel to organize a collection of elements as a row or as a column in a table.
- Panel.Viewbox is used to automatically resize a single element to fit inside the panel's available area.
- Panel.Grid is not used to house typical elements, but is used only to draw regular patterns of lines. The elements must be Shapes used to describe the repeating lines.
- Panel.Link is only used by Link parts and Link Adornments.
- Panel.Graduated is used to draw regular tick marks and text along the main Shape element.
// Either: $(go.Panel, go.Panel.Horizontal, ... // Or: $(go.Panel, "Horizontal", ... // Full example: $(go.Panel, "Horizontal", { width: 60, height: 60 }, // panel properties // elements in the panel: $(go.Shape, "Rectangle", { stroke: "lime" }), $(go.TextBlock, "Some Text") ) // end of panel definition
For an overview of most Panel types, please read the Introduction page on Panels.
Panel.Vertical and Panel.Horizontal panels are frequently used to position two or more GraphObjects vertically above each other or horizontally next to each other. Use the GraphObject.alignment or GraphObject.stretch properties on the individual elements to control their position and size. Set #isOpposite to true if you want the elements arranged from right-to-left in Horizontal Panels or from bottom-to-top in Vertical Panels.
Panel.Spot and Panel.Auto panels have a "main" element, signified by the Panel's first element with GraphObject.isPanelMain set to true. If there is no such element, it uses the first element as the "main" one. Use the GraphObject.alignment property to position elements with respect to the main element. Use the GraphObject.alignmentFocus property to further specify the position within Spot Panels. "Spot" and "Auto" Panels should have two or more elements in them.
In Panel.Table panels you will want to set the GraphObject.row and GraphObject.column properties on each element. The GraphObject.alignment and GraphObject.stretch properties are also useful when an element's table cell is larger than that element.
Please read the Introduction page on Table Panels for more examples and explanation.
Panel.TableRow and Panel.TableColumn panels can only be used as elements within a Panel.Table Panel. They are typically only used in item templates, e.g. for automatically creating rows in a Table Panel based on model data provided in an #itemArray. You will still need to specify properties on the individual elements within a TableRow or TableColumn as if they were immediate elements of the containing Table panel.
For an example that uses TableRow Panels, see Records sample.
Panel.Grid panels are often used for the Diagram's Diagram.grid.
$(go.Diagram, "myDiagramDiv", { . . . grid: $(go.Panel, "Grid", { gridCellSize: new go.Size(40, 40) }, $(go.Shape, "LineH", { stroke: "lightgray" }), $(go.Shape, "LineV", { stroke: "lightgray" }) ), . . . });Or to get a green bar effect:
$(go.Diagram, "myDiagramDiv", { . . . grid: $(go.Panel, "Grid", { gridCellSize: new go.Size(100, 100) }, $(go.Shape, "BarH", { fill: "lightgreen", height: 50 }) ), . . . });But Grid Panels can also be stand alone objects:
$(go.Node, go.Panel.Grid, { gridCellSize: new go.Size(6, 6), width: 60, height: 60 }, $(go.Shape, "LineH", { stroke: "gray" }), $(go.Shape, "LineV", { stroke: "gray" }))
A Grid Panel's elements do not participate in object picking.
Please read the Introduction page on Grid Patterns for more examples and explanation.
Panel.Graduated panels, like Spot and Auto Panels have a "main" element. The other elements within a Graduated Panel are used to define ticks and labels to draw along the main shape's path.
$(go.Part, "Graduated", $(go.Shape, { geometryString: "M0 0 H400" }), $(go.Shape, { geometryString: "M0 0 V10" }), // offset to display below ticks $(go.TextBlock, { segmentOffset: new go.Point(0, 12) }) );
Only the main shape of a Graduated Panel participates in object picking, but a background
can be set if the entire panel needs to be pickable.
You cannot set or bind the Panel.itemArray of a Graduated Panel.
Events on the tick Shapes and TextBlock labels of a Graduated Panel will be ignored.
Graduated Panel TextBlock labels cannot be edited.
Rotating the main shape will not rotate the ticks, just as rotating a Spot Panel's main element won't rotate its children. Rotation should generally be done at the Panel level. Another similarity to Spot Panels is that resizing of a Graduated Panel should generally be done on the main shape.
Please read the Introduction page on Graduated Panels for more examples and explanation.
Changing and accessing elements of a Panel
You can change the collection of #elements by calling #add, #insertAt, #remove, or #removeAt. You can get direct access to a particular element by calling #elt.
You can search the visual tree of a Panel for GraphObjects that given a GraphObject.name using #findObject.
Panel Size and Appearance
Panels typically compute their own size based on their elements and Panel #type, but can also be sized by setting GraphObject.desiredSize, GraphObject.minSize, and GraphObject.maxSize. Setting an explicit size on a Panel may cause nested elements of that panel to size themselves differently, especially in the cases of nested elements having a GraphObject.stretch value or TextBlock's having word wrap.
Panels have no visual components of their own unless a GraphObject.background or GraphObject.areaBackground is specified or separators are specified either as defaults for the whole Table Panel or on individual RowColumnDefinitions.
In addition to the GraphObject properties on elements that are only used by certain types of panels, several Panel properties only apply to specific Panel types.
- Panels of #type Panel.Table use the #rowCount, #rowSizing, #columnCount, #columnSizing, #leftIndex, #topIndex, and all of the "default" separator properties.
- Panels of #type Panel.TableRow and Panel.TableColumn do not act like regular GraphObjects, instead they are only to be used immediately within a Panel.Table. They are pass-through containers that hold elements for their parent table, and ignore their own scale and angle.
- Panels of #type Panel.Grid use the #gridCellSize and #gridOrigin properties.
- Panels of #type Panel.Viewbox use the #viewboxStretch property.
- Panels of #type Panel.Graduated use the #graduatedMin, #graduatedMax, #graduatedTickUnit, and #graduatedTickBase properties.
For live examples of all Panel types, see the Introduction page on Panels.
Data Binding
Panels also provide fundamental support for data binding. When a diagram gets a new model or when a diagram's model is augmented with additional data, the diagram automatically creates a new Node or Link whose #data property refers to the corresponding node data or link data object.
For more discussion of data binding, please read the Introduction page on Models and Data Binding.
Panels provide support for automatically creating elements within the Panel based on items in a JavaScript Array. This is achieved by setting or binding the #itemArray property, which acts in a manner similar to the Model.nodeDataArray property. You can supply an #itemTemplate, which must itself be a simple Panel, which is copied to create the element in this container Panel corresponding to an item in the itemArray. This property is analogous to the Diagram.nodeTemplate property, although for the diagram the template must be a Node, Group, or simple Part.
Much like the Diagram.nodeTemplateMap, Panel's #itemTemplateMap supports having multiple templates, so that the actual structure of the element created for a data item can be chosen dynamically. Just as the Model.nodeCategoryProperty determines which template in the Diagram.nodeTemplateMap is copied to create a Node, the #itemCategoryProperty names the data property whose value chooses the Panel in the itemTemplateMap to copy for the item.
When binding the #itemArray property, it is commonplace to set Model.copiesArrays and Model.copiesArrayObjects properties to true, so that when a node is copied, the item Array and its contents are copied, not shared. Or more generally, to customize the model's copying processes, you can supply a custom Model.copyNodeDataFunction.
For more discussion and examples of item arrays, please read the Introduction page on Item Arrays.
PanningTool
The PanningTool supports manual panning, where the user can shift the Diagram.position by dragging the mouse.More...
This tool is a standard mouse-move tool, the ToolManager.panningTool. Although the ToolManager.dragSelectingTool takes precedence over this tool, the DragSelectingTool only starts if there is a delay after a mouse-down event. If it does not start, then this PanningTool gets a chance to start.
This tool does not utilize any Adornments or tool handles. This tool does not modify the model or conduct any transaction.
Part
This is the base class for all user-manipulated top-level objects.More... Because it inherits from Panel, it is automatically a visual container of other GraphObjects. Because it thus also inherits from GraphObject, it also has properties such as GraphObject.actualBounds, GraphObject.contextMenu, and GraphObject.visible.
If you just want an object that users can select and manipulate, you can create an instance of this class.
If you want an object that also supports being connected by links to other objects, use the Node class, which inherits from Part. Create those connections by using instances of the Link class.
If you want a node that logically contains a subgraph of nodes and links, use the Group class, which inherits from Node.
If you want an object that decorates another Part, without having to modify that Part, use the Adornment class. Adornments do not support linking or grouping or being selected.
You can construct a Part, add GraphObjects to it programmatically, and then add the part to a diagram by calling Diagram.add. However it is commonplace to add data to a model by setting its Model.nodeDataArray or calling Model.addNodeData, or for Links, setting the GraphLinksModel.linkDataArray or calling GraphLinksModel.addLinkData. Such actions will cause a diagram that is displaying the model to copy a template, which is a Part that may have data Bindings, and add the new part to the diagram. The Panel.data property will refer to that data object in the model.
Some examples of adding Parts to a Diagram:
// A simple Part template myDiagram.nodeTemplate = $(go.Part, "Horizontal", $(go.Shape, "Circle", { width: 20, height: 20 }), $(go.TextBlock, "Hello World") ); // Node templates can be either Nodes, or simple Parts // (But not Groups, Adornments, or Links) // Adds copies of the nodeTemplate bound to the specified node data: myDiagram.model.nodeDataArray = [ { key: "Alpha" }, { key: "Beta" } ]; // Adds one copy of the nodeTemplate bound to the given node data: myDiagram.model.addNodeData( { key: "Gamma" } );
See the Introduction on using Models for examples and more information.
Layers and Z-ordering
Parts added to a Diagram exist in one of the Diagram's Layers. You can specify which layer the part should be in by setting #layerName. Parts cannot be nested in the visual tree -- they cannot be added to other Parts of Panels.
Parts can be individually z-ordered within a layer by setting #zOrder. Parts within the same layer that have a higher zOrder number will be drawn above parts with a lower number.
Size and Position
The size and position of a part are given by its GraphObject.actualBounds. The size is determined by the GraphObjects that are elements inside this part. You can change the position by setting GraphObject.position or Part.location.
The "location" of a part is commonly the same as its "position". The "position" is always the point that is at the top-left corner of the area occupied by the part. But the "location" may be different from the "position" if you want to think of the part as being "at" a different spot in the part. For example, you might want the "location" to be at the center of a Picture that has a TextBlock title of arbitrary size. In this case you would set the #locationSpot to be Spot.Center and the #locationObjectName to be the name of the Picture element in your Part.
A part may be selected or de-selected by setting its #isSelected property. This may also happen due to a call to Diagram.select or other operations that change the selection. The user may change this property as part of the operation of the ClickSelectingTool, due to the user's mouse click, if the part is #selectable.
Ability Properties (Permissions)
There are many properties named "...able", that control what operations the user may perform on this part. These properties correspond to the similarly named properties on Diagram and Layer that govern the behavior for all parts in all layers or for all parts in the given layer. For example, the Part.copyable property corresponds to the properties Diagram.allowCopy and Layer.allowCopy.
For each of these "ability" properties there is a corresponding "can..." predicate. For example, the Part.canCopy predicate is false if any of the three previously named properties is false. Commands and tools will normally call these predicates rather than just looking at Part properties.
For more discussion about permissions, please read: Permissions.
As previously mentioned, each Diagram supports the notion of selected parts. One way of displaying that a part is selected is by modifying the part. You can set the #selectionChanged property to be a function that is called when the value of #isSelected has changed; it is passed the Part as the first argument. The function can modify the color of one or more GraphObjects in the visual tree of that Part. Or perhaps it could toggle the GraphObject.visible property of an object that is normally hidden when the part is not selected.
The Part class also supports showing separate visual objects for a part when it gets selected. These visuals are typically used to show that the part is selected ("selection handles") or are used to allow the user to manipulate or modify the part with a tool ("tool handles"). These handles are instances of Adornments. The #updateAdornments method is responsible for showing or hiding adornments, normally depending on whether the part is selected.
When the #selectionAdorned property is true, a selected part automatically gets an Adornment created for it. By default the selection adornment is just a simple blue box around the Part, and a blue shape following the route of a selected Link. However you can set the #selectionAdornmentTemplate to an arbitrarily complex Adornment. This way it can show more information or buttons for executing various commands when the user selects a Part.
Tool handles are shown for those mode-less mouse-down tools that need it. The process of updating adornments for a part will call Tool.updateAdornments on each tool in ToolManager.mouseDownTools. Most tools might not need special tool handles. But, for example, ResizingTool naturally will want to create an adornment with eight resize handles positioned at the corners and at the middles of the sides of the selected node's visual element, if the node has its #canResize function returning true.
One may not always want the whole Part to get the selection handle or all tool handles. Sometimes one wants to emphasize selection by highlighting a particular element within the part's visual tree. This can be achieved by setting the #selectionObjectName property, and making sure the desired element has the same GraphObject.name property value.
For more discussion about selection, see Selection.
Similarly the #resizeObjectName and #rotateObjectName properties direct the corresponding ResizingTool and RotatingTool to operate on the particular GraphObject in the Part's visual tree with the given name. That includes both providing tool handles and actually modifying properties on that object.
Parts are not resizable or rotatable by default: you need to set #resizable and/or #rotatable to true.
For more discussion about tools, see Tools.
A Part may be positioned (or a Link may be routed) by a Layout. This will happen automatically if Diagram.layout or Group.layout are set. The default Diagram.layout will position any nodes that were not given explicit positions or location.
If you set #isLayoutPositioned to false, this part will not participate in any of the standard layouts, so it will not be moved by a layout or affect other parts in a layout. In order for the part to get a #location or position you will need to supply it explicitly.
As parts are added to or removed from a diagram, the Layout responsible for positioning the part is invalidated.
This will cause the layout to be performed again in the near future, at the end of the transaction.
This automatic layout invalidation also occurs as parts change their visibility (GraphObject.visible)
or their size (GraphObject.actualBounds).
If you do want there to be a Diagram.layout but you do not want an automatic layout to happen
after removing parts (for example), you can set #layoutConditions not to include the Part.LayoutRemoved flag.
In this particular case, you could set #layoutConditions to:
go.Part.LayoutStandard & ~go.Part.LayoutRemoved
.
It may also reasonable for your application to set it to Part.LayoutNone.
Do not forget to consider applying the same conditions to links as well as to nodes and groups.
If you want to save the locations/positions of the parts in a diagram, it is commonplace to data bind the #location to a property on your node data with a TwoWay Binding (call Binding.makeTwoWay). For example:
$(go.Part, "Horizontal", new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify), ...
Then as the nodes are moved, whether manually by the user or automatically by a Layout, the model data is automatically updated with the location.
For more discussion about related topics, see Selection, Tools, and Permissions.
Parts that are templates should have no relationships with other Parts. Only real Parts that are in a Diagram can belong to Groups or have any Adornments. Only real Nodes in a Diagram can be connected with Links.
PathFigure
A PathFigure represents a section of a Geometry.More... It is a single connected series of two-dimensional geometric PathSegments.
The Geometry.figures property is a list of PathFigures.
A PathFigure must not be modified once its containing Geometry has been assigned to a Shape.
PathSegment
A PathSegment represents a straight line or curved segment of a path between two or more points that are part of a PathFigure.More...
A PathSegment must not be modified once its containing PathFigure's Geometry has been assigned to a Shape.
Picture
A Picture is a GraphObject that shows an image, video-frame, or Canvas element.More...
You can specify what to show by either setting the #source URL property to a URL string or the #element property to an HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement.
If a #source URL is set, the Picture will automatically create a corresponding HTMLImageElement and retain a reference to it in memory. If multiple Pictures specify the same #source URL then they will all refer to the same HTMLImageElement.
Some created Pictures:
var $ = go.GraphObject.make; // for conciseness in defining GraphObjects // A shape with the figure set to "example.png". It will show a gray area until the image is loaded: $(go.Picture, { source: "example.png", background: "gray", width: 50, height: 50 }) // Alternatively: $(go.Picture, "example.png", { background: "gray", width: 50, height: 50 })
If an element is not completely loaded during Diagram initialization, a redraw may occur, and if an image's size is not known before loading, the containing Part of this Picture may be resized, causing side effects such as layouts. This can be avoided by knowing the size of the image beforehand, and setting the Picture's GraphObject.desiredSize.
With some images (notably sprite sheets) only a portion of the image is expected to be drawn. The #sourceRect property allows the programmer to specify a rectangular area of the source image that the Picture should display.
The #imageStretch property allows an image to be resized inside of its bounding box. This property does not change the size of the Picture element, it only resizes or re-scales the image to fit (or not) in its bounds.
For examples of sizing and #imageStretch, see the Introduction page on Pictures.
The #errorFunction property allows one to set a function to call when a source fails to load. This is useful in instances where images cannot be guaranteed to work, such as with user specified input. The error function can set the #source to a known good value, but care should be taken to avoid error infinite loops when doing so.
Placeholder
If a Placeholder is in the visual tree of a Group, it represents the area of all of the member Parts of that Group.More... If a Placeholder is in the visual tree of an Adornment, it represents the area of the Adornment.adornedObject. It can only be used in the visual tree of a Group node or an Adornment. There can be at most one Placeholder in a Group or an Adornment.
Point
A Point represents an x- and y-coordinate pair in two-dimensional space.More...
Use the static functions Point.parse and Point.stringify to convert to and from a standard string representation that is independent of the current locale.
When an instance of this class is the value of a property of a GraphObject class or Diagram or CommandHandler or a Tool class, you should treat the object as if it were frozen or read-only -- you cannot modify its properties. This allows the property to return a value without allocating a new instance. If you need to do your own calculations with the value, call #copy to make a new instance with the same values that you can modify.
Many methods modify the object's properties and then return a reference to "this" object. The only instance method to allocate a new object is the #copy method. The static Point.parse method also allocates a new object.
The "Debug" implementation of this class is significantly slower than the "Release" implementation, mostly due to additional error checking.
You cannot inherit from this class.
Rect
A Rect describes a rectangular two-dimensional area as a top-left point (x and y values) and a size (width and height values).More...
Use the static functions Rect.parse and Rect.stringify to convert to and from a standard string representation that is independent of the current locale.
When an instance of this class is the value of a property of a GraphObject class or Diagram or CommandHandler or a Tool class, you should treat the object as if it were frozen or read-only -- you cannot modify its properties. This allows the property to return a value without allocating a new instance. If you need to do your own calculations with the value, call #copy to make a new instance with the same values that you can modify.
Many methods modify the object's properties and then return a reference to "this" object. The only instance method to allocate a new object is the #copy method. The static Rect.parse method also allocates a new object. The #center, #position, and #size properties all allocate and return a new object.
The "Debug" implementation of this class is significantly slower than the "Release" implementation, mostly due to additional error checking.
You cannot inherit from this class.
RelinkingTool
The RelinkingTool allows the user to reconnect an existing Link
if the Link.relinkableTo and/or Link.relinkableFrom properties are true.More...
For a general discussion of validation, see Introduction to Validation.
By default an instance of this tool is installed as a mouse-down tool in the Diagram.toolManager as the ToolManager.relinkingTool.
This tool makes use of two Adornments, each including a relink handle (potentially one for each end of the link), shown when a link is selected.
This tool conducts a transaction while the tool is active. A successful relinking will result in a "LinkRelinked" DiagramEvent and a "Relinking" transaction.
ResizingTool
The ResizingTool is used to interactively change the size of a GraphObject in the selected Part or Node by setting its GraphObject.desiredSize property.More... You may want to save the size to the model by using a TwoWay Binding on the "desiredSize" property of the GraphObject that is named by Part.resizeObjectName. This tool does not operate on Links.
You can limit the permitted minimum and maximum dimensions by setting #minSize and #maxSize. The resizing will also respect the GraphObject.minSize and GraphObject.maxSize properties. Width or height values that are NaN do not constrain the resizing. Override #computeMinSize and/or #computeMaxSize to change this behavior.
You can also limit the width and/or height to be multiples of a particular size by setting Part.resizeCellSize. If either or both of these values are NaN, as they are by default, it will get the values from this tool's #cellSize. Then if either or both of the width and height are still NaN, and if DraggingTool.isGridSnapEnabled is true, it will use the DraggingTool.gridSnapCellSize. Finally it will consider the Diagram.grid's Panel.gridCellSize if #isGridSnapEnabled is true. Override #computeCellSize to change this behavior.
Pressing the Shift key or resizing a Shape with a Shape.geometryStretch of GraphObject.Uniform or GraphObject.UniformToFill will maintain the aspect ratio during the resize. Override #computeReshape to change this behavior.
This tool makes use of an Adornment, shown when the Part or Node is selected, that includes some number of resize handles. The resize handles are normally copies of ResizingTool.handleArchetype, unless you specify a custom resize Adornment by setting Part.resizeAdornmentTemplate. The resize Adornment is normally a "Spot" Panel with eight resize handles, each with GraphObject.alignment set to one of the eight standard Spot values -- the four corners and the four side middles. The GraphObject.alignment is what identifies and distinguishes each of the handles and the behavior when the user drags the handle.
This tool conducts a transaction while the tool is active. A successful resizing will result in a "PartResized" DiagramEvent and a "Resizing" transaction.
For a general discussion of the sizing of objects, see: Introduction to the sizing of GraphObjects. For customizing the ResizingTool, see Introduction to the ResizingTool.
RotatingTool
The RotatingTool is used to interactively change the GraphObject.angle of a GraphObject by setting its GraphObject.angle property.More... You may want to save the angle to the model by using a TwoWay Binding on the "angle" property of the GraphObject that is named by Part.rotateObjectName.
This tool allows the user to rotate the Part.rotateObject of the selected Part. Normally this works with Parts or Nodes; it does not make sense for Links. The Part must be Part.rotatable, which is false by default.
You can limit the permitted angles by setting #snapAngleMultiple and #snapAngleEpsilon. For example, if you want to permit only angles that are multiples of 90 degrees, set #snapAngleMultiple to 90 and #snapAngleEpsilon to 45. Pressing the Shift key ignores these properties.
This tool makes use of an Adornment that includes a rotation handle. It is shown when the selected Part is Part.rotatable. The rotate handle is normally a copy of RotatingTool.handleArchetype. unless you specify a custom rotate Adornment by setting Part.rotateAdornmentTemplate.
This tool conducts a transaction while the tool is active. A successful rotation will result in a "PartRotated" DiagramEvent and a "Rotating" transaction.
For customizing the RotatingTool, see Introduction to the RotatingTool.
RowColumnDefinition
The RowColumnDefinition class describes constraints on a row or a column in a Panel of type Panel.Table.More... It also provides information about the actual layout after the Table Panel has been arranged.
Set
An unordered iterable collection that cannot contain two instances of the same kind of value.More... It optionally enforces the type of elements that may be added to the Set.
An example usage:
var set = new go.Set("string"); // make a set of strings set.add("orange"); set.add("apple"); set.add("orange"); // now set.count === 2 // and set.contains("orange") === true // and set.contains("banana") === false
You can iterate over the items in a Set:
var it = aSet.iterator; while (it.next()) { . . . it.value . . . }Or:
aSet.each(function(val) { . . . val . . . });
Although not precisely implementing the features of the EcmaScript 6 Set class, this GoJS Set class has synonyms for the following methods and property:
- add(val): #add
- delete(val): #remove
- has(val): #contains
- clear(): clear
- size: #count
new go.Set().addAll(iterable)
Shape
A Shape is a GraphObject that shows a geometric figure.More... The Geometry determines what is drawn; the properties #fill and #stroke (and other stroke properties) determine how it is drawn.
There are generally two types of shapes: Those that use a custom Geometry by setting Shape.geometry, and those that receive an automatically generated Geometry using the value of #figure, #toArrow, or #fromArrow. An explicitly set Geometry always supersedes the figure and arrowhead properties.
Some created Shapes:
var $ = go.GraphObject.make; // for conciseness in defining GraphObjects // A shape with the figure set to RoundedRectangle: $(go.Shape, { figure: "RoundedRectangle", fill: "lightgreen" }) // Alternatively: $(go.Shape, "RoundedRectangle", { fill: "lightgreen" }) // A shape with a custom geometry, using geometryString: $(go.Shape, { geometry: go.Geometry.parse("M120 0 L80 80 0 50z") }) // A shape with a custom geometry, using geometryString: $(go.Shape, { geometryString: "F M120 0 L80 80 0 50z", fill: "lightgreen" }) // A common link template, using two shapes, // the first for the link path and the second for the arrowhead myDiagram.linkTemplate = $(go.Link, // The first shape in a link is special, its geometry is set by the Link's routing, // so it does not need a geometry or figure set manually $(go.Shape, { strokeWidth: 2, stroke: 'gray' }), $(go.Shape, { toArrow: "Standard", fill: 'gray', stroke: null }) );
You can see more custom geometry examples and read about geometryString on the Geometry Path Strings Introduction page.
When automatically generating a Shape Geometry, the value of #toArrow takes precedence, then #fromArrow, then #figure. If the value of #toArrow or #fromArrow is "None" then it is ignored, and the "None" value of #figure is identical to "Rectangle".
All of the predefined figures are shown in the Shapes sample. You can define your own named figures by calling the static function Shape.defineFigureGenerator. Get a Map of named figures by calling the static function Shape.getFigureGenerators.
All of the predefined arrowheads are shown in the Arrowheads sample. You can define your own named arrowheads by calling the static function Shape.defineArrowheadGeometry. Get a Map of named arrowheads by calling the static function Shape.getArrowheadGeometries.
You can see a copy of all of the built-in arrowhead definitions in this file: Arrowheads.js.
The Shape properties #parameter1, and #parameter2 determine details of the construction of some #figure geometries. Specifically, they often set the #spot1, #spot2 for the Shape. These spots determine the "inner area" of an Auto panel when a Shape is the main object. See the Auto Panels section of the Panels Introduction page for more details.
Shapes use their geometric bounds when determining hit testing, but use rectangular bounds when participating in (panel) layouts.
Size
A Size describes a width and a height in two-dimensional coordinates.More... The width and height must both be non-negative.
Use the static functions Size.parse and Size.stringify to convert to and from a standard string representation that is independent of the current locale.
When an instance of this class is the value of a property of a GraphObject class or Diagram or CommandHandler or a Tool class, you should treat the object as if it were frozen or read-only -- you cannot modify its properties. This allows the property to return a value without allocating a new instance. If you need to do your own calculations with the value, call #copy to make a new instance with the same values that you can modify.
Many methods modify the object's properties and then return a reference to "this" object. The only instance method to allocate a new object is the #copy method. The static Size.parse method also allocates a new object.
The "Debug" implementation of this class is significantly slower than the "Release" implementation, mostly due to additional error checking.
You cannot inherit from this class.
Spot
A Spot represents a relative point from (0,0) to (1,1) within the bounds of a rectangular area plus an absolute offset.More...
Use the static functions Spot.parse and Spot.stringify to convert to and from a standard string representation that is independent of the current locale.
When an instance of this class is the value of a property of a GraphObject class or Diagram or CommandHandler or a Tool class, you should treat the object as if it were frozen or read-only -- you cannot modify its properties. This allows the property to return a value without allocating a new instance. If you need to do your own calculations with the value, call #copy to make a new instance with the same values that you can modify.
Many methods modify the object's properties and then return a reference to "this" object. The only instance method to allocate a new object is the #copy method. The static Spot.parse method also allocates a new object.
The "Debug" implementation of this class is significantly slower than the "Release" implementation, mostly due to additional error checking.
You cannot inherit from this class.
TextBlock
A TextBlock is a GraphObject that displays a #text string in a given #font.More...
The size and appearance of the text is specified by #font, which takes a well-formed CSS string as its value. The order of the CSS properties given is important for cross-browser compatibility, and should be given in this order:
"font-style font-variant font-weight font-size font-family"
For example, "Italic small-caps bold 32px Georgia, Serif" is a valid font string using every CSS font property. Note that not all browsers may support every property.
Text is drawn using the #stroke brush, which may be any CSS color string or a Brush. Some created TextBlocks:
var $ = go.GraphObject.make; // for conciseness in defining GraphObjects // A TextBlock with text and stroke properties set: $(go.TextBlock, { text: "Hello World", stroke: "gray" }) // Alternatively: $(go.TextBlock, "Hello World", { stroke: "gray" })
TextBlocks typically receive a natural size based on their text and font strings, but often a width is given in order to cause the text to wrap at a certain place. In order for wrapping to occur, the #wrap property must not be TextBlock.None.
TextBlocks can be edited by users using the TextEditingTool. The HTML Element that a given TextBlock uses as its text editor can be customized by setting the #textEditor property. For an example of custom text editing tool use, see the Custom TextEditingTool Sample.
For examples of TextBlock possibilities and functionality, see the Introduction page on TextBlocks.
TextEditingTool
The TextEditingTool is used to let the user interactively edit text in place.More... This sets the TextBlock.text property; you may want to save the changed text to the model by using a TwoWay Binding on the "text" property of editable TextBlocks.
Typically this is used by setting the TextBlock.editable property to true on a particular TextBlock in a part. When the part is selected and the user clicks on the TextBlock or invokes the CommandHandler.editTextBlock command, this tool is started and it uses an HTMLTextArea to perform in-place text editing. (For more details see the description for TextEditingTool.doActivate.)
The TextBlock is accessible as the TextEditingTool.textBlock property. The text editor is accessible as the TextEditingTool.currentTextEditor property. From the text editor control one can access the TextBlock being edited via the 'textEditingTool' property to get to this tool, from which one can use the TextEditingTool.textBlock property.
You can disable mouse clicking from starting this text editing tool by setting Tool.isEnabled to false. You can disable the F2 key from starting this text editing tool by making sure Part.canEdit returns false, by either setting Diagram.allowTextEdit to false or by setting Part.textEditable to false.
If you want to programmatically start the user editing a particular TextBlock, call CommandHandler.editTextBlock. That command method is also invoked by the F2 key on the keyboard.
For a general discussion of text editing validation, see: Introduction to Text Validation. For customizing the TextEditingTool, read about HTMLInfo and see Introduction to Text Editors.
Tool
Tools handle mouse, keyboard, and touch events.More...
The currently running tool, Diagram.currentTool, receives all input events from the Diagram
via canonicalized InputEvents.
For more discussion, see Introduction to Tools.
See samples that make use of tools in the samples index.
Most tools are "mode-less" tools that are managed by the ToolManager, which chooses the current tool based on the kind and position of the mouse event and the parts in the diagram. The ToolManager has properties holding instances of most of the pre-defined Tool classes. These classes include:
- In the ToolManager.mouseDownTools List:
- In the ToolManager.mouseMoveTools List:
- In the ToolManager.mouseUpTools List:
A tool is in the "running" state when it is the value of Diagram.currentTool. The Diagram.currentTool property setter will call #doStop on the old tool and then call #doStart on the new tool.
A tool can then go into the "active" state once it decides it can actually do something. This happens with a call to #doActivate, normally called by the ToolManager. Later it is deactivated (#doDeactivate) and then stopped. #isActive should be true when the tool is "active". Often tools should ignore certain common events, such as calls to #doMouseMove, unless the tool #isActive.
You can prevent a "mode-less" tool (i.e. one managed by the ToolManager) from being started by the ToolManager by setting isEnabled to false.
You can also go into a particular "mode" by setting Diagram.currentTool explicitly, thereby circumventing the normal operation of the ToolManager. This ignores the isEnabled property and does not call the #canStart predicate. The behavior will depend on the tool -- not all of the predefined tools support operating as a "modal" tool.
Tools cannot be shared amongst multiple Diagrams.
If you define a Tool subclass, you may override any of the methods whose names start with "do" and any other methods that are documented to be overridable, such as #canStart. However you must seriously consider calling the base method in order to gets its default behavior. There may be situations where not calling the base method may cause subtle bugs. But that depends on the method and the tool. Please read the Introduction page on Extensions for how to override methods and how to call the base method.
ToolManager
This special Tool is responsible for managing all of the Diagram's mode-less tools, which you can access as the Diagram.toolManager.More...
Mode-less tools are tools that are present in one of the following lists: #mouseDownTools, #mouseMoveTools, or #mouseUpTools. This ToolManager tool is normally the Diagram.defaultTool, so it is also usually the Diagram.currentTool when the user is doing "nothing".
When this tool is running as the current tool, it handles mouse-down, mouse-move, and mouse-up events and the corresponding touch events. For each event it iterates over each of the tools in the corresponding list, calling the tool's Tool.canStart predicate. If that predicate returns true, it starts that tool by making it the diagram's current tool. It then activates the tool and passes on the event to the tool by calling the corresponding method (either Tool.doMouseDown, Tool.doMouseMove, or Tool.doMouseUp).
Because this tool is typically the one running as the diagram's current tool when the user isn't "doing" anything, this tool can also handle other events, such as mouse wheel events and keyboard commands.
Keyboard events are just passed on to the Diagram.commandHandler's CommandHandler.doKeyDown or CommandHandler.doKeyUp method.
This tool also is responsible for showing tooltips. Tooltip Adornments may be declared as any GraphObject.toolTip, or as the Diagram.toolTip if the mouse or finger remains motionless in the background of the diagram. You can set #toolTipDuration to control how long the tooltip remains visible after being motionless.
This tool does not utilize any tool handles. This tool does not conduct any transactions. But of course some of the tools that the ToolManager starts can show tool handles and/or conduct their own transactions.
Transaction
A Transaction holds a list of ChangedEvents collected during a transaction, as the value of the read-only #changes property.More...
Start a transaction by calling UndoManager.startTransaction (or Model.startTransaction or Diagram.startTransaction, which call that method). Be sure to finish a transaction with a matching call to UndoManager.commitTransaction (or Model.commitTransaction or Diagram.commitTransaction), or a call to UndoManager.rollbackTransaction (or the same named methods on Model or Diagram).
If you are performing multiple or repeated changes to a model or diagram, surround all of the code with calls to start and commit the transaction -- do not perform repeated calls to start-commit-start-commit. Typically each change caused by the user, such as a button click or a change of focus or a mouse drag, should perform one transaction in which all changes are made. All of the predefined commands and tools perform transactions.
Undoing or redoing a transaction is done by calling UndoManager.undo or UndoManager.redo. Those methods call the undo or redo methods here.
The UndoManager holds a list of Transactions in its UndoManager.history.
TreeEdge
This holds TreeLayout-specific information about Links.More...
This class inherits from LayoutEdge.
TreeLayout
This layout positions nodes of a tree-structured graph in layers (rows or columns).More...
For a discussion and examples of the most commonly used properties, see Trees page in the Introduction.
If you want to experiment interactively with most of the properties, try the Tree Layout sample.
See samples that make use of TreeLayout in the samples index.
This layout makes use of a LayoutNetwork of TreeVertexes and TreeEdges that normally correspond to the Nodes and Links of the Diagram.
The most commonly set properties for controlling the results of a TreeLayout are:
- #angle: the direction in which the tree grows, from parent to child; the default value of zero means that the tree grows towards the right, with the children of a node arranged in a layer that is a column. An angle of 0 or 180 means that children form vertical layers -- breadth is height and depth is width; an angle of 90 or 270 means that children form horizontal layers -- breadth is width and depth is height.
- #layerSpacing: the distance between layers -- between a parent node and its child nodes.
- #nodeSpacing: the distance between nodes within a layer -- between siblings.
- #alignment: the relative position of a parent node with its children.
- #sorting and #comparer: specify the order of the immediate children of a parent node.
- #compaction: whether subtrees should be packed closer together if there is room.
- #layerStyle: whether the children of one node are aligned with the children of a sibling node.
- #setsPortSpot, #portSpot, #setsChildPortSpot, and #childPortSpot: this controls whether to set the Link.fromSpot and Link.toSpot to be sensible for the #angle.
- #nodeIndent and #nodeIndentPastParent: if the #alignment is TreeLayout.AlignmentStart or TreeLayout.AlignmentEnd, control how much extra space the first child is given when positioned.
- #breadthLimit, #rowSpacing: try to limit the total breadth of a subtree to a certain distance; when there are too many children or when they are too broad, this puts children into additional rows (or columns, depending on the angle) thereby limiting the breadth while increasing the depth of the tree.
When you set one of the TreeLayout properties listed above, that property normally applies to all of the nodes in the tree. What if you want #alignment to be TreeLayout.AlignmentCenterChildren for the root node but TreeLayout.AlignmentBus for the other nodes in the tree? Or what if you want want #layerSpacing to be 50 for all layers except for the layer separating "leaf" nodes from their parent?
One common solution is to set #treeStyle. For the former scenario, you could set #treeStyle to TreeLayout.StyleRootOnly; the value of #alignment would only apply to the root node. For the latter scenario, you could set it to TreeLayout.StyleLastParents; the value of #layerSpacing would apply to all nodes except those that have children but that do not have grandchildren. How do you then set the alignment or layerSpacing for the other nodes? By setting the TreeLayout properties whose names start with "alternate...". In these cases that would mean setting #alternateAlignment or #alternateLayerSpacing.
These TreeLayout properties actually apply to the TreeVertex that the TreeLayout uses to represent a Node within the TreeNetwork. All of those TreeLayout properties are actually stored in #rootDefaults; all of the "alternate..." properties are stored in #alternateDefaults. Depending on the value of #treeStyle, the actual TreeVertex properties for each Node are copied appropriately from either rootDefaults or alternateDefaults. In the default case where treeStyle is TreeLayout.StyleLayered, the alternateDefaults are ignored. (Note that treeStyle, and a few other properties such as #path and #arrangement, apply to the whole layout, not to an individual node/vertex.)
The use of #treeStyle and "alternate..." TreeLayout properties will cover a lot of common needs for tree layout customization. However, there may be times when that is not enough. Imagine a situation where you want a special TreeVertex property value for a particular Node. The solution is to override #assignTreeVertexValues, where you can examine the given TreeVertex, including its corresponding LayoutVertex.node, to decide what TreeVertex property values should apply.
TreeModel
TreeModels support tree-structured graphs of nodes and links.More... Each node can have at most one "tree parent"; cycles are not permitted. The reference to the parent node's key is a property of the child node data.
TreeModels, unlike GraphLinksModels, do not support arbitrary link relationships between nodes, nor is there a separate link data object for each parent-child relationship. Furthermore there is no support for grouping or label nodes.
The #nodeParentKeyProperty property names the property on the node data whose value is the key of the "tree parent" node. The default value for this property is "parent".
For example, one can define a graph consisting of one parent node with two child nodes:
model.nodeDataArray = [ { key: "Alpha" }, { key: "Beta", parent: "Alpha" }, { key: "Gamma", parent: "Alpha" } ];
If you need to show a more complicated graph structure than a tree, use a GraphLinksModel. If you want to have multiple links between the same pair of nodes, or if you want to associate more information with each link and cannot put the information on the child node, you will need to have a separate link data object for each link, and that would require the use of GraphLinksModel.
TreeVertex
This holds TreeLayout-specific information about Nodes.More...
This class inherits from LayoutVertex.
UndoManager
UndoManager observes and records model and diagram changes in transactions and supports undo/redo operations.More... You will need to set the isEnabled property to true in order for users to perform an undo or a redo.
Typically an operation will call startTransaction, make some changes to the Model or Diagram, and then call commitTransaction. Any ChangedEvents that occur will be recorded in a Transaction object. If for some reason you do not wish to complete the transaction successfully, you can call rollbackTransaction instead of commitTransaction.
The history property is a list of Transactions. commitTransaction will add the currentTransaction to the history list. rollbackTransaction will undo the changes remembered in the currentTransaction and then discard it, without changing the history. You can limit how many transactions are remembered in the history by setting maxHistoryLength.
Transactions may be nested. Be sure to call either commitTransaction or rollbackTransaction for each call to startTransaction. Avoid repeated start-commit-start-commit calls as a result of a user's actions. Instead, start, make all changes, and then commit.
If you want to restore the diagram to the state before the latest complete transaction, call undo. Call redo to change the diagram to a later state. If after some number of undo's you start a transaction, all of the history after the current state is discarded, and a new transaction may be recorded. You cannot undo or redo during a transaction.
Initially each Model has its own UndoManager. UndoManagers may be shared by multiple Models by replacing the standard Model.undoManager created by the model constructor.
There are several informational properties:
- isInTransaction is true when a top-level transaction has been started that has not yet been committed or rolled-back.
- currentTransaction holds the flattened list of all ChangedEvents that have happened within the current transaction.
- transactionLevel indicates the current depth of nesting.
- nestedTransactionNames holds the stack of transaction names supplied to startTransaction calls.
- history holds only complete top-level transactions.
- isUndoingRedoing is true during a call to undo or redo.
- historyIndex indicates which Transaction in the history is the next to be "undone"; this is decremented by each undo and incremented by a redo.
- transactionToUndo and transactionToRedo indicate which Transaction may be undone or redone next, if any.
- models returns an iterator over all of the Models that this UndoManager is handling.