• GoJS组织结构图2


    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8">
      <title>组织结构图</title>
      <meta name="description" content="An organization chart editor -- edit details and change relationships." />
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <!-- Copyright 1998-2019 by Northwoods Software Corporation. -->
    
      <style>
        .inspector {
          display: inline-block;
          font: bold 14px helvetica, sans-serif;
          background-color: #212121;
          /* Grey 900 */
          color: #F5F5F5;
          /* Grey 100 */
          cursor: default;
        }
    
        .inspector table {
          border-collapse: separate;
          border-spacing: 2px;
        }
    
        .inspector td,
        th {
          padding: 2px;
        }
    
        .inspector input {
          background-color: #424242;
          /* Grey 800 */
          color: #F5F5F5;
          /* Grey 100 */
          font: bold 12px helvetica, sans-serif;
          border: 0px;
          padding: 2px;
        }
    
        .inspector input:disabled {
          background-color: #BDBDBD;
          /* Grey 400 */
          color: #616161;
          /* Grey 700 */
        }
    
        .inspector select {
          background-color: #424242;
        }
      </style>
    
      <script src="https://cdnjs.cloudflare.com/ajax/libs/gojs/1.8.13/go-debug.js"></script>
      <!-- this is only for the GoJS Samples framework -->
    
    </head>
    
    <body onload="init()">
      <div id="sample">
        <div id="myDiagramDiv" style="background-color: #34343C; border: solid 1px black; height: 570px;"></div>
        <p>
          <button id="zoomToFit">Zoom to Fit</button>
          <button id="centerRoot">Center on root</button>
        </p>
    
        <div>
          <div id="myInspector">
          </div>
        </div>
        <p>
          This editable organizational chart sample color-codes the Nodes according to the tree level in the hierarchy.
        </p>
        <p>
          Double click on a node in order to add a person or the diagram background to add a new boss. Double clicking the diagram
          uses the
          <a>ClickCreatingTool</a>
          with a custom
          <a>ClickCreatingTool.insertPart</a> to scroll to the new node and start editing the
          <a>TextBlock</a> for its name .
        </p>
        <p>
          Drag a node onto another in order to change relationships. You can also draw a link from a node's background to other nodes
          that have no "boss". Links can also be relinked to change relationships. Right-click or tap-hold a Node to bring up
          a context menu which allows you to:
          <ul>
            <li>Vacate Position - remove the information specfic to the current person in that role</li>
            <li>Remove Role - removes the role entirely and reparents any children</li>
            <li>Remove Department - removes the role and the whole subtree</li>
          </ul>
          Deleting a Node or Link will orphan the child Nodes and generate a new tree. A custom SelectionDeleting
          <a>DiagramEvent</a> listener will clear out the boss info when the parent is removed.
        </p>
        <p>
          Select a node to edit/update node data values. This sample uses the
          <a href="../extensions/dataInspector.html">Data Inspector</a> extension to display and modify Part data.
        </p>
        <p>
          To learn how to build an org chart from scratch with GoJS, see the
          <a href="../learn/index.html">Getting Started tutorial</a>.
        </p>
        <p>
          If you want to have some "assistant" nodes on the side, above the regular reports, see the
          <a href="orgChartAssistants.html">Org Chart Assistants</a> sample, which is a copy of this sample that uses a custom
          <a>TreeLayout</a> to position "assistants" that way.
        </p>
        <div>
          <div>
            <button id="SaveButton" onclick="save()">Save</button>
            <button onclick="load()">Load</button>
            Diagram Model saved in JSON format:
          </div>
          <textarea id="mySavedModel" style="100%; height:270px;">
    { "class": "go.TreeModel",
      "nodeDataArray": [
    {"key":1, "name":"中心主题"}
     ]
    }
        </textarea>
        </div>
      </div>
    </body>
    
    <script id="code">
    
      function init() {
        if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
        var $ = go.GraphObject.make;  //GraphObject是所有图形基类,这里简洁定义模板,$太敏感,为避免与jQuery冲突,此处使用ds
    
        //图表由节点、文字、线、箭头组成。
        myDiagram =
          $(go.Diagram, "myDiagramDiv", // must be the ID or reference to div
            {
              "initialContentAlignment": go.Spot.Center, // 将图表在画布中居中显示
              maxSelectionCount: 1, // 一次允许选择一个部件,
              //"isReadOnly": true, // 只读
              "allowZoom": true, //画布缩放
              //"InitialLayoutCompleted":loadDiagramProperties这是函数名,  //一个DiagramEvent侦听器
              //鼠标滚轮事件放大和缩小,而不是向上和向下滚动
              "toolManager.mouseWheelBehavior": go.ToolManager.WheelZoom,
              "undoManager.isEnabled": true, // 操作支持Ctrl-Z撤销、Ctrl-Y重做
              validCycle: go.Diagram.CycleDestinationTree, // make sure users can only create trees
              //"clickCreatingTool.archetypeNodeData": { // 默认单根节点,这段代码允许多根节点,双击背景创建新的根节点
              //  name: "(自由节点)"
              //},
              "clickCreatingTool.insertPart": function (loc) {  // scroll to the new node
                var node = go.ClickCreatingTool.prototype.insertPart.call(this, loc);
                if (node !== null) {
                  this.diagram.select(node);
                  this.diagram.commandHandler.scrollToPart(node);
                  this.diagram.commandHandler.editTextBlock(node.findObject("NAMETB"));
                }
                return node;
              },
              layout:
              $(go.TreeLayout,
                {
                  treeStyle: go.TreeLayout.StyleLastParents,
                  arrangement: go.TreeLayout.ArrangementHorizontal,
                  // properties for most of the tree:
                  angle: 90,
                  layerSpacing: 35,
                  // properties for the "last parents":
                  alternateAngle: 90,
                  alternateLayerSpacing: 35,
                  alternateAlignment: go.TreeLayout.AlignmentBus,
                  alternateNodeSpacing: 20
                })
            });
    
        // 当文档被修改时,在标题中添加“*”并启用“Save”按钮
        myDiagram.addDiagramListener("Modified", function (e) {
          var button = document.getElementById("SaveButton");
          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);
          }
        });
    
        // manage boss info manually when a node or link is deleted from the diagram
        myDiagram.addDiagramListener("SelectionDeleting", function (e) {
          var part = e.subject.first(); // e.subject is the myDiagram.selection collection,
          // so we'll get the first since we know we only have one selection
          myDiagram.startTransaction("clear boss");
          if (part instanceof go.Node) {
            var it = part.findTreeChildrenNodes(); // find all child nodes
            while (it.next()) { // now iterate through them and clear out the boss information
              var child = it.value;
              var bossText = child.findObject("boss"); // since the boss TextBlock is named, we can access it by name
              if (bossText === null) return;
              bossText.text = "";
            }
          } else if (part instanceof go.Link) {
            var child = part.toNode;
            var bossText = child.findObject("boss"); // since the boss TextBlock is named, we can access it by name
            if (bossText === null) return;
            bossText.text = "";
          }
          myDiagram.commitTransaction("clear boss");
        });
    
        var levelColors = ["#AC193D", "#2672EC", "#8C0095", "#5133AB",
          "#008299", "#D24726", "#008A00", "#094AB2"];
    
        // override TreeLayout.commitNodes to also modify the background brush based on the tree depth level
        myDiagram.layout.commitNodes = function () {
          go.TreeLayout.prototype.commitNodes.call(myDiagram.layout);  // do the standard behavior
          // then go through all of the vertexes and set their corresponding node's Shape.fill
          // to a brush dependent on the TreeVertex.level value
          myDiagram.layout.network.vertexes.each(function (v) {
            if (v.node) {
              var level = v.level % (levelColors.length);
              var color = levelColors[level];
              var shape = v.node.findObject("SHAPE");
              if (shape) shape.stroke = $(go.Brush, "Linear", { 0: color, 1: go.Brush.lightenBy(color, 0.05), start: go.Spot.Left, end: go.Spot.Right });
            }
          });
        };
    
        // when a node is double-clicked, add a child to it
        function createNode(e, obj) {
          //拿到节点的对象,后面要拿什么值就直接拿
          var clicked = obj.part;
          if (clicked !== null) {
            var thisemp = clicked.data;
            myDiagram.startTransaction("createNode");
            var newemp = {
              name: "(新节点)",
              parent: thisemp.key
            };
            //console.log(thisemp.key);
            console.log(myDiagram.model.toJson());
            myDiagram.model.addNodeData(newemp);
            myDiagram.commitTransaction("createNode");
          }
        }
    
        // this is used to determine feedback during drags
        function mayWorkFor(node1, node2) {
          if (!(node1 instanceof go.Node)) return false;  // must be a Node
          if (node1 === node2) return false;  // cannot work for yourself
          if (node2.isInTreeOf(node1)) return false;  // cannot work for someone who works for you
          return true;
        }
    
        // 为大多数文本块提供通用样式
        // Some of these values may be overridden in a particular TextBlock.某些值可能在特定的文本块中被覆盖
        function textStyle() {
          //strokez颜色,添加textAlign: "center"好像效果可能是被覆盖了
          return { font: "9pt  Segoe UI,sans-serif", stroke: "white" };
        }
    
        // This converter is used by the Picture.
        function findHeadShot(key) {
          if (key < 0 || key > 16) return "images/HSnopic.jpg"; // There are only 16 images on the server
          return "images/HS" + key + ".jpg"
        }
    
        // define the Node template 定义节点模板 描述如何构建每个节点
        myDiagram.nodeTemplate =
          $(go.Node, "Auto",//形状自动填充适合 与css设置auto同样效果
            //{ doubleClick: createNode },
            { // handle dragging a Node onto a Node to (maybe) change the reporting relationship
              mouseDragEnter: function (e, node, prev) {
                var diagram = node.diagram;
                var selnode = diagram.selection.first();
                if (!mayWorkFor(selnode, node)) return;
                var shape = node.findObject("SHAPE");
                if (shape) {
                  shape._prevFill = shape.fill;  // remember the original brush
                  shape.fill = "darkred";
                }
              },
              mouseDragLeave: function (e, node, next) {
                var shape = node.findObject("SHAPE");
                if (shape && shape._prevFill) {
                  shape.fill = shape._prevFill;  // restore the original brush
                }
              },
              mouseDrop: function (e, node) {
                var diagram = node.diagram;
                var selnode = diagram.selection.first();  // assume just one Node in selection
                if (mayWorkFor(selnode, node)) {
                  // find any existing link into the selected node
                  var link = selnode.findTreeParentLink();
                  if (link !== null) {  // 重新连接所有已有连接
                    link.fromNode = node;
                  } else {  // 新建连接
                    diagram.toolManager.linkingTool.insertLink(node, node.port, selnode, selnode.port);
                  }
                }
              }
            },
            // for sorting, have the Node.text be the data.name
            new go.Binding("text", "name"),
            // bind the Part.layerName to control the Node's layer depending on whether it isSelected
            new go.Binding("layerName", "isSelected", function (sel) { return sel ? "Foreground" : ""; }).ofObject(),
            //设置节点形状:长方形
            $(go.Shape, "Rectangle",
              {
                name: "SHAPE", fill: "#333333", stroke: 'white', strokeWidth: 3.5,
                // set the port properties: 是否可连接fromLinkable、toLinkable
                portId: "", fromLinkable: false, toLinkable: false, cursor: "pointer"
              }),
            // Panel 有不同的类型,每个类型表示一种布局,通过不同的坐标系统排列    
            $(go.Panel, "Horizontal",
              // 定义文本显示框
              $(go.Panel, "Table",
                {
                  minSize: new go.Size(130, NaN),
                  maxSize: new go.Size(150, NaN),
                  margin: new go.Margin(30),//设置文本和边框距离
                  defaultAlignment: go.Spot.Center
                },
                $(go.RowColumnDefinition, { column: 2,  4 }),
                // 设置文本节点
                $(go.TextBlock, textStyle(),  // the name
                  {
                    row: 0, column: 0, columnSpan: 5,
                    font: "12pt Segoe UI,sans-serif",
                    editable: true, isMultiline: false,// editable文本是否可编辑
                    minSize: new go.Size(10, 16)
                  },
                  //将节点数据nodeDataArray.name与text建立联系
                  new go.Binding("text", "name").makeTwoWay())
              )  // end Table Panel
            ) // end Horizontal Panel
          );  // end Node
    
        // 选中的节点显示用于添加子节点的按钮
        myDiagram.nodeTemplate.selectionAdornmentTemplate =
          $(go.Adornment, "Spot",
            $(go.Panel, "Auto",
              $(go.Placeholder, {
                margin: new go.Margin(4, -20, 50, 4)
              })
            ),
            // 所选节点的删除按钮
            $("Button", {
              alignment: go.Spot.Right,
              alignmentFocus: go.Spot.Left,
              click: function (e, obj) {
                var node = obj.part.adornedPart;
                //console.log(node.data.key);
                if (node !== null) {
                  myDiagram.startTransaction("remove dept");
                  // 删除单个节点myDiagram.model.removeNodeData(node.data)只要能拿到node.data对象就能删除了;
                  //删除整个子树,包括节点本身
                  myDiagram.removeParts(node.findTreeParts());
                  myDiagram.commitTransaction("remove dept");
                }
              }
              // 定义装饰中此按钮的单击行为
            },
              $(go.TextBlock, "-", // 按钮的内容
                {
                  font: "bold 8pt sans-serif"
                })
            ),
            // 所选节点的新增按钮
            $("Button", {
              alignment: go.Spot.Right,
              alignmentFocus: go.Spot.Right,
              click: createNode
            },
              $(go.TextBlock, "+", // 按钮的内容
                {
                  font: "bold 8pt sans-serif"
                })
            )
          );
    
        //监听键盘事件
        myDiagram.commandHandler.doKeyDown = function () {
          var e = myDiagram.lastInput;
          var control = e.control || e.meta;
          var key = e.key;
    
          console.log('key' + key);//Tab和Enter键好像这里取到的名字是空
    
          if (control && (key === 'Z' || key === 'Y')) {
            console.log('Ctrl+Z/Y');
          };
    
          // 取消Del/Backspace删除键的命令关联:
          //if (key === 'Del' || key === 'Backspace') return;
    
          go.CommandHandler.prototype.doKeyDown.call(this);
        };
    
        // 设置线条和箭头,是否允许拖动连接relinkableFrom,relinkableTo
        myDiagram.linkTemplate =
          $(go.Link, go.Link.Orthogonal,
            { corner: 5, relinkableFrom: false, relinkableTo: false },
            $(go.Shape, { strokeWidth: 1.5, stroke: "#F5F5F5" }));  // the link shape
    
        // 拖拽框选功能
        myDiagram.toolManager.dragSelectingTool.box =
          $(go.Part,
            { layerName: "Tool", selectable: true },
            $(go.Shape,
              { name: "SHAPE", fill: null, stroke: "chartreuse", strokeWidth: 3 }));
    
        // read in the JSON-format data from the "mySavedModel" element
        load();
    
        // support editing the properties of the selected person in HTML
        if (window.Inspector) myInspector = new Inspector("myInspector", myDiagram,
          {
            properties: {
              "key": { readOnly: true },
              "comments": {}
            }
          });
    
        // Setup zoom to fit button
        document.getElementById('zoomToFit').addEventListener('click', function () {
          myDiagram.commandHandler.zoomToFit();
        });
    
        document.getElementById('centerRoot').addEventListener('click', function () {
          myDiagram.scale = 1;
          myDiagram.commandHandler.scrollToPart(myDiagram.findNodeForKey(1));
        });
    
      } // end init
    
      // Show the diagram's model in JSON format
      function save() {
        document.getElementById("mySavedModel").value = myDiagram.model.toJson();
        myDiagram.isModified = false;
      }
      function load() {
        // model中的数据每一个js对象都代表着一个相应的模型图中的元素
        myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
        // make sure new data keys are unique positive integers
        var lastkey = 1;
        myDiagram.model.makeUniqueKeyFunction = function (model, data) {
          var k = data.key || lastkey;
          while (model.findNodeDataForKey(k)) k++;
          data.key = lastkey = k;
          return k;
        };
      }
    
      "use strict";//严格模式,在开发中使用严格模式能帮助我们早发现错误
    
      function Inspector(divid, diagram, options) {
        var mainDiv = document.getElementById(divid);
        mainDiv.className = "inspector";
        mainDiv.innerHTML = "";
        this._div = mainDiv;
        this._diagram = diagram;
        this._inspectedProperties = {};
        this._multipleProperties = {};
    
        // Either a GoJS Part or a simple data object, such as Model.modelData
        this.inspectedObject = null;
    
        // Inspector options defaults:
        this.includesOwnProperties = true;
        this.declaredProperties = {};
        this.inspectsSelection = true;
        this.propertyModified = null;
        this.multipleSelection = false;
        this.showAllProperties = false;
        this.showSize = 0;
    
        if (options !== undefined) {
          if (options["includesOwnProperties"] !== undefined) this.includesOwnProperties = options["includesOwnProperties"];
          if (options["properties"] !== undefined) this.declaredProperties = options["properties"];
          if (options["inspectSelection"] !== undefined) this.inspectsSelection = options["inspectSelection"];
          if (options["propertyModified"] !== undefined) this.propertyModified = options["propertyModified"];
          if (options['multipleSelection'] !== undefined) this.multipleSelection = options['multipleSelection'];
          if (options['showAllProperties'] !== undefined) this.showAllProperties = options['showAllProperties'];
          if (options['showSize'] !== undefined) this.showSize = options['showSize'];
        }
    
        var self = this;
        diagram.addModelChangedListener(function (e) {
          if (e.isTransactionFinished) self.inspectObject();
        });
        if (this.inspectsSelection) {
          diagram.addDiagramListener("ChangedSelection", function (e) { self.inspectObject(); });
        }
      }
    
      // Some static predicates to use with the "show" property.
      Inspector.showIfNode = function (part) { return part instanceof go.Node };
      Inspector.showIfLink = function (part) { return part instanceof go.Link };
      Inspector.showIfGroup = function (part) { return part instanceof go.Group };
    
      // Only show the property if its present. Useful for "key" which will be shown on Nodes and Groups, but normally not on Links
      Inspector.showIfPresent = function (data, propname) {
        if (data instanceof go.Part) data = data.data;
        return typeof data === "object" && data[propname] !== undefined;
      };
    
        /**
        * Update the HTML state of this Inspector given the properties of the {@link #inspectedObject}.
        * @param {Object} object is an optional argument, used when {@link #inspectSelection} is false to
        *                        set {@link #inspectedObject} and show and edit that object's properties.
        */
      Inspector.prototype.inspectObject = function (object) {
        var inspectedObject = null;
        var inspectedObjects = null;
        if (object === null) return;
        if (object === undefined) {
          if (this.inspectsSelection) {
            if (this.multipleSelection) { // gets the selection if multiple selection is true
              inspectedObjects = this._diagram.selection;
            } else { // otherwise grab the first object
              inspectedObject = this._diagram.selection.first();
            }
          } else { // if there is a single inspected object
            inspectedObject = this.inspectedObject;
          }
        } else { // if object was passed in as a parameter
          inspectedObject = object;
        }
        if (inspectedObjects && inspectedObjects.count === 1) {
          inspectedObject = inspectedObjects.first();
        }
        if (inspectedObjects && inspectedObjects.count <= 1) {
          inspectedObjects = null;
        }
    
        // single object or no objects
        if (!inspectedObjects || !this.multipleSelection) {
          if (inspectedObject === null) {
            this.inspectedObject = inspectedObject;
            this.updateAllHTML();
            return;
          }
    
          this.inspectedObject = inspectedObject;
          if (this.inspectObject === null) return;
          var mainDiv = this._div;
          mainDiv.innerHTML = '';
    
          // use either the Part.data or the object itself (for model.modelData)
          var data = (inspectedObject instanceof go.Part) ? inspectedObject.data : inspectedObject;
          if (!data) return;
          // Build table:
          var table = document.createElement('table');
          var tbody = document.createElement('tbody');
          this._inspectedProperties = {};
          this.tabIndex = 0;
          var declaredProperties = this.declaredProperties;
    
          // Go through all the properties passed in to the inspector and show them, if appropriate:
          for (var name in declaredProperties) {
            var desc = declaredProperties[name];
            if (!this.canShowProperty(name, desc, inspectedObject)) continue;
            var val = this.findValue(name, desc, data);
            tbody.appendChild(this.buildPropertyRow(name, val));
          }
          // Go through all the properties on the model data and show them, if appropriate:
          if (this.includesOwnProperties) {
            for (var k in data) {
              if (k === '__gohashid') continue; // skip internal GoJS hash property
              if (this._inspectedProperties[k]) continue; // already exists
              if (declaredProperties[k] && !this.canShowProperty(k, declaredProperties[k], inspectedObject)) continue;
              tbody.appendChild(this.buildPropertyRow(k, data[k]));
            }
          }
    
          table.appendChild(tbody);
          mainDiv.appendChild(table);
        } else { // multiple objects selected
          var mainDiv = this._div;
          mainDiv.innerHTML = '';
          var shared = new go.Map(); // for properties that the nodes have in common
          var properties = new go.Map(); // for adding properties
          var all = new go.Map(); // used later to prevent changing properties when unneeded
          var it = inspectedObjects.iterator;
          // Build table:
          var table = document.createElement('table');
          var tbody = document.createElement('tbody');
          this._inspectedProperties = {};
          this.tabIndex = 0;
          var declaredProperties = this.declaredProperties;
          it.next();
          inspectedObject = it.value;
          this.inspectedObject = inspectedObject;
          var data = (inspectedObject instanceof go.Part) ? inspectedObject.data : inspectedObject;
          if (data) { // initial pass to set shared and all
            // Go through all the properties passed in to the inspector and add them to the map, if appropriate:
            for (var name in declaredProperties) {
              var desc = declaredProperties[name];
              if (!this.canShowProperty(name, desc, inspectedObject)) continue;
              var val = this.findValue(name, desc, data);
              if (val === '' && desc && desc.type === 'checkbox') {
                shared.add(name, false);
                all.add(name, false);
              } else {
                shared.add(name, val);
                all.add(name, val);
              }
            }
            // Go through all the properties on the model data and add them to the map, if appropriate:
            if (this.includesOwnProperties) {
              for (var k in data) {
                if (k === '__gohashid') continue; // skip internal GoJS hash property
                if (this._inspectedProperties[k]) continue; // already exists
                if (declaredProperties[k] && !this.canShowProperty(k, declaredProperties[k], inspectedObject)) continue;
                shared.add(k, data[k]);
                all.add(k, data[k]);
              }
            }
          }
          var nodecount = 2;
          while (it.next() && (this.showSize < 1 || nodecount <= this.showSize)) { // grabs all the properties from the other selected objects
            properties.clear();
            inspectedObject = it.value;
            if (inspectedObject) {
              // use either the Part.data or the object itself (for model.modelData)
              data = (inspectedObject instanceof go.Part) ? inspectedObject.data : inspectedObject;
              if (data) {
                // Go through all the properties passed in to the inspector and add them to properties to add, if appropriate:
                for (var name in declaredProperties) {
                  var desc = declaredProperties[name];
                  if (!this.canShowProperty(name, desc, inspectedObject)) continue;
                  var val = this.findValue(name, desc, data);
                  if (val === '' && desc && desc.type === 'checkbox') {
                    properties.add(name, false);
                  } else {
                    properties.add(name, val);
                  }
                }
                // Go through all the properties on the model data and add them to properties to add, if appropriate:
                if (this.includesOwnProperties) {
                  for (var k in data) {
                    if (k === '__gohashid') continue; // skip internal GoJS hash property
                    if (this._inspectedProperties[k]) continue; // already exists
                    if (declaredProperties[k] && !this.canShowProperty(k, declaredProperties[k], inspectedObject)) continue;
                    properties.add(k, data[k]);
                  }
                }
              }
            }
            if (!this.showAllProperties) {
              // Cleans up shared map with properties that aren't shared between the selected objects
              // Also adds properties to the add and shared maps if applicable
              var addIt = shared.iterator;
              var toRemove = [];
              while (addIt.next()) {
                if (properties.has(addIt.key)) {
                  var newVal = all.get(addIt.key) + '|' + properties.get(addIt.key);
                  all.set(addIt.key, newVal);
                  if ((declaredProperties[addIt.key] && declaredProperties[addIt.key].type !== 'color'
                    && declaredProperties[addIt.key].type !== 'checkbox' && declaredProperties[addIt.key].type !== 'select')
                    || !declaredProperties[addIt.key]) { // for non-string properties i.e color
                    newVal = shared.get(addIt.key) + '|' + properties.get(addIt.key);
                    shared.set(addIt.key, newVal);
                  }
                } else { // toRemove array since addIt is still iterating
                  toRemove.push(addIt.key);
                }
              }
              for (var i = 0; i < toRemove.length; i++) { // removes anything that doesn't showAllPropertiess
                shared.remove(toRemove[i]);
                all.remove(toRemove[i]);
              }
            } else {
              // Adds missing properties to all with the correct amount of seperators
              var addIt = properties.iterator;
              while (addIt.next()) {
                if (all.has(addIt.key)) {
                  if ((declaredProperties[addIt.key] && declaredProperties[addIt.key].type !== 'color'
                    && declaredProperties[addIt.key].type !== 'checkbox' && declaredProperties[addIt.key].type !== 'select')
                    || !declaredProperties[addIt.key]) { // for non-string properties i.e color
                    var newVal = all.get(addIt.key) + '|' + properties.get(addIt.key);
                    all.set(addIt.key, newVal);
                  }
                } else {
                  var newVal = '';
                  for (var i = 0; i < nodecount - 1; i++) newVal += '|';
                  newVal += properties.get(addIt.key);
                  all.set(addIt.key, newVal);
                }
              }
              // Adds bars in case properties is not in all
              addIt = all.iterator;
              while (addIt.next()) {
                if (!properties.has(addIt.key)) {
                  if ((declaredProperties[addIt.key] && declaredProperties[addIt.key].type !== 'color'
                    && declaredProperties[addIt.key].type !== 'checkbox' && declaredProperties[addIt.key].type !== 'select')
                    || !declaredProperties[addIt.key]) { // for non-string properties i.e color
                    var newVal = all.get(addIt.key) + '|';
                    all.set(addIt.key, newVal);
                  }
                }
              }
            }
            nodecount++;
          }
          // builds the table property rows and sets multipleProperties to help with updateall
          var mapIt;
          if (!this.showAllProperties) mapIt = shared.iterator;
          else mapIt = all.iterator;
          while (mapIt.next()) {
            tbody.appendChild(this.buildPropertyRow(mapIt.key, mapIt.value)); // shows the properties that are allowed
          }
          table.appendChild(tbody);
          mainDiv.appendChild(table);
          var allIt = all.iterator;
          while (allIt.next()) {
            this._multipleProperties[allIt.key] = allIt.value; // used for updateall to know which properties to change
          }
        }
      };
    
        /**
        * @ignore
        * This predicate should be false if the given property should not be shown.
        * Normally it only checks the value of "show" on the property descriptor.
        * The default value is true.
        * @param {string} propertyName the property name
        * @param {Object} propertyDesc the property descriptor
        * @param {Object} inspectedObject the data object
        * @return {boolean} whether a particular property should be shown in this Inspector
        */
      Inspector.prototype.canShowProperty = function (propertyName, propertyDesc, inspectedObject) {
        if (propertyDesc.show === false) return false;
        // if "show" is a predicate, make sure it passes or do not show this property
        if (typeof propertyDesc.show === "function") return propertyDesc.show(inspectedObject, propertyName);
        return true;
      }
    
        /**
        * @ignore
        * This predicate should be false if the given property should not be editable by the user.
        * Normally it only checks the value of "readOnly" on the property descriptor.
        * The default value is true.
        * @param {string} propertyName the property name
        * @param {Object} propertyDesc the property descriptor
        * @param {Object} inspectedObject the data object
        * @return {boolean} whether a particular property should be shown in this Inspector
        */
      Inspector.prototype.canEditProperty = function (propertyName, propertyDesc, inspectedObject) {
        if (this._diagram.isReadOnly || this._diagram.isModelReadOnly) return false;
        // assume property values that are functions of Objects cannot be edited
        var data = (inspectedObject instanceof go.Part) ? inspectedObject.data : inspectedObject;
        var valtype = typeof data[propertyName];
        if (valtype === "function") return false;
        if (propertyDesc) {
          if (propertyDesc.readOnly === true) return false;
          // if "readOnly" is a predicate, make sure it passes or do not show this property
          if (typeof propertyDesc.readOnly === "function") return !propertyDesc.readOnly(inspectedObject, propertyName);
        }
        return true;
      }
    
        /**
         * @ignore
         * @param {any} propName
         * @param {any} propDesc
         * @param {any} data
         * @return {any}
         */
      Inspector.prototype.findValue = function (propName, propDesc, data) {
        var val = '';
        if (propDesc && propDesc.defaultValue !== undefined) val = propDesc.defaultValue;
        if (data[propName] !== undefined) val = data[propName];
        if (val === undefined) return '';
        return val;
      }
    
        /**
        * @ignore
        * This sets this._inspectedProperties[propertyName] and creates the HTML table row:
        *    <tr>
        *      <td>propertyName</td>
        *      <td><input value=propertyValue /></td>
        *    </tr>
        * @param {string} propertyName the property name
        * @param {*} propertyValue the property value
        * @return the table row
        */
      Inspector.prototype.buildPropertyRow = function (propertyName, propertyValue) {
        var mainDiv = this._div;
        var tr = document.createElement("tr");
    
        var td1 = document.createElement("td");
        td1.textContent = propertyName;
        tr.appendChild(td1);
    
        var td2 = document.createElement("td");
        var decProp = this.declaredProperties[propertyName];
        var input = null;
        var self = this;
        function updateall() { self.updateAllProperties(); }
    
        if (decProp && decProp.type === "select") {
          input = document.createElement("select");
          this.updateSelect(decProp, input, propertyName, propertyValue);
          input.addEventListener("change", updateall);
        } else {
          input = document.createElement("input");
    
          input.value = this.convertToString(propertyValue);
          if (decProp) {
            var t = decProp.type;
            if (t !== 'string' && t !== 'number' && t !== 'boolean' &&
              t !== 'arrayofnumber' && t !== 'point' && t !== 'size' &&
              t !== 'rect' && t !== 'spot' && t !== 'margin') {
              input.setAttribute("type", decProp.type);
            }
            if (decProp.type === "color") {
              if (input.type === "color") {
                input.value = this.convertToColor(propertyValue);
                // input.addEventListener("input", updateall);
                input.addEventListener("change", updateall);
              }
            } if (decProp.type === "checkbox") {
              input.checked = !!propertyValue;
              input.addEventListener("change", updateall);
            }
          }
          if (input.type !== "color") input.addEventListener("blur", updateall);
        }
    
        if (input) {
          input.tabIndex = this.tabIndex++;
          input.disabled = !this.canEditProperty(propertyName, decProp, this.inspectedObject);
          td2.appendChild(input);
        }
        tr.appendChild(td2);
    
        this._inspectedProperties[propertyName] = input;
        return tr;
      };
    
        /**
        * @ignore
        * HTML5 color input will only take hex,
        * so var HTML5 canvas convert the color into hex format.
        * This converts "rgb(255, 0, 0)" into "#FF0000", etc.
        * @param {string} propertyValue
        * @return {string}
        */
      Inspector.prototype.convertToColor = function (propertyValue) {
        var ctx = document.createElement("canvas").getContext("2d");
        ctx.fillStyle = propertyValue;
        return ctx.fillStyle;
      };
    
        /**
        * @ignore
        * @param {string}
        * @return {Array.<number>}
        */
      Inspector.prototype.convertToArrayOfNumber = function (propertyValue) {
        if (propertyValue === "null") return null;
        var split = propertyValue.split(' ');
        var arr = [];
        for (var i = 0; i < split.length; i++) {
          var str = split[i];
          if (!str) continue;
          arr.push(parseFloat(str));
        }
        return arr;
      };
    
        /**
        * @ignore
        * @param {*}
        * @return {string}
        */
      Inspector.prototype.convertToString = function (x) {
        if (x === undefined) return "undefined";
        if (x === null) return "null";
        if (x instanceof go.Point) return go.Point.stringify(x);
        if (x instanceof go.Size) return go.Size.stringify(x);
        if (x instanceof go.Rect) return go.Rect.stringify(x);
        if (x instanceof go.Spot) return go.Spot.stringify(x);
        if (x instanceof go.Margin) return go.Margin.stringify(x);
        if (x instanceof go.List) return this.convertToString(x.toArray());
        if (Array.isArray(x)) {
          var str = "";
          for (var i = 0; i < x.length; i++) {
            if (i > 0) str += " ";
            var v = x[i];
            str += this.convertToString(v);
          }
          return str;
        }
        return x.toString();
      };
    
        /**
        * @ignore
        * Update all of the HTML in this Inspector.
        */
      Inspector.prototype.updateAllHTML = function () {
        var inspectedProps = this._inspectedProperties;
        var diagram = this._diagram;
        var isPart = this.inspectedObject instanceof go.Part;
        var data = isPart ? this.inspectedObject.data : this.inspectedObject;
        if (!data) {  // clear out all of the fields
          for (var name in inspectedProps) {
            var input = inspectedProps[name];
            if (input instanceof HTMLSelectElement) {
              input.innerHTML = "";
            } else if (input.type === "color") {
              input.value = "#000000";
            } else if (input.type === "checkbox") {
              input.checked = false;
            } else {
              input.value = "";
            }
    
          }
        } else {
          for (var name in inspectedProps) {
            var input = inspectedProps[name];
            var propertyValue = data[name];
            if (input instanceof HTMLSelectElement) {
              var decProp = this.declaredProperties[name];
              this.updateSelect(decProp, input, name, propertyValue);
            } else if (input.type === "color") {
              input.value = this.convertToColor(propertyValue);
            } else if (input.type === "checkbox") {
              input.checked = !!propertyValue;
            } else {
              input.value = this.convertToString(propertyValue);
            }
          }
        }
      }
    
        /**
        * @ignore
        * Update an HTMLSelectElement with an appropriate list of choices, given the propertyName
        */
      Inspector.prototype.updateSelect = function (decProp, select, propertyName, propertyValue) {
        select.innerHTML = "";  // clear out anything that was there
        var choices = decProp.choices;
        if (typeof choices === "function") choices = choices(this.inspectedObject, propertyName);
        if (!Array.isArray(choices)) choices = [];
        decProp.choicesArray = choices;  // remember list of actual choice values (not strings)
        for (var i = 0; i < choices.length; i++) {
          var choice = choices[i];
          var opt = document.createElement("option");
          opt.text = this.convertToString(choice);
          select.add(opt, null);
        }
        select.value = this.convertToString(propertyValue);
      }
    
        /**
        * @ignore
        * Update all of the data properties of {@link #inspectedObject} according to the
        * current values held in the HTML input elements.
        */
      Inspector.prototype.updateAllProperties = function () {
        var inspectedProps = this._inspectedProperties;
        var diagram = this._diagram;
        if (diagram.selection.count === 1 || !this.multipleSelection) { // single object update
          var isPart = this.inspectedObject instanceof go.Part;
          var data = isPart ? this.inspectedObject.data : this.inspectedObject;
          if (!data) return;  // must not try to update data when there's no data!
    
          diagram.startTransaction('set all properties');
          for (var name in inspectedProps) {
            var input = inspectedProps[name];
            var value = input.value;
    
            // don't update "readOnly" data properties
            var decProp = this.declaredProperties[name];
            if (!this.canEditProperty(name, decProp, this.inspectedObject)) continue;
    
            // If it's a boolean, or if its previous value was boolean,
            // parse the value to be a boolean and then update the input.value to match
            var type = '';
            if (decProp !== undefined && decProp.type !== undefined) {
              type = decProp.type;
            }
            if (type === '') {
              var oldval = data[name];
              if (typeof oldval === 'boolean') type = 'boolean'; // infer boolean
              else if (typeof oldval === 'number') type = 'number';
              else if (oldval instanceof go.Point) type = 'point';
              else if (oldval instanceof go.Size) type = 'size';
              else if (oldval instanceof go.Rect) type = 'rect';
              else if (oldval instanceof go.Spot) type = 'spot';
              else if (oldval instanceof go.Margin) type = 'margin';
            }
    
            // convert to specific type, if needed
            switch (type) {
              case 'boolean': value = !(value === false || value === 'false' || value === '0'); break;
              case 'number': value = parseFloat(value); break;
              case 'arrayofnumber': value = this.convertToArrayOfNumber(value); break;
              case 'point': value = go.Point.parse(value); break;
              case 'size': value = go.Size.parse(value); break;
              case 'rect': value = go.Rect.parse(value); break;
              case 'spot': value = go.Spot.parse(value); break;
              case 'margin': value = go.Margin.parse(value); break;
              case 'checkbox': value = input.checked; break;
              case 'select': value = decProp.choicesArray[input.selectedIndex]; break;
            }
    
            // in case parsed to be different, such as in the case of boolean values,
            // the value shown should match the actual value
            input.value = value;
    
            // modify the data object in an undo-able fashion
            diagram.model.setDataProperty(data, name, value);
    
            // notify any listener
            if (this.propertyModified !== null) this.propertyModified(name, value, this);
          }
          diagram.commitTransaction('set all properties');
        } else { // selection object update
          diagram.startTransaction('set all properties');
          for (var name in inspectedProps) {
            var input = inspectedProps[name];
            var value = input.value;
            var arr1 = value.split('|');
            var arr2 = [];
            if (this._multipleProperties[name]) {
              // don't split if it is union and its checkbox type
              if (this.declaredProperties[name] && this.declaredProperties[name].type === 'checkbox' && this.showAllProperties) {
                arr2.push(this._multipleProperties[name]);
              } else {
                arr2 = this._multipleProperties[name].toString().split('|');
              }
            }
            var it = diagram.selection.iterator;
            var change = false;
            if (this.declaredProperties[name] && this.declaredProperties[name].type === 'checkbox') change = true; // always change checkbox
            if (arr1.length < arr2.length // i.e Alpha|Beta -> Alpha procs the change
              && (!this.declaredProperties[name] // from and to links
                || !(this.declaredProperties[name] // do not change color checkbox and choices due to them always having less
                  && (this.declaredProperties[name].type === 'color' || this.declaredProperties[name].type === 'checkbox' || this.declaredProperties[name].type === 'choices')))) {
              change = true;
            } else { // standard detection in change in properties
              for (var j = 0; j < arr1.length && j < arr2.length; j++) {
                if (!(arr1[j] === arr2[j])
                  && !(this.declaredProperties[name] && this.declaredProperties[name].type === 'color' && arr1[j].toLowerCase() === arr2[j].toLowerCase())) {
                  change = true;
                }
              }
            }
            if (change) { // only change properties it needs to change instead all of them
              for (var i = 0; i < diagram.selection.count; i++) {
                it.next();
                var isPart = it.value instanceof go.Part;
                var data = isPart ? it.value.data : it.value;
    
                if (data) { // ignores the selected node if there is no data
                  if (i < arr1.length) value = arr1[i];
                  else value = arr1[0];
    
                  // don't update "readOnly" data properties
                  var decProp = this.declaredProperties[name];
                  if (!this.canEditProperty(name, decProp, it.value)) continue;
    
                  // If it's a boolean, or if its previous value was boolean,
                  // parse the value to be a boolean and then update the input.value to match
                  var type = '';
                  if (decProp !== undefined && decProp.type !== undefined) {
                    type = decProp.type;
                  }
                  if (type === '') {
                    var oldval = data[name];
                    if (typeof oldval === 'boolean') type = 'boolean'; // infer boolean
                    else if (typeof oldval === 'number') type = 'number';
                    else if (oldval instanceof go.Point) type = 'point';
                    else if (oldval instanceof go.Size) type = 'size';
                    else if (oldval instanceof go.Rect) type = 'rect';
                    else if (oldval instanceof go.Spot) type = 'spot';
                    else if (oldval instanceof go.Margin) type = 'margin';
                  }
    
                  // convert to specific type, if needed
                  switch (type) {
                    case 'boolean': value = !(value === false || value === 'false' || value === '0'); break;
                    case 'number': value = parseFloat(value); break;
                    case 'arrayofnumber': value = this.convertToArrayOfNumber(value); break;
                    case 'point': value = go.Point.parse(value); break;
                    case 'size': value = go.Size.parse(value); break;
                    case 'rect': value = go.Rect.parse(value); break;
                    case 'spot': value = go.Spot.parse(value); break;
                    case 'margin': value = go.Margin.parse(value); break;
                    case 'checkbox': value = input.checked; break;
                    case 'select': value = decProp.choicesArray[input.selectedIndex]; break;
                  }
    
                  // in case parsed to be different, such as in the case of boolean values,
                  // the value shown should match the actual value
                  input.value = value;
    
                  // modify the data object in an undo-able fashion
                  diagram.model.setDataProperty(data, name, value);
    
                  // notify any listener
                  if (this.propertyModified !== null) this.propertyModified(name, value, this);
                }
              }
            }
          }
          diagram.commitTransaction('set all properties');
        }
      };
    
    
    </script>
    
    </html>

    参考 学习文档  https://liuxiaofan.com/2018/03/16/3521.html

  • 相关阅读:
    刷题总结——宠物收养所(bzoj1208)
    算法复习——trie树(poj2001)
    刷题总结——bzoj2243染色
    算法复习——虚树(消耗战bzoj2286)
    设置SSH自动登陆(免密码,用户名)
    自旋锁Spin lock与互斥锁Mutex的区别
    如何去除Linux文件的^M字符
    重构
    比赛日程安排
    基于libzip的简易压缩(zip)/解压缩(unzip)程序
  • 原文地址:https://www.cnblogs.com/Alwaysbecoding/p/11898305.html
Copyright © 2020-2023  润新知