Quantcast
Channel: GoJS - Northwoods Software
Viewing all articles
Browse latest Browse all 7069

How to refactor GoJS code?

$
0
0

@tesicg wrote:

We're trying to refactor GoJS code by break it to separate files in this way:

1) Main functionality (we apply module pattern):

var SchemaEditor = (function() {

	/* Private members */
	var black = "black";
	var transparent = "transparent";
	var sd = {
		mode: "pointer", // Set to default mode. Alternatives are "node" and "link", for adding a new node or a new link respectively.
		itemType: "pointer", // Set when user clicks on a node or link button.
		nodeCounter: {}
	};
	var myDiagram;
	var $ = go.GraphObject.make;

	/* Private methods */
	// update the diagram every 250 milliseconds
	function loop() {
		setTimeout(function () { updateStates(); loop(); }, 250);
	}

	// update the value and appearance of each node according to its type and input values
	function updateStates() {
		var oldskip = myDiagram.skipsUndoManager;
		myDiagram.skipsUndoManager = true;
		// do all "input" nodes first
		myDiagram.nodes.each(function (node) {
			if (node.category === "input") {
				doInput(node);
			}
		});
		// now we can do all other kinds of nodes
		myDiagram.nodes.each(function (node) {
			switch (node.category) {
				case "image1": doImage1(node); break;
				case "image2": doImage2(node); break;
				case "image3": doImage3(node); break;
				case "image4": doImage4(node); break;
				case "image5": doImage5(node); break;
				case "image6": doImage6(node); break;
				case "image7": doImage7(node); break;
				case "image8": doImage8(node); break;
				case "image9": doImage9(node); break;
				case "image10": doImage10(node); break;
				case "table": doTable(node); break;
				case "hBar": dohBar(node); break;
			}
		});
		myDiagram.skipsUndoManager = oldskip;
	}

	// update nodes by the specific function for its type
	// determine the color of links coming out of this node based on those coming in and node type
	function doImage1(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doImage2(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doImage3(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doImage4(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doImage5(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doImage6(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doImage7(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doImage8(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doImage9(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doImage10(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function doTable(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function dohBar(node) {
		// assume there is just one input link
		// we just need to update the node's Shape.fill
		node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
	}

	function BarLink() {
		go.Link.call(this);
	}
	go.Diagram.inherit(BarLink, go.Link);

	/** @override */
	BarLink.prototype.getLinkPoint = function (node, port, spot, from, ortho, othernode, otherport) {
		if (node.category === "hBar") {
			var op = go.Link.prototype.getLinkPoint.call(this, othernode, otherport, this.computeSpot(!from), !from, ortho, node, port);
			var r = new go.Rect(port.getDocumentPoint(go.Spot.TopLeft),
								port.getDocumentPoint(go.Spot.BottomRight));
			var y = (op.y > r.centerY) ? r.bottom : r.top;
			if (op.x < r.left) return new go.Point(r.left, y);
			if (op.x > r.right) return new go.Point(r.right, y);
			return new go.Point(op.x, y);
		} else {
			return go.Link.prototype.getLinkPoint.call(this, node, port, spot, from, ortho, othernode, otherport);
		}
	};

	/** @override */
	BarLink.prototype.getLinkDirection = function (node, port, linkpoint, spot, from, ortho, othernode, otherport) {
		var p = port.getDocumentPoint(go.Spot.Center);
		var op = otherport.getDocumentPoint(go.Spot.Center);
		var below = op.y > p.y;
		return below ? 90 : 270;
	};
	// end BarLink class

	var setMode = function (mode, itemType) {
		myDiagram.startTransaction();
		sd.mode = mode;
		sd.itemType = itemType;
		if (mode === "link") {
			if (itemType === 'default') {
				if (document.getElementById("defaultMode").classList.contains('non-pushed')) {
					document.getElementById("defaultMode").classList.remove('non-pushed');
					document.getElementById("defaultMode").classList.add('pushed');
					document.getElementById("orthoMode").classList.add('non-pushed');
					document.getElementById("cornerMode").classList.add('non-pushed');
				}
			}
			if (itemType === 'ortho') {
				if (document.getElementById("orthoMode").classList.contains('non-pushed')) {
					document.getElementById("orthoMode").classList.remove('non-pushed');
					document.getElementById("orthoMode").classList.add('pushed');
					document.getElementById("defaultMode").classList.add('non-pushed');
					document.getElementById("cornerMode").classList.add('non-pushed');
				}
			}
			if (itemType === 'corner') {
				if (document.getElementById("cornerMode").classList.contains('non-pushed')) {
					document.getElementById("cornerMode").classList.remove('non-pushed');
					document.getElementById("cornerMode").classList.add('pushed');
					document.getElementById("defaultMode").classList.add('non-pushed');
					document.getElementById("orthoMode").classList.add('non-pushed');
				}
			}
			myDiagram.allowLink = true;
			myDiagram.nodes.each(function (n) { n.port.cursor = "pointer"; });
		}
		myDiagram.commitTransaction("mode changed");
	};

	// save a model to and load a model from JSON text, displayed below the Diagram
	var save = function () {
		document.getElementById("mySavedModel").value = myDiagram.model.toJson();
		myDiagram.isModified = false;
	};

	var load = function () {
		myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
	};

	function addLinkTemplateMaps() {
		// creates relinkable Links that will avoid crossing Nodes when possible and will jump over other Links in their paths
		myDiagram.linkTemplateMap.add("default",
			$(go.Link,
			{
				relinkableFrom: true, relinkableTo: true,
				selectionAdorned: false, // Links are not adorned when selected so that their color remains visible.
				shadowOffset: new go.Point(0, 0), shadowBlur: 5, shadowColor: "black"
			},
			new go.Binding("isShadowed", "isSelected").ofObject(),
			$(go.Shape,
				{ name: "SHAPE", strokeWidth: 2, stroke: black })));

		myDiagram.linkTemplateMap.add("corner",
		  $(go.Link,
			{ reshapable: true, resegmentable: true, routing: go.Link.AvoidsNodes },
			new go.Binding("points").makeTwoWay(), // TwoWay Binding of Link.points
			$(go.Shape)
		  ));

		myDiagram.linkTemplateMap.add("ortho",
			$(BarLink, // subclass defined below
				{
					routing: go.Link.Orthogonal,
					relinkableFrom: true,
					relinkableTo: true,
					toPortChanged: function (link, oldport, newport) {
						if (newport instanceof go.Shape) link.path.stroke = newport.fill;
					}
				},
				$(go.Shape,
				{ strokeWidth: 2 })
			));
	};

	var initSchemaEditor = function () {
		myDiagram = $(go.Diagram, "myDiagramDiv",
		{
			initialContentAlignment: go.Spot.Center,
			allowDrop: true, // Nodes from the Palette can be dropped into the Diagram
			"undoManager.isEnabled": true,
			"grid.visible": true,

			"linkingTool.portGravity": 0, // no snapping while drawing new links
			"linkingTool.doActivate": function () {
				// change the curve of the LinkingTool.temporaryLink
				this.temporaryLink.curve = (sd.itemType === "default") ? go.Link.Normal : go.Link.Orthogonal;
				go.LinkingTool.prototype.doActivate.call(this);
			},
			// override the link creation process
			"linkingTool.insertLink": function (fromnode, fromport, tonode, toport) {
				// to control what kind of Link is created,
				// change the LinkingTool.archetypeLinkData's category
				myDiagram.model.setCategoryForLinkData(this.archetypeLinkData, sd.itemType);
				// also change the text indicating the condition, which the user can edit
				this.archetypeLinkData.text = sd.itemType;
				return go.LinkingTool.prototype.insertLink.call(this, fromnode, fromport, tonode, toport);
			},

			"clickCreatingTool.archetypeNodeData": {}, // enable ClickCreatingTool
			"clickCreatingTool.isDoubleClick": false, // operates on a single click in background
			"clickCreatingTool.canStart": function () { // but only in "node" creation mode
				return sd.mode === "node" && go.ClickCreatingTool.prototype.canStart.call(this);
			},
			"clickCreatingTool.insertPart": function (loc) { // customize the data for the new node
				sd.nodeCounter[sd.itemType] += 1;
				var newNodeId = sd.itemType + sd.nodeCounter[sd.itemType];
				this.archetypeNodeData = {
					key: newNodeId,
					category: sd.itemType,
					label: newNodeId
				};
				return go.ClickCreatingTool.prototype.insertPart.call(this, loc);
			}
		});

		myDiagram.requestUpdate();

		// when the document is modified, add a "*" to the title and enable the "Save" button
		myDiagram.addDiagramListener("Modified", function (e) {
			var button = document.getElementById("saveModel");
			if (button) button.disabled = !myDiagram.isModified;
			var idx = document.title.indexOf("*");
			if (myDiagram.isModified) {
				if (idx < 0) document.title += "*";
			} else {
				if (idx >= 0) document.title = document.title.substr(0, idx);
			}
		});

		myDiagram.model =
		  $(go.GraphLinksModel,
			{
				linkFromPortIdProperty: "fromPort", // required information:
				linkToPortIdProperty: "toPort" // identifies data property names
			});

		addLinkTemplateMaps();

		loadNodes();

		// add the templates created above to myDiagram and palette
		myDiagram.nodeTemplateMap.add("image1", image1Template);
		myDiagram.nodeTemplateMap.add("image2", image2Template);
		myDiagram.nodeTemplateMap.add("image3", image3Template);
		myDiagram.nodeTemplateMap.add("image4", image4Template);
		myDiagram.nodeTemplateMap.add("image5", image5Template);
		myDiagram.nodeTemplateMap.add("image6", image6Template);
		myDiagram.nodeTemplateMap.add("image7", image7Template);
		myDiagram.nodeTemplateMap.add("image8", image8Template);
		myDiagram.nodeTemplateMap.add("image9", image9Template);
		myDiagram.nodeTemplateMap.add("image10", image10Template);
		myDiagram.nodeTemplateMap.add("table", tableTemplate);
		myDiagram.nodeTemplateMap.add("hBar", hBarTemplate);

		var palette = new go.Palette("palette"); // create a new Palette in the HTML DIV element "palette"

		// share the template map with the Palette
		palette.nodeTemplateMap = myDiagram.nodeTemplateMap;
		//palette.groupTemplateMap = myDiagram.groupTemplateMap;

		palette.model.nodeDataArray = [
		  { category: "image1" },
		  { category: "image2" },
		  { category: "image3" },
		  { category: "image4" },
		  { category: "image5" },
		  { category: "image6" },
		  { category: "image7" },
		  { category: "image8" },
		  { category: "image9" },
		  { category: "image10" },
		  { category: "table" },
		  { category: "hBar" }
		];

		myDiagram.addDiagramListener("ExternalObjectsDropped", function (e) {
			if (myDiagram.currentTool instanceof go.TextEditingTool) {
				myDiagram.currentTool.acceptText(go.TextEditingTool.LostFocus);
			}
			myDiagram.commandHandler.ungroupSelection();
			jQuery.ajax({
				type: "GET",
				url: '/Home/GetRandomObjectProperties'
			}).done(function (data) {
				// loop through selection to find table node and populate properties
				myDiagram.selection.each(function (p) {
					if (p instanceof go.Node && p.category === "table") {
						var nodedata = p.data;
						var properties = data.map(function (item) {
							return { "property_name": item.Item1.toString(), "property_value": item.Item2.toString() };
						});
						myDiagram.model.setDataProperty(nodedata, "properties", properties);
						return;
					}
				});
			});
		});

		// load the initial diagram
		load();

		// continually update the diagram
		loop();
	};

	/* Public methods */
	return {
		initSchemaEditor: initSchemaEditor,
		setMode: setMode,
		save: save,
		load: load
	};
})();

2) The file where we want to keep all node templates (we don't apply module pattern):

function loadNodes() {
    // node template helpers
    this.sharedToolTip =
        $(go.Adornment, "Auto",
            $(go.Shape, "RoundedRectangle", { fill: "lightyellow" }),
            $(go.TextBlock, { margin: 2 },
                new go.Binding("text", "", function (d) { return d.category; })));

    // define some common property settings
    function nodeStyle() {
        return [
            new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
            new go.Binding("isShadowed", "isSelected").ofObject(),
            {
                selectionAdorned: false,
                shadowOffset: new go.Point(0, 0),
                shadowBlur: 15,
                shadowColor: "blue",
                toolTip: sharedToolTip
            }
        ];
    }

    function portStyle0(input) {
        return {
            desiredSize: new go.Size(3, 3),
            fill: "black",
            fromLinkable: !input,
            toLinkable: input,
            cursor: "pointer"
        };
    }

    function portStyle1() {
        return {
            desiredSize: new go.Size(3, 3),
            fill: "black",
            toLinkable: true,
            cursor: "pointer",
            fromLinkable: true,
            fromSpot: go.Spot.TopBottomSides,
            toSpot: go.Spot.TopBottomSides
        };
    }

    this.image1Template =
      $(go.Node, "Vertical", nodeStyle(),
        $(go.Picture, "Images/ElectricalElements/Cell_1.svg"),
        $(go.Shape, "Rectangle", portStyle1(),
          { portId: "", alignment: new go.Spot(0.18, 0) })
      );

    this.image2Template =
      $(go.Node, "Vertical", nodeStyle(),
        $(go.Picture, "Images/ElectricalElements/Cell_2.svg"),
        $(go.Shape, "Rectangle", portStyle1(),
          { portId: "", alignment: new go.Spot(0.33, 0) })
      );

    this.image3Template =
      $(go.Node, "Vertical", nodeStyle(),
        $(go.Picture, "Images/ElectricalElements/GTU.svg"),
        $(go.Shape, "Rectangle", portStyle1(),
          { portId: "", alignment: new go.Spot(0.215, 0) })
      );

    this.image4Template =
        $(go.Node, "Vertical", nodeStyle(),
        $(go.Shape, "Rectangle", portStyle0(false),
            { portId: "1", alignment: new go.Spot(0.125, 0) }),
        $(go.Picture, "Images/ElectricalElements/Sec_1.svg"),
        $(go.Shape, "Rectangle", portStyle1(),
            { portId: "2", alignment: new go.Spot(0.125, 0) })
        );

    this.image5Template =
        $(go.Node, "Vertical", nodeStyle(),
        $(go.Shape, "Rectangle", portStyle0(true),
            { portId: "3", alignment: new go.Spot(0.523, 0) }),
        $(go.Picture, "Images/ElectricalElements/Sec_2.svg"),
        $(go.Shape, "Rectangle", portStyle1(),
            { portId: "4", alignment: new go.Spot(0.523, 0) })
        );

    this.image6Template =
      $(go.Node, "Vertical", nodeStyle(),
        $(go.Shape, "Rectangle", portStyle0(true),
          { portId: "", alignment: new go.Spot(0.12, 0) }),
        $(go.Picture, "Images/ElectricalElements/Sec_3.svg"),
        $(go.Shape, "Rectangle", portStyle1(),
          { portId: "", alignment: new go.Spot(0.12, 0) })
      );

    this.image7Template =
      $(go.Node, "Vertical", nodeStyle(),
        $(go.Picture, "Images/ElectricalElements/Tr_1.svg"),
        $(go.Shape, "Rectangle", portStyle1(),
          { portId: "", alignment: new go.Spot(0.42, 0) })
      );

    this.image8Template =
        $(go.Node, "Vertical", nodeStyle(),
        $(go.Shape, "Rectangle", portStyle1(),
            { portId: "", alignment: new go.Spot(0.59, 0) }),
        $(go.Picture, "Images/ElectricalElements/Tr_2.svg")
        );

    this.image9Template =
        $(go.Node, "Vertical", nodeStyle(),
            {
                resizable: true,
                resizeObjectName: "SHAPE", selectionObjectName: "SHAPE"
            },
        $(go.Shape, "Rectangle",
            {
                name: "SHAPE",
                fill: transparent,
                width: 60,
                height: 40,
                stroke: black,
                strokeWidth: 1,
                strokeDashArray: [5, 5]
            },
          new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify))
        );

    this.image10Template =
      $(go.Node, "Vertical", nodeStyle(),
        $(go.TextBlock,
        {
            text: "text",
            editable: true,
            isMultiline: true
        },
        new go.Binding("text", "text").makeTwoWay())
      );

    this.tableTemplate =
        $(go.Node, go.Panel.Auto, nodeStyle(),
        $(go.Shape, { fill: "white", stroke: "gray", strokeWidth: 1 }),
        { movable: true },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        $(go.Panel, "Table",
            new go.Binding("itemArray", "properties"),
            {
                defaultAlignment: go.Spot.Left,
                defaultColumnSeparatorStroke: "black",
                itemTemplate:
                    $(go.Panel, "TableRow",
                        $(go.TextBlock, new go.Binding("text", "property_name"),
                        { column: 0, margin: 1, font: "bold 7pt sans-serif" }),
                        $(go.TextBlock, new go.Binding("text", "property_value"),
                        { column: 1, margin: 1 })
                    )
            },
            $(go.Panel, "TableRow",
                { isPanelMain: true },
                $(go.TextBlock, "Name",
                { column: 0, margin: new go.Margin(1, 1, 0, 1), font: "bold 7pt sans-serif" }),
                $(go.TextBlock, "Value",
                { column: 1, margin: new go.Margin(1, 1, 0, 1), font: "bold 7pt sans-serif" })
            ),
            $(go.RowColumnDefinition,
            { row: 0, background: "lightgray" }),
            $(go.RowColumnDefinition,
            { row: 1, separatorStroke: "black" })
        )
    );

    this.hBarTemplate =
      $(go.Node,
        new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
        {
            layerName: "Background",
            // special resizing: just at the ends
            resizable: true, resizeObjectName: "SHAPE",
            resizeAdornmentTemplate:
              $(go.Adornment, "Spot",
                $(go.Placeholder),
                $(go.Shape, // left resize handle
                  {
                      alignment: go.Spot.Left, cursor: "col-resize",
                      desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "dodgerblue"
                  }),
                $(go.Shape, // right resize handle
                  {
                      alignment: go.Spot.Right, cursor: "col-resize",
                      desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "dodgerblue"
                  })),
            rotatable: true
        },
        $(go.Shape, "Rectangle",
          {
              name: "SHAPE",
              fill: "black", stroke: null, strokeWidth: 0,
              width: 60, height: 5,
              minSize: new go.Size(50, 5),
              maxSize: new go.Size(Infinity, 5)
          },
          new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
          new go.Binding("fill"),
          { portId: "", toLinkable: true })
      );
};

We call directly loadNodes function from main file, but the palette is empty.

Ideally, we'd like to have separate file with node templates organized according to module pattern as well.

Posts: 1

Participants: 1

Read full topic


Viewing all articles
Browse latest Browse all 7069

Trending Articles