UNPKG

24.6 kBHTMLView Raw
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4<meta charset="utf-8"/>
5<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
6
7<link rel="stylesheet" href="../assets/css/style.css"/>
8<!-- Copyright 1998-2021 by Northwoods Software Corporation. --> <title> GoJS HTML Interaction -- Northwoods Software </title>
9 <link rel="stylesheet" href="../assets/css/prism.css" />
10 </head>
11 <script>
12
13 window.diagrams = [];
14 window.goCode = function(pre, w, h, parentid, animation) {
15 window.diagrams.push([pre, w, h, parentid, animation]);
16 }
17 </script>
18 <body>
19 <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
20 <div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
21 <div class="md:pl-4">
22 <a class="text-white hover:text-white no-underline hover:no-underline
23 font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
24 <h1 class="mb-0 p-1 ">GoJS</h1>
25 </a>
26 </div>
27 <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
28 <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
29 <path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
30 <path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
31 </svg>
32 </button>
33 <div id="topnavList" class="hidden sm:block items-center w-auto mt-0 text-white p-0 z-20">
34 <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
35 <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
36 <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
37 <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
38 <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
39 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
40 <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
41 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
42 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
43 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
44 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
45 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
46 </ul>
47 </div>
48 </div>
49 <hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
50 </nav>
51
52 <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
53
54 <div id="navSide" class="flex flex-col w-full md:w-40 lg:w-48 text-gray-700 bg-white flex-shrink-0">
55 <div class="flex-shrink-0 px-8 py-4">
56 <button id="navButton" class="rounded-lg md:hidden focus:outline-none focus:ring" aria-label="Navigation">
57 <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
58 <path id="navOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
59 <path id="navClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
60 </svg>
61 </button>
62 </div>
63 <nav id="navList" class="min-h-screen hidden md:block sidebar-nav flex-grow px-1 lg:px-4 pb-4 md:pb-0 md:overflow-y-auto break-words">
64 <a href="index.html">Basics</a>
65 <a href="buildingObjects.html">Building Parts</a>
66 <a href="usingModels.html">Using Models</a>
67 <a href="dataBinding.html">Data Binding</a>
68 <a href="react.html">GoJS with React</a>
69 <a href="angular.html">GoJS with Angular</a>
70 <a href="textBlocks.html">TextBlocks</a>
71 <a href="shapes.html">Shapes</a>
72 <a href="pictures.html">Pictures</a>
73 <a href="panels.html">Panels</a>
74 <a href="tablePanels.html">Table Panels</a>
75 <a href="brush.html">Brushes</a>
76 <a href="sizing.html">Sizing Objects</a>
77 <a href="itemArrays.html">Item Arrays</a>
78 <a href="changedEvents.html">Changed Events</a>
79 <a href="transactions.html">Transactions</a>
80 <a href="viewport.html">Coordinates</a>
81 <a href="initialView.html">Initial View</a>
82 <a href="collections.html">Collections</a>
83 <a href="links.html">Links</a>
84 <a href="linkLabels.html">Link Labels</a>
85 <a href="connectionPoints.html">Link Points</a>
86 <a href="ports.html">Ports</a>
87 <a href="nodes.html">Nodes</a>
88 <a href="debugging.html">Debugging</a>
89 <a href="layouts.html">Layouts</a>
90 <a href="trees.html">Trees</a>
91 <a href="subtrees.html">SubTrees</a>
92 <a href="groups.html">Groups</a>
93 <a href="subgraphs.html">SubGraphs</a>
94 <a href="sizedGroups.html">Sized Groups</a>
95 <a href="selection.html">Selection</a>
96 <a href="highlighting.html">Highlighting</a>
97 <a href="animation.html">Animation</a>
98 <a href="toolTips.html">ToolTips</a>
99 <a href="contextmenus.html">Context Menus</a>
100 <a href="events.html">Diagram Events</a>
101 <a href="tools.html">Tools</a>
102 <a href="commands.html">Commands</a>
103 <a href="permissions.html">Permissions</a>
104 <a href="validation.html">Validation</a>
105 <a href="HTMLInteraction.html">HTML Interaction</a>
106 <a href="layers.html">Layers &amp; Z-ordering</a>
107 <a href="palette.html">Palette</a>
108 <a href="overview.html">Overview</a>
109 <a href="resizing.html">Resizing Diagrams</a>
110 <a href="replacingDeleting.html">Replacing and Deleting</a>
111 <a href="buttons.html">Buttons</a>
112 <a href="templateMaps.html">Template Maps</a>
113 <a href="legends.html">Legends and Titles</a>
114 <a href="extensions.html">Extensions</a>
115 <a href="geometry.html">Geometry Strings</a>
116 <a href="grids.html">Grid Patterns</a>
117 <a href="graduatedPanels.html">Graduated Panels</a>
118 <a href="makingImages.html">Diagram Images</a>
119 <a href="makingSVG.html">Diagram SVG</a>
120 <a href="printing.html">Printing</a>
121 <a href="serverSideImages.html">Server-side Images</a>
122 <a href="nodeScript.html">GoJS in Node.js</a>
123 <a href="testing.html">Testing</a>
124 <a href="storage.html">Storage</a>
125 <a href="performance.html">Performance</a>
126 <a href="source.html">Building from Source</a>
127 <a href="platforms.html">Platforms</a>
128 <a href="deployment.html">Deployment</a>
129 </nav>
130 </div>
131 <div class="pt-4 px-2 md:px-0 lg:px-4 pb-16 w-full overflow-hidden">
132
133
134
135
136
137<h1>HTML Interaction</h1>
138
139<p>
140This intro page explains how to use GoJS Diagrams alongside other HTML elements in a webapp.
141
142<p>
143For custom Text Editors, Context Menus, and ToolTips, which are invoked and hidden via GoJS tool operations, it is best to use the <a>HTMLInfo</a> class. <code>HTMLInfo</code> is described in the second section of this page.
144
145
146
147<h2 id="UsingHTMLAlongsideGoJS">Using HTML Alongside GoJS</h2>
148
149
150<h3 id="EditingPartsWithHTMLDataInspector">Editing Parts with the HTML Data Inspector</h3>
151
152<p>
153Generally, GoJS can interact with the rest of the page via JavaScript that programatically moves and modifies GoJS objects and the Diagram. If you have not read about programatically interacting with Parts and the Model, there is a <a href="../learn/graphobject.html">GraphObject Manipulation tutorial</a> for this purpose.
154
155<p>
156To help programmers get started with HTML controls we have implemented a simple <a href="../extensions/DataInspector.html">Data Inspector Extension</a>, an HTML-based property editor that displays and allows editing of data for the selected Part.
157
158<p>
159The Data Inspector chiefly works via a <code>"ChangedSelection"</code> <a href="events.html">Diagram Listener</a>. When triggered, it populates HTML Fields. Editing those fields and clicking away then update the selected Part by calling <code>diagram.model.setDataProperty</code> to update the model.
160
161<h3 id="JQueryAndGoJS">jQuery and GoJS</h3>
162
163<p>
164GoJS does not depend on jQuery, but the two can be used together. The <a href="../samples/tabs.html">Tabs Sample</a> shows how to use GoJS inside a jQuery tab. The <a href="../samples/htmlInteraction.html">HTML Interaction Sample</a> places a GoJS Palette inside of a jQuery movable window, and a data inspector that modifies the current selected node inside another.
165
166<p>
167jQuery normally sets the <code>$</code> variable. If you are copying code from our samples or documentation, be aware that we usually do this:
168<code>var $ = go.GraphObject.make;</code> so that uses of <code>$</code> in our examples will build <a>GraphObject</a>s and other GoJS objects.
169Caution: calling jQuery when trying to build <b>GoJS</b> objects will cause unusual and cryptic errors.
170So you should locally assign the <code>$</code> variable or use a different variable for building <b>GoJS</b> objects.
171
172<h3 id="HTMLFocusOnDiagrams">HTML Focus on Diagrams</h3>
173
174<p>
175 When a browser element gets focus, some browsers scroll that element into view as much as possible.
176 Because this behavior may be unwelcome in some web apps, the <a>Diagram.scrollsPageOnFocus</a> property defaults to false.
177 However you may want to set this property to true in order to get the standard behavior.
178
179<p>
180You can remove the outline while the Diagram is in focus. This is a CSS effect, not a GoJS effect, and can be removed by removing the CSS outline from all HTML elements inside the Diagram div:
181
182<pre class="lang-css"><code>/* affect all elements inside myDiagramDiv */
183#myDiagramDiv * {
184 outline: none;
185 -webkit-tap-highlight-color: rgba(255, 255, 255, 0); /* mobile webkit */
186}
187</code></pre>
188
189
190
191<h2 id="HTMLInfoClass">The HTMLInfo Class</h2>
192<p>
193Use the <a>HTMLInfo</a> class to display custom HTML page elements, such as a context menu, tooltip, or text editor made of HTML.
194
195<p>
196Properties that can be set to an instance of <code>HTMLInfo</code> include:
197
198<ul>
199 <li><a>TextEditingTool.defaultTextEditor</a>
200 <li><a>TextBlock.textEditor</a>
201 <li><a>GraphObject.contextMenu</a>
202 <li><a>Diagram.contextMenu</a>
203 <li><a>GraphObject.toolTip</a>
204 <li><a>Diagram.toolTip</a>
205</ul>
206
207<h3 id="Usage">Usage</h3>
208
209<p>
210When replacing GoJS functionality with custom functionality, the main concern is when to show and hide the custom content. <code>HTMLInfo</code> does this with two settable functions defined by the programmer and called by GoJS:
211
212<ul>
213<li><a>HTMLInfo.show</a>, called by GoJS when custom information should be displayed, for example when activating a ToolTip, ContextMenuTool, or TextEditingTool.
214<li><a>HTMLInfo.hide</a>, called by GoJS when custom information is finished, and should no longer be displayed, for example when ending these tools.
215</ul>
216
217<p>
218In lieu of setting <a>HTMLInfo.hide</a>, you can set the <a>HTMLInfo.mainElement</a> property to the primary HTML Element that you are showing/hiding, and HTMLInfo will automatically hide the provided element by calling:
219
220<pre class="lang-js"><code>mainElement.style.display = "none";</code></pre>
221
222
223<h3 id="HTMLInfoSamples">HTMLInfo samples</h3>
224
225<ul>
226 <li>Text Editors: <a href="../samples/customTextEditingTool.html">Custom Text Editors sample</a> and <a href="../extensions/TextEditor.html">Re-implementation of the default Text Editor</a>
227 <li>Context Menus: <a href="../samples/customContextMenu.html">Custom Context Menu</a> and <a href="../samples/htmlLightBoxContextMenu.html">HTML Lightbox Context Menu</a> (a re-implementation of the default touch context menu)
228 <li>Tooltips: <a href="../samples/dataVisualization.html">Data Visualization Tooltip</a>
229</ul>
230
231<h3 id="Tooltips">Tooltips</h3>
232
233<p>
234For tooltips, if a <a>GraphObject.toolTip</a> or <a>Diagram.toolTip</a> is set to an instance of <code>HTMLInfo</code>, GoJS calls <code>HTMLInfo.show</code> in <a>ToolManager.showToolTip</a>. After the tooltip delay, GoJS will call <code>HTMLInfo.hide</code> in <a>ToolManager.hideToolTip</a>.
235
236<p>
237What follows is an example using <code>HTMLInfo.show</code> and <code>HTMLInfo.hide</code>, but the <code>HTMLInfo.hide</code> is simple enough that setting the <code>HTMLInfo.mainElement</code> to the tooltip div instead would be sufficient.
238
239<div id="diagramParent" style="position: relative;">
240 <div id="toolTipDIV" style="position: absolute; background: white; z-index: 1000; display: none;">
241 <p id="toolTipParagraph">Tooltip
242 </div>
243</div>
244
245<pre class="lang-js" id="toolTipExample"><code>
246 function showToolTip(obj, diagram, tool) {
247 var toolTipDIV = document.getElementById('toolTipDIV');
248 var pt = diagram.lastInput.viewPoint;
249 toolTipDIV.style.left = (pt.x + 10) + "px";
250 toolTipDIV.style.top = (pt.y + 10) + "px";
251 document.getElementById('toolTipParagraph').textContent = "Tooltip for: " + obj.data.key;
252 toolTipDIV.style.display = "block";
253 }
254
255 function hideToolTip(diagram, tool) {
256 var toolTipDIV = document.getElementById('toolTipDIV');
257 toolTipDIV.style.display = "none";
258 }
259
260 var myToolTip = $(go.HTMLInfo, {
261 show: showToolTip,
262 hide: hideToolTip
263 /*
264 since hideToolTip is very simple,
265 we could have set mainElement instead of setting hide:
266 mainElement: document.getElementById('toolTipDIV')
267 */
268 });
269
270 diagram.nodeTemplate =
271 $(go.Node, "Auto",
272 {
273 toolTip: myToolTip
274 },
275 $(go.Shape, "RoundedRectangle", { strokeWidth: 0},
276 new go.Binding("fill", "color")),
277 $(go.TextBlock,
278 { margin: 8 },
279 new go.Binding("text", "key"))
280 );
281
282 diagram.model = new go.GraphLinksModel(
283 [
284 { key: "Alpha", color: "lightblue" },
285 { key: "Beta", color: "orange" },
286 { key: "Gamma", color: "lightgreen" },
287 { key: "Delta", color: "pink" }
288 ]);
289</code></pre>
290
291<pre class="lang-html"><code>
292 &lt;!-- this must be added as a sibling of the Diagram --&gt;
293 &lt;div id="toolTipDIV" style="position: absolute; background: white; z-index: 1000; display: none;"&gt;
294 &lt;p id="toolTipParagraph"&gt;Tooltip&lt;/p&gt;
295 &lt;/div&gt;
296</code></pre>
297
298<script>goCode("toolTipExample", 600, 160, "diagramParent")</script>
299
300<h3 id="ContextMenus">Context Menus</h3>
301
302<p>
303For context menus, <a>ContextMenuTool.showContextMenu</a> will call <code>HTMLInfo.show</code>. <a>ContextMenuTool.hideContextMenu</a> will call <code>HTMLInfo.hide</code>.
304
305<pre class="lang-js"><code>// Assign an HTMLInfo to the Diagram:
306myDiagram.contextMenu = $(go.HTMLInfo, {
307 show: showContextMenu,
308 hide: hideContextMenu
309});
310
311function showContextMenu(obj, diagram, tool) {
312 // Show the context menu HTML element:
313 SomeDOMElement.style.display = "block";
314
315 // Also show relevant buttons given the current state
316 // and the GraphObject obj; if null, the context menu is for the whole Diagram
317}
318
319function hideContextMenu() {
320 SomeDOMElement.style.display = "none";
321}
322
323function buttonClick() {
324 // do some action when a context menu button is clicked
325
326 // then:
327 myDiagram.currentTool.stopTool();
328}
329</code></pre>
330
331<h3 id="TextEditors">Text Editors</h3>
332
333<p>
334For custom text editors, <a>TextEditingTool.doActivate</a> will call <code>HTMLInfo.show</code>. <a>TextEditingTool.doDeactivate</a> will call <code>HTMLInfo.hide</code>.
335
336<p>
337HTMLInfos used as text editors must also define a <a>HTMLInfo.valueFunction</a>. When <a>TextEditingTool.acceptText</a> is called, GoJS will call <code>HTMLInfo.valueFunction</code> and use the return value as the value for the TextEditingTool completion.
338
339<p>
340
341The example below constructs an HTMLInfo that uses <code>HTMLInfo.show</code> and <code>HTMLInfo.hide</code> to dynamically add, populate, and remove HTML elements from the page.
342
343
344
345<div id="diagramParent2" style="position: relative;">
346</div>
347<pre class="lang-js" id="textEditorExample"><code>
348// Diagram setup. The HTMLInfo is set at the end of this code block.
349diagram.nodeTemplate =
350 $(go.Node, "Auto",
351 $(go.Shape, "RoundedRectangle", { strokeWidth: 0},
352 new go.Binding("fill", "color")),
353 $(go.TextBlock,
354 { editable: true, margin: 8, choices: ['Alpha', 'Beta', 'Gamma', 'Delta'] },
355 new go.Binding("text"))
356 );
357
358diagram.model = new go.GraphLinksModel(
359[
360 { text: "Alpha", color: "lightblue" },
361 { text: "Beta", color: "orange" },
362 { text: "Gamma", color: "lightgreen" },
363 { text: "Delta", color: "pink" }
364]);
365
366// Create an HTMLInfo and dynamically create some HTML to show/hide
367var customEditor = new go.HTMLInfo();
368var customSelectBox = document.createElement("select");
369
370customEditor.show = function(textBlock, diagram, tool) {
371 if (!(textBlock instanceof go.TextBlock)) return;
372
373 // Populate the select box:
374 customSelectBox.innerHTML = "";
375
376 // this sample assumes textBlock.choices is not null
377 var list = textBlock.choices;
378 for (var i = 0; i < list.length; i++) {
379 var op = document.createElement("option");
380 op.text = list[i];
381 op.value = list[i];
382 customSelectBox.add(op, null);
383 }
384
385 // After the list is populated, set the value:
386 customSelectBox.value = textBlock.text;
387
388 // Do a few different things when a user presses a key
389 customSelectBox.addEventListener("keydown", function(e) {
390 var key = e.key;
391 if (key === "Enter") { // Accept on Enter
392 tool.acceptText(go.TextEditingTool.Enter);
393 return;
394 } else if (key === "Tab") { // Accept on Tab
395 tool.acceptText(go.TextEditingTool.Tab);
396 e.preventDefault();
397 return false;
398 } else if (key === "Escape") { // Cancel on Esc
399 tool.doCancel();
400 if (tool.diagram) tool.diagram.focus();
401 }
402 }, false);
403
404 var loc = textBlock.getDocumentPoint(go.Spot.TopLeft);
405 var pos = diagram.transformDocToView(loc);
406 customSelectBox.style.left = pos.x + "px";
407 customSelectBox.style.top = pos.y + "px";
408 customSelectBox.style.position = 'absolute';
409 customSelectBox.style.zIndex = 100; // place it in front of the Diagram
410
411 diagram.div.appendChild(customSelectBox);
412}
413
414customEditor.hide = function(diagram, tool) {
415 diagram.div.removeChild(customSelectBox);
416}
417
418// This is necessary for HTMLInfo instances that are used as text editors
419customEditor.valueFunction = function() { return customSelectBox.value; }
420
421// Set the HTMLInfo:
422diagram.toolManager.textEditingTool.defaultTextEditor = customEditor;
423
424</code></pre>
425<script>goCode("textEditorExample", 600, 160, "diagramParent2")</script>
426
427
428
429
430
431
432 </div>
433 </div>
434
435 <div class="bg-nwoods-primary">
436 <section class="max-w-screen-lg text-white container mx-auto py-2 px-12">
437 <p id="version" class="leading-none mb-2 my-4">GoJS</p>
438 </section>
439 </div><footer class="bg-nwoods-primary text-white">
440 <div class="container max-w-screen-lg mx-auto px-8">
441 <div class="w-full py-6">
442
443 <div class="max-w-screen-lg xl:max-w-screen-xl mx-auto px-4 sm:px-6 md:px-8">
444 <ul class="text-sm font-medium pb-14 sm:pb-20 grid grid-cols-1 sm:grid-cols-3 gap-y-10">
445 <li class="list-none row-span-2">
446 <h2 class="text-base font-semibold tracking-wide">GoJS</h2>
447 <ul class="list-none space-y-4 md:space-y-1 px-0">
448 <li>
449 <a href="../samples/index.html">Samples</a>
450 </li>
451 <li>
452 <a href="../learn/index.html">Learn</a>
453 </li>
454 <li>
455 <a href="../intro/index.html">Intro</a>
456 </li>
457 <li>
458 <a href="../api/index.html">API</a>
459 </li>
460 <li>
461 <a href="../changelog.html">Changelog</a>
462 </li>
463 <li>
464 <a href="https://github.com/NorthwoodsSoftware/GoJS">GitHub</a>
465 </li>
466 </ul>
467 </li>
468 <li class="list-none row-span-2">
469 <h2 class="text-base font-semibold tracking-wide">Support</h2>
470 <ul class="list-none space-y-4 md:space-y-1 px-0">
471 <li>
472 <a href="https://www.nwoods.com/contact.html"
473 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a>
474 </li>
475 <li>
476 <a href="https://forum.nwoods.com/c/gojs">Forum</a>
477 </li>
478 <li>
479 <a href="https://www.nwoods.com/app/activate.aspx?sku=gojs">Activate</a>
480 </li>
481 <li>
482 <a href="https://www.nwoods.com/sales/index.html"
483 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a>
484 </li>
485 <li>
486 <a href="https://www.youtube.com/channel/UC9We8EoX596-6XFjJDtZIDg">Videos</a>
487 </li>
488 </ul>
489 </li>
490 <li class="list-none row-span-2">
491 <h2 class="text-base font-semibold tracking-wide">Company</h2>
492 <ul class="list-none space-y-4 md:space-y-1 px-0">
493 <li>
494 <a href="https://www.nwoods.com">Northwoods</a>
495 </li>
496 <li>
497 <a href="https://www.nwoods.com/about.html">About Us</a>
498 </li>
499 <li>
500 <a href="https://www.nwoods.com/contact.html">Contact Us</a>
501 </li>
502 <li>
503 <a href="https://twitter.com/northwoodsgo">Twitter</a>
504 </li>
505
506 </ul>
507 </li>
508 </ul>
509
510
511 <p class="text-sm text-gray-100 md:mb-6">
512 Copyright 1998-2021 <a class="text-white" href="https://www.nwoods.com">Northwoods Software</a>
513 </p>
514 </div>
515 </div>
516</footer> </body>
517
518<script async src="https://www.googletagmanager.com/gtag/js?id=UA-1506307-5"></script>
519<script>
520 window.dataLayer = window.dataLayer || [];
521 function gtag(){dataLayer.push(arguments);}
522 gtag('js', new Date()); gtag('config', 'UA-1506307-5');
523 var getOutboundLink = function(url, label) {
524 gtag('event', 'click', {
525 'event_category': 'outbound',
526 'event_label': label,
527 'transport_type': 'beacon'
528 });
529 }
530
531 // topnav
532 var topButton = document.getElementById("topnavButton");
533 var topnavList = document.getElementById("topnavList");
534 topButton.addEventListener("click", function() {
535 this.classList.toggle("active");
536 topnavList.classList.toggle("hidden");
537 document.getElementById("topnavOpen").classList.toggle("hidden");
538 document.getElementById("topnavClosed").classList.toggle("hidden");
539 });
540</script>
541 <script src="../assets/js/prism.js"></script>
542 <script src="../release/go.js"></script>
543 <script src="../assets/js/goDoc.js"></script>
544 <script>
545 document.addEventListener("DOMContentLoaded", function() {
546 if (window.go) document.getElementById('version').textContent = "GoJS version " + go.version;
547 if (window.goDoc) window.goDoc();
548 var d = window.diagrams;
549 for (var i = 0; i < d.length; i++) {
550 var dargs = d[i];
551 goCodeExecute(dargs[0], dargs[1], dargs[2], dargs[3], dargs[4]);
552 }
553 if (window.extra) window.extra();
554 });
555 </script>
556</html>