Commit 511e9851d3998d68cffff0ba9e80bdf9fcbafd3f

Authored by ww
1 parent 10b07ecb

fix: build not copy echarts

Showing 2 changed files with 15106 additions and 0 deletions
  1 +/**
  2 + * Copyright (c) 2006-2012, JGraph Ltd
  3 + */
  4 +Format = function (editorUi, container) {
  5 + this.editorUi = editorUi;
  6 + this.container = container;
  7 +};
  8 +
  9 +/**
  10 + * Background color for inactive tabs.
  11 + * 没有激活的tab栏 背景色
  12 + */
  13 +Format.inactiveTabBackgroundColor = "#f1f3f4";
  14 +
  15 +/**
  16 + * Icons for markers (24x16).
  17 + */
  18 +Format.classicFilledMarkerImage = Graph.createSvgImage(
  19 + 20,
  20 + 22,
  21 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 10 2 L 5 8 L 10 14 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>',
  22 + 32,
  23 + 20,
  24 +);
  25 +Format.classicThinFilledMarkerImage = Graph.createSvgImage(
  26 + 20,
  27 + 22,
  28 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 4 L 3 8 L 8 12 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>',
  29 + 32,
  30 + 20,
  31 +);
  32 +Format.openFilledMarkerImage = Graph.createSvgImage(
  33 + 20,
  34 + 22,
  35 + '<path transform="translate(4,2)" stroke-width="2" d="M 8 0 L 0 8 L 8 16 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  36 + 32,
  37 + 20,
  38 +);
  39 +Format.openThinFilledMarkerImage = Graph.createSvgImage(
  40 + 20,
  41 + 22,
  42 + '<path transform="translate(4,2)" stroke-width="2" d="M 8 4 L 0 8 L 8 12 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  43 + 32,
  44 + 20,
  45 +);
  46 +Format.openAsyncFilledMarkerImage = Graph.createSvgImage(
  47 + 20,
  48 + 22,
  49 + '<path transform="translate(4,2)" stroke-width="2" d="M 8 4 L 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  50 + 32,
  51 + 20,
  52 +);
  53 +Format.blockFilledMarkerImage = Graph.createSvgImage(
  54 + 20,
  55 + 22,
  56 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 2 L 8 14 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>',
  57 + 32,
  58 + 20,
  59 +);
  60 +Format.blockThinFilledMarkerImage = Graph.createSvgImage(
  61 + 20,
  62 + 22,
  63 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 4 L 8 12 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>',
  64 + 32,
  65 + 20,
  66 +);
  67 +Format.asyncFilledMarkerImage = Graph.createSvgImage(
  68 + 20,
  69 + 22,
  70 + '<path transform="translate(4,2)" stroke-width="2" d="M 6 8 L 6 4 L 0 8 L 24 8" stroke="#404040" fill="#404040"/>',
  71 + 32,
  72 + 20,
  73 +);
  74 +Format.ovalFilledMarkerImage = Graph.createSvgImage(
  75 + 20,
  76 + 22,
  77 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 A 5 5 0 0 1 5 3 A 5 5 0 0 1 11 8 A 5 5 0 0 1 5 13 A 5 5 0 0 1 0 8 Z M 10 8 L 24 8" stroke="#404040" fill="#404040"/>',
  78 + 32,
  79 + 20,
  80 +);
  81 +Format.diamondFilledMarkerImage = Graph.createSvgImage(
  82 + 20,
  83 + 22,
  84 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 6 2 L 12 8 L 6 14 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>',
  85 + 32,
  86 + 20,
  87 +);
  88 +Format.diamondThinFilledMarkerImage = Graph.createSvgImage(
  89 + 20,
  90 + 22,
  91 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 3 L 16 8 L 8 13 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>',
  92 + 32,
  93 + 20,
  94 +);
  95 +Format.classicMarkerImage = Graph.createSvgImage(
  96 + 20,
  97 + 22,
  98 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 10 2 L 5 8 L 10 14 Z M 5 8 L 24 8" stroke="#404040" fill="transparent"/>',
  99 + 32,
  100 + 20,
  101 +);
  102 +Format.classicThinMarkerImage = Graph.createSvgImage(
  103 + 20,
  104 + 22,
  105 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 4 L 5 8 L 8 12 Z M 5 8 L 24 8" stroke="#404040" fill="transparent"/>',
  106 + 32,
  107 + 20,
  108 +);
  109 +Format.blockMarkerImage = Graph.createSvgImage(
  110 + 20,
  111 + 22,
  112 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 2 L 8 14 Z M 8 8 L 24 8" stroke="#404040" fill="transparent"/>',
  113 + 32,
  114 + 20,
  115 +);
  116 +Format.blockThinMarkerImage = Graph.createSvgImage(
  117 + 20,
  118 + 22,
  119 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 4 L 8 12 Z M 8 8 L 24 8" stroke="#404040" fill="transparent"/>',
  120 + 32,
  121 + 20,
  122 +);
  123 +Format.asyncMarkerImage = Graph.createSvgImage(
  124 + 20,
  125 + 22,
  126 + '<path transform="translate(4,2)" stroke-width="2" d="M 6 8 L 6 4 L 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  127 + 32,
  128 + 20,
  129 +);
  130 +Format.ovalMarkerImage = Graph.createSvgImage(
  131 + 20,
  132 + 22,
  133 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 A 5 5 0 0 1 5 3 A 5 5 0 0 1 11 8 A 5 5 0 0 1 5 13 A 5 5 0 0 1 0 8 Z M 10 8 L 24 8" stroke="#404040" fill="transparent"/>',
  134 + 32,
  135 + 20,
  136 +);
  137 +Format.diamondMarkerImage = Graph.createSvgImage(
  138 + 20,
  139 + 22,
  140 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 6 2 L 12 8 L 6 14 Z M 12 8 L 24 8" stroke="#404040" fill="transparent"/>',
  141 + 32,
  142 + 20,
  143 +);
  144 +Format.diamondThinMarkerImage = Graph.createSvgImage(
  145 + 20,
  146 + 22,
  147 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 3 L 16 8 L 8 13 Z M 16 8 L 24 8" stroke="#404040" fill="transparent"/>',
  148 + 32,
  149 + 20,
  150 +);
  151 +Format.boxMarkerImage = Graph.createSvgImage(
  152 + 20,
  153 + 22,
  154 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 3 L 10 3 L 10 13 L 0 13 Z M 10 8 L 24 8" stroke="#404040" fill="transparent"/>',
  155 + 32,
  156 + 20,
  157 +);
  158 +Format.halfCircleMarkerImage = Graph.createSvgImage(
  159 + 20,
  160 + 22,
  161 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 3 A 5 5 0 0 1 5 8 A 5 5 0 0 1 0 13 M 5 8 L 24 8" stroke="#404040" fill="transparent"/>',
  162 + 32,
  163 + 20,
  164 +);
  165 +Format.dashMarkerImage = Graph.createSvgImage(
  166 + 20,
  167 + 22,
  168 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 12 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  169 + 32,
  170 + 20,
  171 +);
  172 +Format.crossMarkerImage = Graph.createSvgImage(
  173 + 20,
  174 + 22,
  175 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 12 14 M 12 2 L 0 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  176 + 32,
  177 + 20,
  178 +);
  179 +Format.circlePlusMarkerImage = Graph.createSvgImage(
  180 + 20,
  181 + 22,
  182 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 A 6 6 0 0 1 6 2 A 6 6 0 0 1 12 8 A 6 6 0 0 1 6 14 A 6 6 0 0 1 0 8 Z M 6 2 L 6 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  183 + 32,
  184 + 20,
  185 +);
  186 +Format.circleMarkerImage = Graph.createSvgImage(
  187 + 20,
  188 + 22,
  189 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 A 6 6 0 0 1 6 2 A 6 6 0 0 1 12 8 A 6 6 0 0 1 6 14 A 6 6 0 0 1 0 8 Z M 12 8 L 24 8" stroke="#404040" fill="transparent"/>',
  190 + 32,
  191 + 20,
  192 +);
  193 +Format.ERmandOneMarkerImage = Graph.createSvgImage(
  194 + 20,
  195 + 22,
  196 + '<path transform="translate(4,2)" stroke-width="2" d="M 6 2 L 6 14 M 9 2 L 9 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  197 + 32,
  198 + 20,
  199 +);
  200 +Format.ERmanyMarkerImage = Graph.createSvgImage(
  201 + 20,
  202 + 22,
  203 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 12 8 L 0 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  204 + 32,
  205 + 20,
  206 +);
  207 +Format.ERoneToManyMarkerImage = Graph.createSvgImage(
  208 + 20,
  209 + 22,
  210 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 12 8 L 0 14 M 15 2 L 15 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  211 + 32,
  212 + 20,
  213 +);
  214 +Format.ERzeroToOneMarkerImage = Graph.createSvgImage(
  215 + 20,
  216 + 22,
  217 + '<path transform="translate(4,2)" stroke-width="2" d="M 8 8 A 5 5 0 0 1 13 3 A 5 5 0 0 1 18 8 A 5 5 0 0 1 13 13 A 5 5 0 0 1 8 8 Z M 0 8 L 8 8 M 18 8 L 24 8 M 4 3 L 4 13" stroke="#404040" fill="transparent"/>',
  218 + 32,
  219 + 20,
  220 +);
  221 +Format.ERzeroToManyMarkerImage = Graph.createSvgImage(
  222 + 20,
  223 + 22,
  224 + '<path transform="translate(4,2)" stroke-width="2" d="M 8 8 A 5 5 0 0 1 13 3 A 5 5 0 0 1 18 8 A 5 5 0 0 1 13 13 A 5 5 0 0 1 8 8 Z M 0 8 L 8 8 M 18 8 L 24 8 M 0 3 L 8 8 L 0 13" stroke="#404040" fill="transparent"/>',
  225 + 32,
  226 + 20,
  227 +);
  228 +Format.EROneMarkerImage = Graph.createSvgImage(
  229 + 20,
  230 + 22,
  231 + '<path transform="translate(4,2)" stroke-width="2" d="M 5 2 L 5 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  232 + 32,
  233 + 20,
  234 +);
  235 +Format.baseDashMarkerImage = Graph.createSvgImage(
  236 + 20,
  237 + 22,
  238 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 0 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>',
  239 + 32,
  240 + 20,
  241 +);
  242 +Format.doubleBlockMarkerImage = Graph.createSvgImage(
  243 + 20,
  244 + 22,
  245 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 2 L 8 14 Z M 8 8 L 16 2 L 16 14 Z M 16 8 L 24 8" stroke="#404040" fill="transparent"/>',
  246 + 32,
  247 + 20,
  248 +);
  249 +Format.doubleBlockFilledMarkerImage = Graph.createSvgImage(
  250 + 20,
  251 + 22,
  252 + '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 2 L 8 14 Z M 8 8 L 16 2 L 16 14 Z M 16 8 L 24 8" stroke="#404040" fill="#404040"/>',
  253 + 32,
  254 + 20,
  255 +);
  256 +
  257 +/**
  258 + * Adds a style change item to the given menu.
  259 + */
  260 +Format.processMenuIcon = function (elt, transform) {
  261 + var imgs = elt.getElementsByTagName("img");
  262 +
  263 + if (imgs.length > 0) {
  264 + if (Editor.isDarkMode()) {
  265 + imgs[0].style.filter = "invert(100%)";
  266 + }
  267 +
  268 + imgs[0].className = "geIcon";
  269 + imgs[0].style.padding = "0px";
  270 + imgs[0].style.margin = "0 0 0 2px";
  271 +
  272 + if (transform != null) {
  273 + mxUtils.setPrefixedStyle(imgs[0].style, "transform", transform);
  274 + }
  275 + }
  276 +
  277 + return elt;
  278 +};
  279 +
  280 +/**
  281 + * Returns information about the current selection.
  282 + */
  283 +Format.prototype.labelIndex = 0;
  284 +
  285 +/**
  286 + * Returns information about the current selection.
  287 + * 默认选中的formatBar的tab栏索引 / 0是第一个
  288 + */
  289 +Format.prototype.diagramIndex = 0;
  290 +
  291 +/**
  292 + * Returns information about the current selection.
  293 + */
  294 +Format.prototype.currentIndex = 0;
  295 +
  296 +/**
  297 + * Returns information about the current selection.
  298 + * 是否显示关闭按钮
  299 + */
  300 +Format.prototype.showCloseButton = false;
  301 +
  302 +/**
  303 + * Adds the label menu items to the given menu and parent.
  304 + */
  305 +Format.prototype.init = function () {
  306 + var ui = this.editorUi;
  307 + var editor = ui.editor;
  308 + var graph = editor.graph;
  309 + this.update = mxUtils.bind(this, function (sender, evt) {
  310 + this.refresh();
  311 + });
  312 +
  313 + graph.getSelectionModel().addListener(mxEvent.CHANGE, this.update);
  314 + graph.getModel().addListener(mxEvent.CHANGE, this.update);
  315 + graph.addListener(mxEvent.EDITING_STARTED, this.update);
  316 + graph.addListener(mxEvent.EDITING_STOPPED, this.update);
  317 + graph.getView().addListener("unitChanged", this.update);
  318 + editor.addListener("autosaveChanged", this.update);
  319 + graph.addListener(mxEvent.ROOT, this.update);
  320 + ui.addListener("styleChanged", this.update);
  321 +
  322 + this.refresh();
  323 +};
  324 +
  325 +/**
  326 + * Adds the label menu items to the given menu and parent.
  327 + */
  328 +Format.prototype.clear = function () {
  329 + this.container.innerHTML = "";
  330 +
  331 + // Destroy existing panels
  332 + if (this.panels != null) {
  333 + for (var i = 0; i < this.panels.length; i++) {
  334 + this.panels[i].destroy();
  335 + }
  336 + }
  337 +
  338 + this.panels = [];
  339 +};
  340 +
  341 +/**
  342 + * Adds the label menu items to the given menu and parent.
  343 + */
  344 +Format.prototype.refresh = function () {
  345 + if (this.pendingRefresh != null) {
  346 + window.clearTimeout(this.pendingRefresh);
  347 + this.pendingRefresh = null;
  348 + }
  349 +
  350 + this.pendingRefresh = window.setTimeout(
  351 + mxUtils.bind(this, function () {
  352 + this.immediateRefresh();
  353 + }),
  354 + );
  355 +};
  356 +
  357 +/**
  358 + * Adds the label menu items to the given menu and parent.
  359 + */
  360 +Format.prototype.immediateRefresh = function () {
  361 + // Performance tweak: No refresh needed if not visible
  362 + if (this.container.style.width == "0px") {
  363 + return;
  364 + }
  365 +
  366 + this.clear();
  367 + var ui = this.editorUi;
  368 + var graph = ui.editor.graph;
  369 +
  370 + var div = document.createElement("div");
  371 + div.style.whiteSpace = "nowrap";
  372 + div.style.color = "rgb(112, 112, 112)";
  373 + div.style.textAlign = "left";
  374 + div.style.cursor = "default";
  375 + // div.style.display = 'flex'
  376 +
  377 + var label = document.createElement("div");
  378 + label.className = "geFormatSection";
  379 + label.style.textAlign = "center";
  380 + label.style.fontWeight = "bold";
  381 + label.style.paddingTop = "8px";
  382 + label.style.fontSize = "13px";
  383 + label.style.borderWidth = "0px 0px 1px 1px";
  384 + label.style.borderStyle = "solid";
  385 + label.style.display = "inline-block";
  386 + label.style.height = "25px";
  387 + label.style.overflow = "hidden";
  388 + label.style.width = "100%";
  389 + label.style.flex = 'auto'
  390 + this.container.appendChild(div);
  391 +
  392 + // Prevents text selection
  393 + mxEvent.addListener(
  394 + label,
  395 + mxClient.IS_POINTER ? "pointerdown" : "mousedown",
  396 + mxUtils.bind(this, function (evt) {
  397 + evt.preventDefault();
  398 + }),
  399 + );
  400 +
  401 + var ss = ui.getSelectionState();
  402 + var containsLabel = ss.containsLabel;
  403 + var currentLabel = null;
  404 + var currentPanel = null;
  405 +
  406 +
  407 + /**
  408 + * @description 验证是否有数据绑定面板
  409 + * @returns {boolean}
  410 + */
  411 + const validateHasDataSourcePanel = () => {
  412 + const ui = this.editorUi;
  413 + const ss = ui.getSelectionState();
  414 + const vertices = ss.vertices || []
  415 + const sidebarInstance = ui.sidebar
  416 + const cell = vertices[0]
  417 + if (!cell) return false
  418 + const basicAttr = sidebarInstance.enumCellBasicAttribute
  419 + const componentType = cell.getAttribute(basicAttr.COMPONENT_TYPE)
  420 + const hasPermission = sidebarInstance.getComponentPermission(componentType)
  421 + return !!hasPermission.length && hasSavePermission()
  422 + }
  423 +
  424 + var addClickHandler = mxUtils.bind(
  425 + this,
  426 + function (elt, panel, index, lastEntry) {
  427 + var clickHandler = mxUtils.bind(this, function (evt) {
  428 + if (currentLabel != elt) {
  429 + if (containsLabel) {
  430 + this.labelIndex = index;
  431 + } else if (graph.isSelectionEmpty()) {
  432 + this.diagramIndex = index;
  433 + } else {
  434 + this.currentIndex = index;
  435 + }
  436 +
  437 + if (currentLabel != null) {
  438 + currentLabel.style.backgroundColor =
  439 + Format.inactiveTabBackgroundColor;
  440 + currentLabel.style.borderBottomWidth = "1px";
  441 + }
  442 +
  443 + currentLabel = elt;
  444 + currentLabel.style.backgroundColor = "";
  445 + currentLabel.style.borderBottomWidth = "0px";
  446 +
  447 + if (currentPanel != panel) {
  448 + if (currentPanel != null) {
  449 + currentPanel.style.display = "none";
  450 + }
  451 +
  452 + currentPanel = panel;
  453 + currentPanel.style.display = "";
  454 + }
  455 + }
  456 +
  457 + if (!validateHasDataSourcePanel()) this.currentIndex = 0
  458 + });
  459 +
  460 + mxEvent.addListener(elt, "click", clickHandler);
  461 +
  462 + // Prevents text selection
  463 + mxEvent.addListener(
  464 + elt,
  465 + mxClient.IS_POINTER ? "pointerdown" : "mousedown",
  466 + mxUtils.bind(this, function (evt) {
  467 + evt.preventDefault();
  468 + }),
  469 + );
  470 +
  471 + if (
  472 + (lastEntry && currentLabel == null) ||
  473 + index ==
  474 + (containsLabel
  475 + ? this.labelIndex
  476 + : graph.isSelectionEmpty()
  477 + ? this.diagramIndex
  478 + : this.currentIndex)
  479 + ) {
  480 + // Invokes handler directly as a workaround for no click on DIV in KHTML.
  481 + clickHandler();
  482 + }
  483 + },
  484 + );
  485 +
  486 + var idx = 0;
  487 +
  488 + if (graph.isSelectionEmpty()) {
  489 + mxUtils.write(label, mxResources.get("diagram"));
  490 + label.style.borderLeftWidth = "0px";
  491 +
  492 + div.appendChild(label);
  493 + var diagramPanel = div.cloneNode(false);
  494 + this.panels.push(new DiagramFormatPanel(this, ui, diagramPanel));
  495 + this.container.appendChild(diagramPanel);
  496 +
  497 + if (Editor.styles != null) {
  498 + diagramPanel.style.display = "none";
  499 + label.style.width = this.showCloseButton ? "106px" : "50%";
  500 + label.style.cursor = "pointer";
  501 + label.style.backgroundColor = Format.inactiveTabBackgroundColor;
  502 +
  503 + var label2 = label.cloneNode(false);
  504 + label2.style.borderLeftWidth = "1px";
  505 + label2.style.borderRightWidth = "1px";
  506 + label2.style.backgroundColor = Format.inactiveTabBackgroundColor;
  507 +
  508 + addClickHandler(label, diagramPanel, idx++);
  509 +
  510 + var stylePanel = div.cloneNode(false);
  511 + stylePanel.style.display = "none";
  512 + mxUtils.write(label2, mxResources.get("style"));
  513 + div.appendChild(label2);
  514 + this.panels.push(new DiagramStylePanel(this, ui, stylePanel));
  515 + this.container.appendChild(stylePanel);
  516 +
  517 + addClickHandler(label2, stylePanel, idx++);
  518 + }
  519 +
  520 + // Adds button to hide the format panel since
  521 + // people don't seem to find the toolbar button
  522 + // and the menu item in the format menu
  523 + if (this.showCloseButton) {
  524 + var label2 = label.cloneNode(false);
  525 + label2.style.borderLeftWidth = "1px";
  526 + label2.style.borderRightWidth = "1px";
  527 + label2.style.borderBottomWidth = "1px";
  528 + label2.style.backgroundColor = Format.inactiveTabBackgroundColor;
  529 + label2.style.position = "absolute";
  530 + label2.style.right = "0px";
  531 + label2.style.top = "0px";
  532 + label2.style.width = "25px";
  533 +
  534 + var img = document.createElement("img");
  535 + img.setAttribute("border", "0");
  536 + img.setAttribute("src", Dialog.prototype.closeImage);
  537 + img.setAttribute("title", mxResources.get("hide"));
  538 + img.style.position = "absolute";
  539 + img.style.display = "block";
  540 + img.style.right = "0px";
  541 + img.style.top = "8px";
  542 + img.style.cursor = "pointer";
  543 + img.style.marginTop = "1px";
  544 + img.style.marginRight = "6px";
  545 + img.style.border = "1px solid transparent";
  546 + img.style.padding = "1px";
  547 + img.style.opacity = 0.5;
  548 + label2.appendChild(img);
  549 +
  550 + mxEvent.addListener(img, "click", function () {
  551 + ui.actions.get("formatPanel").funct();
  552 + });
  553 +
  554 + div.appendChild(label2);
  555 + }
  556 + } else if (graph.isEditing()) {
  557 + mxUtils.write(label, mxResources.get("text"));
  558 + div.appendChild(label);
  559 + this.panels.push(new TextFormatPanel(this, ui, div));
  560 + } else {
  561 + //选中图形,右侧FormatBar添加样式 TODO add other style
  562 + label.style.backgroundColor = Format.inactiveTabBackgroundColor;
  563 + label.style.borderLeftWidth = "1px";
  564 + label.style.cursor = "pointer";
  565 + label.style.width = containsLabel || ss.cells.length == 0 ? "50%" : validateHasDataSourcePanel() ? "25%" : "33.333%";
  566 + var label2 = label.cloneNode(false);
  567 + var label3 = label2.cloneNode(false);
  568 + var label4 = label3.cloneNode(false);
  569 +
  570 + // Workaround for ignored background in IE
  571 + label2.style.backgroundColor = Format.inactiveTabBackgroundColor;
  572 + label3.style.backgroundColor = Format.inactiveTabBackgroundColor;
  573 + label4.style.backgroundColor = Format.inactiveTabBackgroundColor;
  574 +
  575 + // Style
  576 + if (containsLabel) {
  577 + label2.style.borderLeftWidth = "0px";
  578 + } else {
  579 + label.style.borderLeftWidth = "0px";
  580 + mxUtils.write(label, mxResources.get("style"));
  581 + div.appendChild(label);
  582 +
  583 + var stylePanel = div.cloneNode(false);
  584 + stylePanel.style.display = "none";
  585 + this.panels.push(new StyleFormatPanel(this, ui, stylePanel));
  586 + this.container.appendChild(stylePanel);
  587 +
  588 + addClickHandler(label, stylePanel, idx++);
  589 + }
  590 +
  591 + if (validateHasDataSourcePanel()) {
  592 + // bind data
  593 + mxUtils.write(label4, "数据绑定");
  594 + div.appendChild(label4);
  595 +
  596 + var dataPanel = div.cloneNode(false);
  597 + dataPanel.style.display = "none";
  598 + this.panels.push(new DataFormatPanel(this, ui, dataPanel));
  599 + this.container.appendChild(dataPanel);
  600 + }
  601 +
  602 + // Text
  603 + mxUtils.write(label2, mxResources.get("text"));
  604 + div.appendChild(label2);
  605 +
  606 + var textPanel = div.cloneNode(false);
  607 + textPanel.style.display = "none";
  608 + this.panels.push(new TextFormatPanel(this, ui, textPanel));
  609 + this.container.appendChild(textPanel);
  610 +
  611 + // Arrange
  612 + mxUtils.write(label3, mxResources.get("arrange"));
  613 + div.appendChild(label3);
  614 +
  615 + var arrangePanel = div.cloneNode(false);
  616 + arrangePanel.style.display = "none";
  617 + this.panels.push(new ArrangePanel(this, ui, arrangePanel));
  618 + this.container.appendChild(arrangePanel);
  619 +
  620 + if (ss.cells.length > 0) {
  621 + addClickHandler(label2, textPanel, idx++);
  622 + } else {
  623 + label2.style.display = "none";
  624 + }
  625 + addClickHandler(label3, arrangePanel, idx++);
  626 + if (ss.cells.length > 0) {
  627 + addClickHandler(label4, dataPanel, idx++, true);
  628 + } else {
  629 + label4.style.display = "none";
  630 + }
  631 + }
  632 +};
  633 +
  634 +/**
  635 + * Base class for format panels.
  636 + */
  637 +BaseFormatPanel = function (format, editorUi, container) {
  638 + this.format = format;
  639 + this.editorUi = editorUi;
  640 + this.container = container;
  641 + this.listeners = [];
  642 +};
  643 +
  644 +/**
  645 + *
  646 + */
  647 +BaseFormatPanel.prototype.buttonBackgroundColor = "white";
  648 +
  649 +/**
  650 + * Install input handler.
  651 + */
  652 +BaseFormatPanel.prototype.installInputHandler = function (
  653 + input,
  654 + key,
  655 + defaultValue,
  656 + min,
  657 + max,
  658 + unit,
  659 + textEditFallback,
  660 + isFloat,
  661 +) {
  662 + unit = unit != null ? unit : "";
  663 + isFloat = isFloat != null ? isFloat : false;
  664 +
  665 + var ui = this.editorUi;
  666 + var graph = ui.editor.graph;
  667 +
  668 + min = min != null ? min : 1;
  669 + max = max != null ? max : 999;
  670 +
  671 + var selState = null;
  672 + var updating = false;
  673 +
  674 + var update = mxUtils.bind(this, function (evt) {
  675 + var value = isFloat ? parseFloat(input.value) : parseInt(input.value);
  676 +
  677 + // Special case: angle mod 360
  678 + if (!isNaN(value) && key == mxConstants.STYLE_ROTATION) {
  679 + // Workaround for decimal rounding errors in floats is to
  680 + // use integer and round all numbers to two decimal point
  681 + value = mxUtils.mod(Math.round(value * 100), 36000) / 100;
  682 + }
  683 +
  684 + value = Math.min(max, Math.max(min, isNaN(value) ? defaultValue : value));
  685 +
  686 + if (graph.cellEditor.isContentEditing() && textEditFallback) {
  687 + if (!updating) {
  688 + updating = true;
  689 +
  690 + if (selState != null) {
  691 + graph.cellEditor.restoreSelection(selState);
  692 + selState = null;
  693 + }
  694 +
  695 + textEditFallback(value);
  696 + input.value = value + unit;
  697 +
  698 + // Restore focus and selection in input
  699 + updating = false;
  700 + }
  701 + } else if (
  702 + value != mxUtils.getValue(ui.getSelectionState().style, key, defaultValue)
  703 + ) {
  704 + if (graph.isEditing()) {
  705 + graph.stopEditing(true);
  706 + }
  707 + // TODO update data ??
  708 + graph.getModel().beginUpdate();
  709 + try {
  710 + var cells = ui.getSelectionState().cells;
  711 + graph.setCellStyles(key, value, cells);
  712 +
  713 + // Handles special case for fontSize where HTML labels are parsed and updated
  714 + if (key == mxConstants.STYLE_FONTSIZE) {
  715 + graph.updateLabelElements(cells, function (elt) {
  716 + elt.style.fontSize = value + "px";
  717 + elt.removeAttribute("size");
  718 + });
  719 + }
  720 +
  721 + for (var i = 0; i < cells.length; i++) {
  722 + if (graph.model.getChildCount(cells[i]) == 0) {
  723 + graph.autoSizeCell(cells[i], false);
  724 + }
  725 + }
  726 +
  727 + ui.fireEvent(
  728 + new mxEventObject(
  729 + "styleChanged",
  730 + "keys",
  731 + [key],
  732 + "values",
  733 + [value],
  734 + "cells",
  735 + cells,
  736 + ),
  737 + );
  738 + } finally {
  739 + graph.getModel().endUpdate();
  740 + }
  741 + }
  742 +
  743 + input.value = value + unit;
  744 + mxEvent.consume(evt);
  745 + });
  746 +
  747 + if (textEditFallback && graph.cellEditor.isContentEditing()) {
  748 + // KNOWN: Arrow up/down clear selection text in quirks/IE 8
  749 + // Text size via arrow button limits to 16 in IE11. Why?
  750 + mxEvent.addListener(input, "mousedown", function () {
  751 + if (document.activeElement == graph.cellEditor.textarea) {
  752 + selState = graph.cellEditor.saveSelection();
  753 + }
  754 + });
  755 +
  756 + mxEvent.addListener(input, "touchstart", function () {
  757 + if (document.activeElement == graph.cellEditor.textarea) {
  758 + selState = graph.cellEditor.saveSelection();
  759 + }
  760 + });
  761 + }
  762 +
  763 + mxEvent.addListener(input, "change", update);
  764 + mxEvent.addListener(input, "blur", update);
  765 +
  766 + return update;
  767 +};
  768 +
  769 +/**
  770 + * Adds the given option.
  771 + */
  772 +BaseFormatPanel.prototype.createPanel = function () {
  773 + var div = document.createElement("div");
  774 + div.className = "geFormatSection";
  775 + div.style.padding = "12px 0px 12px 14px";
  776 +
  777 + return div;
  778 +};
  779 +
  780 +/**
  781 + * Adds the given option.
  782 + */
  783 +BaseFormatPanel.prototype.createTitle = function (title) {
  784 + var div = document.createElement("div");
  785 + div.style.padding = "0px 0px 6px 0px";
  786 + div.style.whiteSpace = "nowrap";
  787 + div.style.overflow = "hidden";
  788 + div.style.width = "200px";
  789 + div.style.fontWeight = "bold";
  790 + mxUtils.write(div, title);
  791 +
  792 + return div;
  793 +};
  794 +
  795 +/**
  796 + *
  797 + */
  798 +BaseFormatPanel.prototype.addAction = function (div, name) {
  799 + var action = this.editorUi.actions.get(name);
  800 + var btn = null;
  801 +
  802 + if (action != null && action.isEnabled()) {
  803 + btn = mxUtils.button(action.label, function (evt) {
  804 + action.funct(evt, evt);
  805 + });
  806 +
  807 + var short = action.shortcut != null ? " (" + action.shortcut + ")" : "";
  808 + btn.setAttribute("title", action.label + short);
  809 + btn.style.marginBottom = "2px";
  810 + btn.style.width = "210px";
  811 + div.appendChild(btn);
  812 + result = true;
  813 + }
  814 +
  815 + return btn;
  816 +};
  817 +
  818 +/**
  819 + *
  820 + */
  821 +BaseFormatPanel.prototype.addActions = function (div, names) {
  822 + var lastBr = null;
  823 + var last = null;
  824 + var count = 0;
  825 +
  826 + for (var i = 0; i < names.length; i++) {
  827 + var btn = this.addAction(div, names[i]);
  828 +
  829 + if (btn != null) {
  830 + count++;
  831 +
  832 + if (mxUtils.mod(count, 2) == 0) {
  833 + last.style.marginRight = "2px";
  834 + last.style.width = "104px";
  835 + btn.style.width = "104px";
  836 + lastBr.parentNode.removeChild(lastBr);
  837 + }
  838 +
  839 + lastBr = mxUtils.br(div);
  840 + last = btn;
  841 + }
  842 + }
  843 +
  844 + return count;
  845 +};
  846 +
  847 +/**
  848 + *
  849 + */
  850 +BaseFormatPanel.prototype.createStepper = function (
  851 + input,
  852 + update,
  853 + step,
  854 + height,
  855 + disableFocus,
  856 + defaultValue,
  857 + isFloat,
  858 +) {
  859 + step = step != null ? step : 1;
  860 + height = height != null ? height : 9;
  861 + var bigStep = 10 * step;
  862 +
  863 + var stepper = document.createElement("div");
  864 + mxUtils.setPrefixedStyle(stepper.style, "borderRadius", "3px");
  865 + stepper.style.border = "1px solid rgb(192, 192, 192)";
  866 + stepper.style.position = "absolute";
  867 +
  868 + var up = document.createElement("div");
  869 + up.style.borderBottom = "1px solid rgb(192, 192, 192)";
  870 + up.style.position = "relative";
  871 + up.style.height = height + "px";
  872 + up.style.width = "10px";
  873 + up.className = "geBtnUp";
  874 + stepper.appendChild(up);
  875 +
  876 + var down = up.cloneNode(false);
  877 + down.style.border = "none";
  878 + down.style.height = height + "px";
  879 + down.className = "geBtnDown";
  880 + stepper.appendChild(down);
  881 +
  882 + mxEvent.addGestureListeners(
  883 + down,
  884 + function (evt) {
  885 + // Stops text selection on shift+click
  886 + mxEvent.consume(evt);
  887 + },
  888 + null,
  889 + function (evt) {
  890 + if (input.value == "") {
  891 + input.value = defaultValue || "2";
  892 + }
  893 +
  894 + var val = isFloat ? parseFloat(input.value) : parseInt(input.value);
  895 +
  896 + if (!isNaN(val)) {
  897 + input.value = val - (mxEvent.isShiftDown(evt) ? bigStep : step);
  898 +
  899 + if (update != null) {
  900 + update(evt);
  901 + }
  902 + }
  903 +
  904 + mxEvent.consume(evt);
  905 + },
  906 + );
  907 +
  908 + mxEvent.addGestureListeners(
  909 + up,
  910 + function (evt) {
  911 + // Stops text selection on shift+click
  912 + mxEvent.consume(evt);
  913 + },
  914 + null,
  915 + function (evt) {
  916 + if (input.value == "") {
  917 + input.value = defaultValue || "0";
  918 + }
  919 +
  920 + var val = isFloat ? parseFloat(input.value) : parseInt(input.value);
  921 +
  922 + if (!isNaN(val)) {
  923 + input.value = val + (mxEvent.isShiftDown(evt) ? bigStep : step);
  924 +
  925 + if (update != null) {
  926 + update(evt);
  927 + }
  928 + }
  929 +
  930 + mxEvent.consume(evt);
  931 + },
  932 + );
  933 +
  934 + // Disables transfer of focus to DIV but also :active CSS
  935 + // so it's only used for fontSize where the focus should
  936 + // stay on the selected text, but not for any other input.
  937 + if (disableFocus) {
  938 + var currentSelection = null;
  939 +
  940 + mxEvent.addGestureListeners(
  941 + stepper,
  942 + function (evt) {
  943 + mxEvent.consume(evt);
  944 + },
  945 + null,
  946 + function (evt) {
  947 + // Workaround for lost current selection in page because of focus in IE
  948 + if (currentSelection != null) {
  949 + try {
  950 + currentSelection.select();
  951 + } catch (e) {
  952 + // ignore
  953 + }
  954 +
  955 + currentSelection = null;
  956 + mxEvent.consume(evt);
  957 + }
  958 + },
  959 + );
  960 + } else {
  961 + // Stops propagation on checkbox labels
  962 + mxEvent.addListener(stepper, "click", function (evt) {
  963 + mxEvent.consume(evt);
  964 + });
  965 + }
  966 +
  967 + return stepper;
  968 +};
  969 +
  970 +/**
  971 + * Adds the given option.
  972 + */
  973 +BaseFormatPanel.prototype.createOption = function (
  974 + label,
  975 + isCheckedFn,
  976 + setCheckedFn,
  977 + listener,
  978 + fn,
  979 +) {
  980 + var div = document.createElement("div");
  981 + div.style.padding = "3px 0px 3px 0px";
  982 + div.style.whiteSpace = "nowrap";
  983 + div.style.textOverflow = "ellipsis";
  984 + div.style.overflow = "hidden";
  985 + div.style.width = "200px";
  986 + div.style.height = "18px";
  987 +
  988 + var cb = document.createElement("input");
  989 + cb.setAttribute("type", "checkbox");
  990 + cb.style.margin = "1px 6px 0px 0px";
  991 + cb.style.verticalAlign = "top";
  992 + div.appendChild(cb);
  993 +
  994 + var span = document.createElement("span");
  995 + span.style.verticalAlign = "top";
  996 + span.style.userSelect = "none";
  997 + mxUtils.write(span, label);
  998 + div.appendChild(span);
  999 +
  1000 + var applying = false;
  1001 + var value = isCheckedFn();
  1002 +
  1003 + var apply = function (newValue, evt) {
  1004 + if (!applying) {
  1005 + applying = true;
  1006 +
  1007 + if (newValue) {
  1008 + cb.setAttribute("checked", "checked");
  1009 + cb.defaultChecked = true;
  1010 + cb.checked = true;
  1011 + } else {
  1012 + cb.removeAttribute("checked");
  1013 + cb.defaultChecked = false;
  1014 + cb.checked = false;
  1015 + }
  1016 +
  1017 + if (value != newValue) {
  1018 + value = newValue;
  1019 +
  1020 + // Checks if the color value needs to be updated in the model
  1021 + if (isCheckedFn() != value) {
  1022 + setCheckedFn(value, evt);
  1023 + }
  1024 + }
  1025 +
  1026 + applying = false;
  1027 + }
  1028 + };
  1029 +
  1030 + mxEvent.addListener(div, "click", function (evt) {
  1031 + if (cb.getAttribute("disabled") != "disabled") {
  1032 + // Toggles checkbox state for click on label
  1033 + var source = mxEvent.getSource(evt);
  1034 +
  1035 + if (source == div || source == span) {
  1036 + cb.checked = !cb.checked;
  1037 + }
  1038 +
  1039 + apply(cb.checked, evt);
  1040 + }
  1041 + });
  1042 +
  1043 + apply(value);
  1044 +
  1045 + if (listener != null) {
  1046 + listener.install(apply);
  1047 + this.listeners.push(listener);
  1048 + }
  1049 +
  1050 + if (fn != null) {
  1051 + fn(div);
  1052 + }
  1053 +
  1054 + return div;
  1055 +};
  1056 +
  1057 +/**
  1058 + * The string 'null' means use null in values.
  1059 + */
  1060 +BaseFormatPanel.prototype.createCellOption = function (
  1061 + label,
  1062 + key,
  1063 + defaultValue,
  1064 + enabledValue,
  1065 + disabledValue,
  1066 + fn,
  1067 + action,
  1068 + stopEditing,
  1069 + cells,
  1070 +) {
  1071 + var ui = this.editorUi;
  1072 + var editor = ui.editor;
  1073 + var graph = editor.graph;
  1074 +
  1075 + enabledValue =
  1076 + enabledValue != null ? (enabledValue == "null" ? null : enabledValue) : 1;
  1077 + disabledValue =
  1078 + disabledValue != null
  1079 + ? disabledValue == "null"
  1080 + ? null
  1081 + : disabledValue
  1082 + : 0;
  1083 +
  1084 + var style =
  1085 + cells != null ? graph.getCommonStyle(cells) : ui.getSelectionState().style;
  1086 +
  1087 + return this.createOption(
  1088 + label,
  1089 + function () {
  1090 + return mxUtils.getValue(style, key, defaultValue) != disabledValue;
  1091 + },
  1092 + function (checked) {
  1093 + if (stopEditing) {
  1094 + graph.stopEditing();
  1095 + }
  1096 +
  1097 + if (action != null) {
  1098 + action.funct();
  1099 + } else {
  1100 + graph.getModel().beginUpdate();
  1101 + try {
  1102 + var temp = cells != null ? cells : ui.getSelectionState().cells;
  1103 + var value = checked ? enabledValue : disabledValue;
  1104 + graph.setCellStyles(key, value, temp);
  1105 +
  1106 + if (fn != null) {
  1107 + fn(temp, value);
  1108 + }
  1109 +
  1110 + ui.fireEvent(
  1111 + new mxEventObject(
  1112 + "styleChanged",
  1113 + "keys",
  1114 + [key],
  1115 + "values",
  1116 + [value],
  1117 + "cells",
  1118 + temp,
  1119 + ),
  1120 + );
  1121 + } finally {
  1122 + graph.getModel().endUpdate();
  1123 + }
  1124 + }
  1125 + },
  1126 + {
  1127 + install: function (apply) {
  1128 + this.listener = function () {
  1129 + apply(mxUtils.getValue(style, key, defaultValue) != disabledValue);
  1130 + };
  1131 +
  1132 + graph.getModel().addListener(mxEvent.CHANGE, this.listener);
  1133 + },
  1134 + destroy: function () {
  1135 + graph.getModel().removeListener(this.listener);
  1136 + },
  1137 + },
  1138 + );
  1139 +};
  1140 +
  1141 +/**
  1142 + * Adds the given color option.
  1143 + */
  1144 +BaseFormatPanel.prototype.createColorOption = function (
  1145 + label,
  1146 + getColorFn,
  1147 + setColorFn,
  1148 + defaultColor,
  1149 + listener,
  1150 + callbackFn,
  1151 + hideCheckbox,
  1152 + defaultColorValue,
  1153 +) {
  1154 + var div = document.createElement("div");
  1155 + div.style.padding = "3px 0px 3px 0px";
  1156 + div.style.whiteSpace = "nowrap";
  1157 + div.style.overflow = "hidden";
  1158 + div.style.width = "200px";
  1159 + div.style.height = "18px";
  1160 +
  1161 + var cb = document.createElement("input");
  1162 + cb.setAttribute("type", "checkbox");
  1163 + cb.style.margin = "1px 6px 0px 0px";
  1164 + cb.style.verticalAlign = "top";
  1165 +
  1166 + if (!hideCheckbox) {
  1167 + div.appendChild(cb);
  1168 + }
  1169 +
  1170 + var span = document.createElement("span");
  1171 + span.style.verticalAlign = "top";
  1172 + mxUtils.write(span, label);
  1173 + div.appendChild(span);
  1174 +
  1175 + var title = "Shift+Click for Color Dropper";
  1176 + var value = getColorFn();
  1177 + var applying = false;
  1178 + var btn = null;
  1179 +
  1180 + var apply = function (color, disableUpdate, forceUpdate) {
  1181 + if (!applying) {
  1182 + var defaultValue = defaultColor == "null" ? null : defaultColor;
  1183 +
  1184 + applying = true;
  1185 + color = /(^#?[a-zA-Z0-9]*$)/.test(color) ? color : defaultValue;
  1186 + var tempColor =
  1187 + color != null && color != mxConstants.NONE ? color : defaultValue;
  1188 +
  1189 + var div = document.createElement("div");
  1190 + div.style.width = "36px";
  1191 + div.style.height = "12px";
  1192 + div.style.margin = "3px";
  1193 + div.style.border = "1px solid black";
  1194 + div.style.backgroundColor =
  1195 + tempColor == "default" ? defaultColorValue : tempColor;
  1196 + btn.innerHTML = "";
  1197 + btn.appendChild(div);
  1198 +
  1199 + if (
  1200 + color != null &&
  1201 + color != mxConstants.NONE &&
  1202 + color.length > 1 &&
  1203 + typeof color === "string"
  1204 + ) {
  1205 + var clr =
  1206 + color.charAt(0) == "#" ? color.substring(1).toUpperCase() : color;
  1207 + var name = ColorDialog.prototype.colorNames[clr];
  1208 + btn.setAttribute(
  1209 + "title",
  1210 + name != null ? name + " (" + title + ")" : title,
  1211 + );
  1212 + }
  1213 +
  1214 + if (color != null && color != mxConstants.NONE) {
  1215 + cb.setAttribute("checked", "checked");
  1216 + cb.defaultChecked = true;
  1217 + cb.checked = true;
  1218 + } else {
  1219 + cb.removeAttribute("checked");
  1220 + cb.defaultChecked = false;
  1221 + cb.checked = false;
  1222 + }
  1223 +
  1224 + btn.style.display = cb.checked || hideCheckbox ? "" : "none";
  1225 +
  1226 + if (callbackFn != null) {
  1227 + callbackFn(color == "null" ? null : color);
  1228 + }
  1229 +
  1230 + value = color;
  1231 +
  1232 + if (!disableUpdate) {
  1233 + // Checks if the color value needs to be updated in the model
  1234 + if (forceUpdate || hideCheckbox || getColorFn() != value) {
  1235 + setColorFn(value == "null" ? null : value, value);
  1236 + }
  1237 + }
  1238 +
  1239 + applying = false;
  1240 + }
  1241 + };
  1242 +
  1243 + var clrInput = document.createElement("input");
  1244 + clrInput.setAttribute("type", "color");
  1245 + clrInput.style.visibility = "hidden";
  1246 + clrInput.style.width = "0px";
  1247 + clrInput.style.height = "0px";
  1248 + clrInput.style.border = "none";
  1249 + div.appendChild(clrInput);
  1250 +
  1251 + btn = mxUtils.button(
  1252 + "",
  1253 + mxUtils.bind(this, function (evt) {
  1254 + var color = value;
  1255 +
  1256 + if (color == "default") {
  1257 + color = defaultColorValue;
  1258 + }
  1259 +
  1260 + if (mxEvent.isShiftDown(evt) && !mxClient.IS_IE && !mxClient.IS_IE11) {
  1261 + clrInput.value = color;
  1262 + clrInput.click();
  1263 +
  1264 + mxEvent.addListener(clrInput, "input", function () {
  1265 + apply(clrInput.value, null, true);
  1266 + });
  1267 + } else {
  1268 + this.editorUi.pickColor(
  1269 + color,
  1270 + function (newColor) {
  1271 + apply(newColor, null, true);
  1272 + },
  1273 + defaultColorValue,
  1274 + );
  1275 + }
  1276 +
  1277 + mxEvent.consume(evt);
  1278 + }),
  1279 + );
  1280 +
  1281 + btn.style.position = "absolute";
  1282 + btn.style.marginTop = "-3px";
  1283 + btn.style.left = "178px";
  1284 + btn.style.height = "22px";
  1285 + btn.className = "geColorBtn";
  1286 + btn.style.display = cb.checked || hideCheckbox ? "" : "none";
  1287 + div.appendChild(btn);
  1288 +
  1289 + var clr =
  1290 + value != null && typeof value === "string" && value.charAt(0) == "#"
  1291 + ? value.substring(1).toUpperCase()
  1292 + : value;
  1293 + var name = ColorDialog.prototype.colorNames[clr];
  1294 + btn.setAttribute("title", name != null ? name + " (" + title + ")" : title);
  1295 +
  1296 + mxEvent.addListener(div, "click", function (evt) {
  1297 + var source = mxEvent.getSource(evt);
  1298 +
  1299 + if (source == cb || source.nodeName != "INPUT") {
  1300 + // Toggles checkbox state for click on label
  1301 + if (source != cb) {
  1302 + cb.checked = !cb.checked;
  1303 + }
  1304 +
  1305 + // Overrides default value with current value to make it easier
  1306 + // to restore previous value if the checkbox is clicked twice
  1307 + if (
  1308 + !cb.checked &&
  1309 + value != null &&
  1310 + value != mxConstants.NONE &&
  1311 + defaultColor != mxConstants.NONE
  1312 + ) {
  1313 + defaultColor = value;
  1314 + }
  1315 +
  1316 + apply(cb.checked ? defaultColor : mxConstants.NONE);
  1317 + }
  1318 + });
  1319 +
  1320 + apply(value, true);
  1321 +
  1322 + if (listener != null) {
  1323 + listener.install(apply);
  1324 + this.listeners.push(listener);
  1325 + }
  1326 +
  1327 + return div;
  1328 +};
  1329 +
  1330 +/**
  1331 + *
  1332 + */
  1333 +BaseFormatPanel.prototype.createCellColorOption = function (
  1334 + label,
  1335 + colorKey,
  1336 + defaultColor,
  1337 + callbackFn,
  1338 + setStyleFn,
  1339 + defaultColorValue,
  1340 +) {
  1341 + var ui = this.editorUi;
  1342 + var editor = ui.editor;
  1343 + var graph = editor.graph;
  1344 +
  1345 + return this.createColorOption(
  1346 + label,
  1347 + function () {
  1348 + // Seems to be null sometimes, not sure why...
  1349 + var state = graph.view.getState(ui.getSelectionState().cells[0]);
  1350 +
  1351 + if (state != null) {
  1352 + return mxUtils.getValue(state.style, colorKey, null);
  1353 + }
  1354 +
  1355 + return null;
  1356 + },
  1357 + function (color, realValue) {
  1358 + graph.getModel().beginUpdate();
  1359 + try {
  1360 + var cells = ui.getSelectionState().cells;
  1361 + graph.setCellStyles(colorKey, color, cells);
  1362 +
  1363 + if (setStyleFn != null) {
  1364 + setStyleFn(color);
  1365 + }
  1366 +
  1367 + ui.fireEvent(
  1368 + new mxEventObject(
  1369 + "styleChanged",
  1370 + "keys",
  1371 + [colorKey],
  1372 + "values",
  1373 + [color],
  1374 + "cells",
  1375 + cells,
  1376 + ),
  1377 + );
  1378 + } finally {
  1379 + graph.getModel().endUpdate();
  1380 + }
  1381 + },
  1382 + defaultColor || mxConstants.NONE,
  1383 + {
  1384 + install: function (apply) {
  1385 + this.listener = function () {
  1386 + // Seems to be null sometimes, not sure why...
  1387 + var state = graph.view.getState(ui.getSelectionState().cells[0]);
  1388 +
  1389 + if (state != null) {
  1390 + apply(mxUtils.getValue(state.style, colorKey, null), true);
  1391 + }
  1392 + };
  1393 +
  1394 + graph.getModel().addListener(mxEvent.CHANGE, this.listener);
  1395 + },
  1396 + destroy: function () {
  1397 + graph.getModel().removeListener(this.listener);
  1398 + },
  1399 + },
  1400 + callbackFn,
  1401 + null,
  1402 + defaultColorValue,
  1403 + );
  1404 +};
  1405 +
  1406 +/**
  1407 + *
  1408 + */
  1409 +BaseFormatPanel.prototype.addArrow = function (elt, height) {
  1410 + height = height != null ? height : 10;
  1411 +
  1412 + var arrow = document.createElement("div");
  1413 + arrow.style.display = "inline-block";
  1414 + arrow.style.paddingRight = "4px";
  1415 + arrow.style.padding = "6px";
  1416 +
  1417 + var m = 10 - height;
  1418 +
  1419 + if (m == 2) {
  1420 + arrow.style.paddingTop = 6 + "px";
  1421 + } else if (m > 0) {
  1422 + arrow.style.paddingTop = 6 - m + "px";
  1423 + } else {
  1424 + arrow.style.marginTop = "-2px";
  1425 + }
  1426 +
  1427 + arrow.style.height = height + "px";
  1428 + arrow.style.borderLeft = "1px solid #a0a0a0";
  1429 +
  1430 + var img = document.createElement("img");
  1431 + img.setAttribute("border", "0");
  1432 + img.setAttribute("valign", "middle");
  1433 + img.setAttribute("src", Toolbar.prototype.dropDownImage);
  1434 + arrow.appendChild(img);
  1435 +
  1436 + var img = arrow.getElementsByTagName("img")[0];
  1437 + img.style.position = "relative";
  1438 + img.style.left = "1px";
  1439 + img.style.top = mxClient.IS_FF ? "0px" : "-4px";
  1440 + mxUtils.setOpacity(arrow, 70);
  1441 +
  1442 + var symbol = elt.getElementsByTagName("div")[0];
  1443 +
  1444 + if (symbol != null) {
  1445 + symbol.style.paddingRight = "6px";
  1446 + symbol.style.marginLeft = "4px";
  1447 + symbol.style.marginTop = "-1px";
  1448 + symbol.style.display = "inline-block";
  1449 + mxUtils.setOpacity(symbol, 60);
  1450 + }
  1451 +
  1452 + mxUtils.setOpacity(elt, 100);
  1453 + elt.style.border = "1px solid #a0a0a0";
  1454 + elt.style.backgroundColor = this.buttonBackgroundColor;
  1455 + elt.style.backgroundImage = "none";
  1456 + elt.style.width = "auto";
  1457 + elt.className += " geColorBtn";
  1458 + mxUtils.setPrefixedStyle(elt.style, "borderRadius", "3px");
  1459 +
  1460 + elt.appendChild(arrow);
  1461 +
  1462 + return symbol;
  1463 +};
  1464 +
  1465 +/**
  1466 + *
  1467 + */
  1468 +BaseFormatPanel.prototype.addUnitInput = function (
  1469 + container,
  1470 + unit,
  1471 + right,
  1472 + width,
  1473 + update,
  1474 + step,
  1475 + marginTop,
  1476 + disableFocus,
  1477 + isFloat,
  1478 +) {
  1479 + marginTop = marginTop != null ? marginTop : 0;
  1480 +
  1481 + var input = document.createElement("input");
  1482 + input.style.position = "absolute";
  1483 + input.style.textAlign = "right";
  1484 + input.style.marginTop = "-2px";
  1485 + input.style.left = 228 - right - width + "px";
  1486 + input.style.width = width + "px";
  1487 + input.style.height = "21px";
  1488 + input.style.border = "1px solid rgb(160, 160, 160)";
  1489 + input.style.borderRadius = "4px";
  1490 + input.style.boxSizing = "border-box";
  1491 +
  1492 + container.appendChild(input);
  1493 +
  1494 + var stepper = this.createStepper(
  1495 + input,
  1496 + update,
  1497 + step,
  1498 + null,
  1499 + disableFocus,
  1500 + null,
  1501 + isFloat,
  1502 + );
  1503 + stepper.style.marginTop = marginTop - 2 + "px";
  1504 + stepper.style.left = 228 - right + "px";
  1505 + container.appendChild(stepper);
  1506 +
  1507 + return input;
  1508 +};
  1509 +
  1510 +/**
  1511 + *
  1512 + */
  1513 +BaseFormatPanel.prototype.createRelativeOption = function (
  1514 + label,
  1515 + key,
  1516 + width,
  1517 + handler,
  1518 + init,
  1519 +) {
  1520 + width = width != null ? width : 52;
  1521 +
  1522 + var ui = this.editorUi;
  1523 + var graph = ui.editor.graph;
  1524 + var div = this.createPanel();
  1525 + div.style.paddingTop = "10px";
  1526 + div.style.paddingBottom = "10px";
  1527 + mxUtils.write(div, label);
  1528 + div.style.fontWeight = "bold";
  1529 +
  1530 + var update = mxUtils.bind(this, function (evt) {
  1531 + if (handler != null) {
  1532 + handler(input);
  1533 + } else {
  1534 + var value = parseInt(input.value);
  1535 + value = Math.min(100, Math.max(0, isNaN(value) ? 100 : value));
  1536 + var state = graph.view.getState(ui.getSelectionState().cells[0]);
  1537 +
  1538 + if (state != null && value != mxUtils.getValue(state.style, key, 100)) {
  1539 + // Removes entry in style (assumes 100 is default for relative values)
  1540 + if (value == 100) {
  1541 + value = null;
  1542 + }
  1543 +
  1544 + var cells = ui.getSelectionState().cells;
  1545 + graph.setCellStyles(key, value, cells);
  1546 + this.editorUi.fireEvent(
  1547 + new mxEventObject(
  1548 + "styleChanged",
  1549 + "keys",
  1550 + [key],
  1551 + "values",
  1552 + [value],
  1553 + "cells",
  1554 + cells,
  1555 + ),
  1556 + );
  1557 + }
  1558 +
  1559 + input.value = (value != null ? value : "100") + " %";
  1560 + }
  1561 +
  1562 + mxEvent.consume(evt);
  1563 + });
  1564 +
  1565 + var input = this.addUnitInput(
  1566 + div,
  1567 + "%",
  1568 + 16,
  1569 + width,
  1570 + update,
  1571 + 10,
  1572 + -15,
  1573 + handler != null,
  1574 + );
  1575 +
  1576 + if (key != null) {
  1577 + var listener = mxUtils.bind(this, function (sender, evt, force) {
  1578 + if (force || input != document.activeElement) {
  1579 + var ss = ui.getSelectionState();
  1580 + var tmp = parseInt(mxUtils.getValue(ss.style, key, 100));
  1581 + input.value = isNaN(tmp) ? "" : tmp + " %";
  1582 + }
  1583 + });
  1584 +
  1585 + mxEvent.addListener(input, "keydown", function (e) {
  1586 + if (e.keyCode == 13) {
  1587 + graph.container.focus();
  1588 + mxEvent.consume(e);
  1589 + } else if (e.keyCode == 27) {
  1590 + listener(null, null, true);
  1591 + graph.container.focus();
  1592 + mxEvent.consume(e);
  1593 + }
  1594 + });
  1595 +
  1596 + graph.getModel().addListener(mxEvent.CHANGE, listener);
  1597 + this.listeners.push({
  1598 + destroy: function () {
  1599 + graph.getModel().removeListener(listener);
  1600 + },
  1601 + });
  1602 + listener();
  1603 + }
  1604 +
  1605 + mxEvent.addListener(input, "blur", update);
  1606 + mxEvent.addListener(input, "change", update);
  1607 +
  1608 + if (init != null) {
  1609 + init(input);
  1610 + }
  1611 +
  1612 + return div;
  1613 +};
  1614 +
  1615 +/**
  1616 + *
  1617 + */
  1618 +BaseFormatPanel.prototype.addLabel = function (div, title, right, width) {
  1619 + width = width != null ? width : 61;
  1620 +
  1621 + var label = document.createElement("div");
  1622 + mxUtils.write(label, title);
  1623 + label.style.position = "absolute";
  1624 + label.style.left = 240 - right - width + "px";
  1625 + label.style.width = width + "px";
  1626 + label.style.marginTop = "6px";
  1627 + label.style.textAlign = "center";
  1628 + div.appendChild(label);
  1629 +};
  1630 +
  1631 +/**
  1632 + *
  1633 + */
  1634 +BaseFormatPanel.prototype.addKeyHandler = function (input, listener) {
  1635 + mxEvent.addListener(
  1636 + input,
  1637 + "keydown",
  1638 + mxUtils.bind(this, function (e) {
  1639 + if (e.keyCode == 13) {
  1640 + this.editorUi.editor.graph.container.focus();
  1641 + mxEvent.consume(e);
  1642 + } else if (e.keyCode == 27) {
  1643 + if (listener != null) {
  1644 + listener(null, null, true);
  1645 + }
  1646 +
  1647 + this.editorUi.editor.graph.container.focus();
  1648 + mxEvent.consume(e);
  1649 + }
  1650 + }),
  1651 + );
  1652 +};
  1653 +
  1654 +/**
  1655 + *
  1656 + */
  1657 +BaseFormatPanel.prototype.styleButtons = function (elts) {
  1658 + for (var i = 0; i < elts.length; i++) {
  1659 + mxUtils.setPrefixedStyle(elts[i].style, "borderRadius", "3px");
  1660 + mxUtils.setOpacity(elts[i], 100);
  1661 + elts[i].style.border = "1px solid #a0a0a0";
  1662 + elts[i].style.padding = "4px";
  1663 + elts[i].style.paddingTop = "3px";
  1664 + elts[i].style.paddingRight = "1px";
  1665 + elts[i].style.margin = "1px";
  1666 + elts[i].style.marginRight = "2px";
  1667 + elts[i].style.width = "24px";
  1668 + elts[i].style.height = "20px";
  1669 + elts[i].className += " geColorBtn";
  1670 + }
  1671 +};
  1672 +
  1673 +/**
  1674 + * Adds the label menu items to the given menu and parent.
  1675 + */
  1676 +BaseFormatPanel.prototype.destroy = function () {
  1677 + if (this.listeners != null) {
  1678 + for (var i = 0; i < this.listeners.length; i++) {
  1679 + this.listeners[i].destroy();
  1680 + }
  1681 +
  1682 + this.listeners = null;
  1683 + }
  1684 +};
  1685 +
  1686 +/**
  1687 + * Adds the label menu items to the given menu and parent.
  1688 + */
  1689 +ArrangePanel = function (format, editorUi, container) {
  1690 + BaseFormatPanel.call(this, format, editorUi, container);
  1691 + this.init();
  1692 +};
  1693 +
  1694 +mxUtils.extend(ArrangePanel, BaseFormatPanel);
  1695 +
  1696 +/**
  1697 + * Adds the label menu items to the given menu and parent.
  1698 + */
  1699 +ArrangePanel.prototype.init = function () {
  1700 + var ss = this.editorUi.getSelectionState();
  1701 +
  1702 + if (ss.cells.length > 0) {
  1703 + this.container.appendChild(this.addLayerOps(this.createPanel()));
  1704 +
  1705 + // Special case that adds two panels
  1706 + this.addGeometry(this.container);
  1707 + this.addEdgeGeometry(this.container);
  1708 +
  1709 + if (!ss.containsLabel || ss.edges.length == 0) {
  1710 + this.container.appendChild(this.addAngle(this.createPanel()));
  1711 + }
  1712 +
  1713 + if (!ss.containsLabel) {
  1714 + this.container.appendChild(this.addFlip(this.createPanel()));
  1715 + }
  1716 +
  1717 + this.container.appendChild(this.addAlign(this.createPanel()));
  1718 +
  1719 + if (ss.vertices.length > 1 && !ss.cell && !ss.row) {
  1720 + this.container.appendChild(this.addDistribute(this.createPanel()));
  1721 + }
  1722 +
  1723 + this.container.appendChild(this.addTable(this.createPanel()));
  1724 + this.container.appendChild(this.addGroupOps(this.createPanel()));
  1725 + }
  1726 +
  1727 + if (ss.containsLabel) {
  1728 + // Adds functions from hidden style format panel
  1729 + var span = document.createElement("div");
  1730 + span.style.width = "100%";
  1731 + span.style.marginTop = "0px";
  1732 + span.style.fontWeight = "bold";
  1733 + span.style.padding = "10px 0 0 14px";
  1734 + mxUtils.write(span, mxResources.get("style"));
  1735 + this.container.appendChild(span);
  1736 +
  1737 + new StyleFormatPanel(this.format, this.editorUi, this.container);
  1738 + }
  1739 +};
  1740 +
  1741 +/**
  1742 + *
  1743 + */
  1744 +ArrangePanel.prototype.addTable = function (div) {
  1745 + var ui = this.editorUi;
  1746 + var editor = ui.editor;
  1747 + var graph = editor.graph;
  1748 + var ss = ui.getSelectionState();
  1749 + div.style.paddingTop = "6px";
  1750 + div.style.paddingBottom = "10px";
  1751 +
  1752 + var span = document.createElement("div");
  1753 + span.style.marginTop = "0px";
  1754 + span.style.marginBottom = "6px";
  1755 + span.style.fontWeight = "bold";
  1756 + mxUtils.write(span, mxResources.get("table"));
  1757 + div.appendChild(span);
  1758 +
  1759 + var panel = document.createElement("div");
  1760 + panel.style.position = "relative";
  1761 + panel.style.paddingLeft = "0px";
  1762 + panel.style.borderWidth = "0px";
  1763 + panel.style.width = "220px";
  1764 + panel.className = "geToolbarContainer";
  1765 +
  1766 + var cell = ss.vertices[0];
  1767 +
  1768 + if (graph.getSelectionCount() > 1) {
  1769 + if (graph.isTableCell(cell)) {
  1770 + cell = graph.model.getParent(cell);
  1771 + }
  1772 +
  1773 + if (graph.isTableRow(cell)) {
  1774 + cell = graph.model.getParent(cell);
  1775 + }
  1776 + }
  1777 +
  1778 + var isTable = ss.table || ss.row || ss.cell;
  1779 + var isStack = graph.isStack(cell) || graph.isStackChild(cell);
  1780 +
  1781 + var showCols = isTable;
  1782 + var showRows = isTable;
  1783 +
  1784 + if (isStack) {
  1785 + var style = graph.isStack(cell)
  1786 + ? ss.style
  1787 + : graph.getCellStyle(graph.model.getParent(cell));
  1788 +
  1789 + showRows = style["horizontalStack"] == "0";
  1790 + showCols = !showRows;
  1791 + }
  1792 +
  1793 + var btns = [];
  1794 +
  1795 + if (showCols) {
  1796 + btns = btns.concat([
  1797 + ui.toolbar.addButton(
  1798 + "geSprite-insertcolumnbefore",
  1799 + mxResources.get("insertColumnBefore"),
  1800 + mxUtils.bind(this, function () {
  1801 + try {
  1802 + if (isStack) {
  1803 + graph.insertLane(cell, true);
  1804 + } else {
  1805 + graph.insertTableColumn(cell, true);
  1806 + }
  1807 + } catch (e) {
  1808 + ui.handleError(e);
  1809 + }
  1810 + }),
  1811 + panel,
  1812 + ),
  1813 + ui.toolbar.addButton(
  1814 + "geSprite-insertcolumnafter",
  1815 + mxResources.get("insertColumnAfter"),
  1816 + mxUtils.bind(this, function () {
  1817 + try {
  1818 + if (isStack) {
  1819 + graph.insertLane(cell, false);
  1820 + } else {
  1821 + graph.insertTableColumn(cell, false);
  1822 + }
  1823 + } catch (e) {
  1824 + ui.handleError(e);
  1825 + }
  1826 + }),
  1827 + panel,
  1828 + ),
  1829 + ui.toolbar.addButton(
  1830 + "geSprite-deletecolumn",
  1831 + mxResources.get("deleteColumn"),
  1832 + mxUtils.bind(this, function () {
  1833 + try {
  1834 + if (isStack) {
  1835 + graph.deleteLane(cell);
  1836 + } else {
  1837 + graph.deleteTableColumn(cell);
  1838 + }
  1839 + } catch (e) {
  1840 + ui.handleError(e);
  1841 + }
  1842 + }),
  1843 + panel,
  1844 + ),
  1845 + ]);
  1846 + }
  1847 +
  1848 + if (showRows) {
  1849 + btns = btns.concat([
  1850 + ui.toolbar.addButton(
  1851 + "geSprite-insertrowbefore",
  1852 + mxResources.get("insertRowBefore"),
  1853 + mxUtils.bind(this, function () {
  1854 + try {
  1855 + if (isStack) {
  1856 + graph.insertLane(cell, true);
  1857 + } else {
  1858 + graph.insertTableRow(cell, true);
  1859 + }
  1860 + } catch (e) {
  1861 + ui.handleError(e);
  1862 + }
  1863 + }),
  1864 + panel,
  1865 + ),
  1866 + ui.toolbar.addButton(
  1867 + "geSprite-insertrowafter",
  1868 + mxResources.get("insertRowAfter"),
  1869 + mxUtils.bind(this, function () {
  1870 + try {
  1871 + if (isStack) {
  1872 + graph.insertLane(cell, false);
  1873 + } else {
  1874 + graph.insertTableRow(cell, false);
  1875 + }
  1876 + } catch (e) {
  1877 + ui.handleError(e);
  1878 + }
  1879 + }),
  1880 + panel,
  1881 + ),
  1882 + ui.toolbar.addButton(
  1883 + "geSprite-deleterow",
  1884 + mxResources.get("deleteRow"),
  1885 + mxUtils.bind(this, function () {
  1886 + try {
  1887 + if (isStack) {
  1888 + graph.deleteLane(cell);
  1889 + } else {
  1890 + graph.deleteTableRow(cell);
  1891 + }
  1892 + } catch (e) {
  1893 + ui.handleError(e);
  1894 + }
  1895 + }),
  1896 + panel,
  1897 + ),
  1898 + ]);
  1899 + }
  1900 +
  1901 + if (btns.length > 0) {
  1902 + this.styleButtons(btns);
  1903 + div.appendChild(panel);
  1904 +
  1905 + if (btns.length > 3) {
  1906 + btns[2].style.marginRight = "10px";
  1907 + }
  1908 +
  1909 + var count = 0;
  1910 +
  1911 + if (ss.mergeCell != null) {
  1912 + count += this.addActions(div, ["mergeCells"]);
  1913 + } else if (ss.style["colspan"] > 1 || ss.style["rowspan"] > 1) {
  1914 + count += this.addActions(div, ["unmergeCells"]);
  1915 + }
  1916 +
  1917 + if (count > 0) {
  1918 + panel.style.paddingBottom = "2px";
  1919 + }
  1920 + } else {
  1921 + div.style.display = "none";
  1922 + }
  1923 +
  1924 + return div;
  1925 +};
  1926 +
  1927 +/**
  1928 + *
  1929 + */
  1930 +ArrangePanel.prototype.addLayerOps = function (div) {
  1931 + this.addActions(div, ["toFront", "toBack"]);
  1932 + this.addActions(div, ["bringForward", "sendBackward"]);
  1933 +
  1934 + return div;
  1935 +};
  1936 +
  1937 +/**
  1938 + *
  1939 + */
  1940 +ArrangePanel.prototype.addGroupOps = function (div) {
  1941 + var ui = this.editorUi;
  1942 + var graph = ui.editor.graph;
  1943 + var ss = ui.getSelectionState();
  1944 +
  1945 + div.style.paddingTop = "8px";
  1946 + div.style.paddingBottom = "6px";
  1947 +
  1948 + var count = 0;
  1949 +
  1950 + if (!ss.cell && !ss.row) {
  1951 + count +=
  1952 + this.addActions(div, ["group", "ungroup", "copySize", "pasteSize"]) +
  1953 + this.addActions(div, ["removeFromGroup"]);
  1954 + }
  1955 +
  1956 + var copyBtn = null;
  1957 +
  1958 + if (
  1959 + ss.cells.length == 1 &&
  1960 + ss.cells[0].value != null &&
  1961 + !isNaN(ss.cells[0].value.nodeType)
  1962 + ) {
  1963 + copyBtn = mxUtils.button(mxResources.get("copyData"), function (evt) {
  1964 + if (mxEvent.isShiftDown(evt)) {
  1965 + var result = graph.getDataForCells(graph.getSelectionCells());
  1966 +
  1967 + var dlg = new EmbedDialog(
  1968 + ui,
  1969 + JSON.stringify(result, null, 2),
  1970 + null,
  1971 + null,
  1972 + function () {
  1973 + console.log(result);
  1974 + ui.alert("Written to Console (Dev Tools)");
  1975 + },
  1976 + mxResources.get("copyData"),
  1977 + null,
  1978 + "Console",
  1979 + "data.json",
  1980 + );
  1981 + ui.showDialog(dlg.container, 450, 240, true, true);
  1982 + dlg.init();
  1983 + } else {
  1984 + ui.actions.get("copyData").funct(evt);
  1985 + }
  1986 + });
  1987 +
  1988 + copyBtn.setAttribute(
  1989 + "title",
  1990 + mxResources.get("copyData") +
  1991 + " (" +
  1992 + this.editorUi.actions.get("copyData").shortcut +
  1993 + ")" +
  1994 + " Shift+Click to Extract Data",
  1995 + );
  1996 + copyBtn.style.marginBottom = "2px";
  1997 + copyBtn.style.width = "210px";
  1998 + div.appendChild(copyBtn);
  1999 + count++;
  2000 + }
  2001 +
  2002 + var pasteBtn = null;
  2003 +
  2004 + if (ui.copiedValue != null && ss.cells.length > 0) {
  2005 + pasteBtn = mxUtils.button(mxResources.get("pasteData"), function (evt) {
  2006 + ui.actions.get("pasteData").funct(evt);
  2007 + });
  2008 +
  2009 + pasteBtn.setAttribute(
  2010 + "title",
  2011 + mxResources.get("pasteData") +
  2012 + " (" +
  2013 + this.editorUi.actions.get("pasteData").shortcut +
  2014 + ")",
  2015 + );
  2016 + pasteBtn.style.marginBottom = "2px";
  2017 + pasteBtn.style.width = "210px";
  2018 + div.appendChild(pasteBtn);
  2019 + count++;
  2020 +
  2021 + if (copyBtn != null) {
  2022 + copyBtn.style.width = "104px";
  2023 + pasteBtn.style.width = "104px";
  2024 + pasteBtn.style.marginBottom = "2px";
  2025 + copyBtn.style.marginBottom = "2px";
  2026 + copyBtn.style.marginRight = "2px";
  2027 + }
  2028 + }
  2029 +
  2030 + if (copyBtn != null || pasteBtn != null) {
  2031 + mxUtils.br(div);
  2032 + }
  2033 +
  2034 + var clearWaypoints = this.addAction(div, "clearWaypoints");
  2035 +
  2036 + if (clearWaypoints != null) {
  2037 + mxUtils.br(div);
  2038 + clearWaypoints.setAttribute(
  2039 + "title",
  2040 + mxResources.get("clearWaypoints") +
  2041 + " (" +
  2042 + this.editorUi.actions.get("clearWaypoints").shortcut +
  2043 + ")" +
  2044 + " Shift+Click to Clear Anchor Points",
  2045 + );
  2046 + count++;
  2047 + }
  2048 +
  2049 + if (graph.getSelectionCount() == 1) {
  2050 + count += this.addActions(div, ["editData", "editLink"]);
  2051 + }
  2052 +
  2053 + if (count == 0) {
  2054 + div.style.display = "none";
  2055 + }
  2056 +
  2057 + return div;
  2058 +};
  2059 +
  2060 +/**
  2061 + *
  2062 + */
  2063 +ArrangePanel.prototype.addAlign = function (div) {
  2064 + var ss = this.editorUi.getSelectionState();
  2065 + var graph = this.editorUi.editor.graph;
  2066 + div.style.paddingTop = "6px";
  2067 + div.style.paddingBottom = "8px";
  2068 + div.appendChild(this.createTitle(mxResources.get("align")));
  2069 +
  2070 + var stylePanel = document.createElement("div");
  2071 + stylePanel.style.position = "relative";
  2072 + stylePanel.style.whiteSpace = "nowrap";
  2073 + stylePanel.style.paddingLeft = "0px";
  2074 + stylePanel.style.paddingBottom = "2px";
  2075 + stylePanel.style.borderWidth = "0px";
  2076 + stylePanel.style.width = "220px";
  2077 + stylePanel.className = "geToolbarContainer";
  2078 +
  2079 + if (ss.vertices.length > 1) {
  2080 + var left = this.editorUi.toolbar.addButton(
  2081 + "geSprite-alignleft",
  2082 + mxResources.get("left"),
  2083 + function () {
  2084 + graph.alignCells(mxConstants.ALIGN_LEFT);
  2085 + },
  2086 + stylePanel,
  2087 + );
  2088 + var center = this.editorUi.toolbar.addButton(
  2089 + "geSprite-aligncenter",
  2090 + mxResources.get("center"),
  2091 + function () {
  2092 + graph.alignCells(mxConstants.ALIGN_CENTER);
  2093 + },
  2094 + stylePanel,
  2095 + );
  2096 + var right = this.editorUi.toolbar.addButton(
  2097 + "geSprite-alignright",
  2098 + mxResources.get("right"),
  2099 + function () {
  2100 + graph.alignCells(mxConstants.ALIGN_RIGHT);
  2101 + },
  2102 + stylePanel,
  2103 + );
  2104 +
  2105 + var top = this.editorUi.toolbar.addButton(
  2106 + "geSprite-aligntop",
  2107 + mxResources.get("top"),
  2108 + function () {
  2109 + graph.alignCells(mxConstants.ALIGN_TOP);
  2110 + },
  2111 + stylePanel,
  2112 + );
  2113 + var middle = this.editorUi.toolbar.addButton(
  2114 + "geSprite-alignmiddle",
  2115 + mxResources.get("middle"),
  2116 + function () {
  2117 + graph.alignCells(mxConstants.ALIGN_MIDDLE);
  2118 + },
  2119 + stylePanel,
  2120 + );
  2121 + var bottom = this.editorUi.toolbar.addButton(
  2122 + "geSprite-alignbottom",
  2123 + mxResources.get("bottom"),
  2124 + function () {
  2125 + graph.alignCells(mxConstants.ALIGN_BOTTOM);
  2126 + },
  2127 + stylePanel,
  2128 + );
  2129 +
  2130 + this.styleButtons([left, center, right, top, middle, bottom]);
  2131 + right.style.marginRight = "10px";
  2132 + }
  2133 +
  2134 + div.appendChild(stylePanel);
  2135 + this.addActions(div, ["snapToGrid"]);
  2136 +
  2137 + return div;
  2138 +};
  2139 +
  2140 +/**
  2141 + *
  2142 + */
  2143 +ArrangePanel.prototype.addFlip = function (div) {
  2144 + var ui = this.editorUi;
  2145 + var editor = ui.editor;
  2146 + var graph = editor.graph;
  2147 + div.style.paddingTop = "6px";
  2148 + div.style.paddingBottom = "10px";
  2149 + var ss = this.editorUi.getSelectionState();
  2150 +
  2151 + var span = document.createElement("div");
  2152 + span.style.marginTop = "2px";
  2153 + span.style.marginBottom = "8px";
  2154 + span.style.fontWeight = "bold";
  2155 + mxUtils.write(span, mxResources.get("flip"));
  2156 + div.appendChild(span);
  2157 +
  2158 + var btn = mxUtils.button(mxResources.get("horizontal"), function (evt) {
  2159 + graph.flipCells(ss.cells, true);
  2160 + });
  2161 +
  2162 + btn.setAttribute("title", mxResources.get("horizontal"));
  2163 + btn.style.width = "104px";
  2164 + btn.style.marginRight = "2px";
  2165 + div.appendChild(btn);
  2166 +
  2167 + var btn = mxUtils.button(mxResources.get("vertical"), function (evt) {
  2168 + graph.flipCells(ss.cells, false);
  2169 + });
  2170 +
  2171 + btn.setAttribute("title", mxResources.get("vertical"));
  2172 + btn.style.width = "104px";
  2173 + div.appendChild(btn);
  2174 +
  2175 + return div;
  2176 +};
  2177 +
  2178 +/**
  2179 + *
  2180 + */
  2181 +ArrangePanel.prototype.addDistribute = function (div) {
  2182 + var ui = this.editorUi;
  2183 + var editor = ui.editor;
  2184 + var graph = editor.graph;
  2185 + div.style.paddingTop = "6px";
  2186 + div.style.paddingBottom = "12px";
  2187 +
  2188 + div.appendChild(this.createTitle(mxResources.get("distribute")));
  2189 +
  2190 + var btn = mxUtils.button(mxResources.get("horizontal"), function (evt) {
  2191 + graph.distributeCells(true);
  2192 + });
  2193 +
  2194 + btn.setAttribute("title", mxResources.get("horizontal"));
  2195 + btn.style.width = "104px";
  2196 + btn.style.marginRight = "2px";
  2197 + div.appendChild(btn);
  2198 +
  2199 + var btn = mxUtils.button(mxResources.get("vertical"), function (evt) {
  2200 + graph.distributeCells(false);
  2201 + });
  2202 +
  2203 + btn.setAttribute("title", mxResources.get("vertical"));
  2204 + btn.style.width = "104px";
  2205 + div.appendChild(btn);
  2206 +
  2207 + return div;
  2208 +};
  2209 +
  2210 +/**
  2211 + *
  2212 + */
  2213 +ArrangePanel.prototype.addAngle = function (div) {
  2214 + var ui = this.editorUi;
  2215 + var editor = ui.editor;
  2216 + var graph = editor.graph;
  2217 + var ss = ui.getSelectionState();
  2218 +
  2219 + div.style.paddingBottom = "8px";
  2220 +
  2221 + var span = document.createElement("div");
  2222 + span.style.position = "absolute";
  2223 + span.style.width = "70px";
  2224 + span.style.marginTop = "0px";
  2225 + span.style.fontWeight = "bold";
  2226 +
  2227 + var input = null;
  2228 + var update = null;
  2229 + var btn = null;
  2230 +
  2231 + if (ss.rotatable && !ss.table && !ss.row && !ss.cell) {
  2232 + mxUtils.write(span, mxResources.get("angle"));
  2233 + div.appendChild(span);
  2234 +
  2235 + input = this.addUnitInput(div, "°", 16, 52, function () {
  2236 + update.apply(this, arguments);
  2237 + });
  2238 +
  2239 + mxUtils.br(div);
  2240 + div.style.paddingTop = "10px";
  2241 + } else {
  2242 + div.style.paddingTop = "8px";
  2243 + }
  2244 +
  2245 + if (!ss.containsLabel) {
  2246 + var label = mxResources.get("reverse");
  2247 +
  2248 + if (ss.vertices.length > 0 && ss.edges.length > 0) {
  2249 + label = mxResources.get("turn") + " / " + label;
  2250 + } else if (ss.vertices.length > 0) {
  2251 + label = mxResources.get("turn");
  2252 + }
  2253 +
  2254 + btn = mxUtils.button(label, function (evt) {
  2255 + ui.actions.get("turn").funct(evt);
  2256 + });
  2257 +
  2258 + btn.setAttribute(
  2259 + "title",
  2260 + label + " (" + this.editorUi.actions.get("turn").shortcut + ")",
  2261 + );
  2262 + btn.style.width = "210px";
  2263 + div.appendChild(btn);
  2264 +
  2265 + if (input != null) {
  2266 + btn.style.marginTop = "8px";
  2267 + }
  2268 + }
  2269 +
  2270 + if (input != null) {
  2271 + var listener = mxUtils.bind(this, function (sender, evt, force) {
  2272 + if (force || document.activeElement != input) {
  2273 + ss = ui.getSelectionState();
  2274 + var tmp = parseFloat(
  2275 + mxUtils.getValue(ss.style, mxConstants.STYLE_ROTATION, 0),
  2276 + );
  2277 + input.value = isNaN(tmp) ? "" : tmp + "°";
  2278 + }
  2279 + });
  2280 +
  2281 + update = this.installInputHandler(
  2282 + input,
  2283 + mxConstants.STYLE_ROTATION,
  2284 + 0,
  2285 + 0,
  2286 + 360,
  2287 + "°",
  2288 + null,
  2289 + true,
  2290 + );
  2291 + this.addKeyHandler(input, listener);
  2292 +
  2293 + graph.getModel().addListener(mxEvent.CHANGE, listener);
  2294 + this.listeners.push({
  2295 + destroy: function () {
  2296 + graph.getModel().removeListener(listener);
  2297 + },
  2298 + });
  2299 + listener();
  2300 + }
  2301 +
  2302 + return div;
  2303 +};
  2304 +
  2305 +BaseFormatPanel.prototype.getUnit = function () {
  2306 + var unit = this.editorUi.editor.graph.view.unit;
  2307 +
  2308 + switch (unit) {
  2309 + case mxConstants.POINTS:
  2310 + return "pt";
  2311 + case mxConstants.INCHES:
  2312 + return '"';
  2313 + case mxConstants.MILLIMETERS:
  2314 + return "mm";
  2315 + case mxConstants.METERS:
  2316 + return "m";
  2317 + }
  2318 +};
  2319 +
  2320 +BaseFormatPanel.prototype.inUnit = function (pixels) {
  2321 + return this.editorUi.editor.graph.view.formatUnitText(pixels);
  2322 +};
  2323 +
  2324 +BaseFormatPanel.prototype.fromUnit = function (value) {
  2325 + var unit = this.editorUi.editor.graph.view.unit;
  2326 +
  2327 + switch (unit) {
  2328 + case mxConstants.POINTS:
  2329 + return value;
  2330 + case mxConstants.INCHES:
  2331 + return value * mxConstants.PIXELS_PER_INCH;
  2332 + case mxConstants.MILLIMETERS:
  2333 + return value * mxConstants.PIXELS_PER_MM;
  2334 + case mxConstants.METERS:
  2335 + return value * mxConstants.PIXELS_PER_MM * 1000;
  2336 + }
  2337 +};
  2338 +
  2339 +BaseFormatPanel.prototype.isFloatUnit = function () {
  2340 + return this.editorUi.editor.graph.view.unit != mxConstants.POINTS;
  2341 +};
  2342 +
  2343 +BaseFormatPanel.prototype.getUnitStep = function () {
  2344 + var unit = this.editorUi.editor.graph.view.unit;
  2345 +
  2346 + switch (unit) {
  2347 + case mxConstants.POINTS:
  2348 + return 1;
  2349 + case mxConstants.INCHES:
  2350 + return 0.1;
  2351 + case mxConstants.MILLIMETERS:
  2352 + return 0.5;
  2353 + case mxConstants.METERS:
  2354 + return 0.001;
  2355 + }
  2356 +};
  2357 +
  2358 +/**
  2359 + *
  2360 + */
  2361 +ArrangePanel.prototype.addGeometry = function (container) {
  2362 + var panel = this;
  2363 + var ui = this.editorUi;
  2364 + var graph = ui.editor.graph;
  2365 + var model = graph.getModel();
  2366 + var rect = ui.getSelectionState();
  2367 +
  2368 + var div = this.createPanel();
  2369 + div.style.paddingBottom = "8px";
  2370 +
  2371 + var span = document.createElement("div");
  2372 + span.style.position = "absolute";
  2373 + span.style.width = "50px";
  2374 + span.style.marginTop = "0px";
  2375 + span.style.fontWeight = "bold";
  2376 + mxUtils.write(span, mxResources.get("size"));
  2377 + div.appendChild(span);
  2378 +
  2379 + var widthUpdate, heightUpdate, leftUpdate, topUpdate;
  2380 + var width = this.addUnitInput(
  2381 + div,
  2382 + this.getUnit(),
  2383 + 87,
  2384 + 52,
  2385 + function () {
  2386 + widthUpdate.apply(this, arguments);
  2387 + },
  2388 + this.getUnitStep(),
  2389 + null,
  2390 + null,
  2391 + this.isFloatUnit(),
  2392 + );
  2393 + var height = this.addUnitInput(
  2394 + div,
  2395 + this.getUnit(),
  2396 + 16,
  2397 + 52,
  2398 + function () {
  2399 + heightUpdate.apply(this, arguments);
  2400 + },
  2401 + this.getUnitStep(),
  2402 + null,
  2403 + null,
  2404 + this.isFloatUnit(),
  2405 + );
  2406 +
  2407 + var autosizeBtn = document.createElement("div");
  2408 + autosizeBtn.className = "geSprite geSprite-fit";
  2409 + autosizeBtn.setAttribute(
  2410 + "title",
  2411 + mxResources.get("autosize") +
  2412 + " (" +
  2413 + this.editorUi.actions.get("autosize").shortcut +
  2414 + ")",
  2415 + );
  2416 + autosizeBtn.style.position = "relative";
  2417 + autosizeBtn.style.cursor = "pointer";
  2418 + autosizeBtn.style.marginTop = "-3px";
  2419 + autosizeBtn.style.border = "0px";
  2420 + autosizeBtn.style.left = "42px";
  2421 + mxUtils.setOpacity(autosizeBtn, 50);
  2422 +
  2423 + mxEvent.addListener(autosizeBtn, "mouseenter", function () {
  2424 + mxUtils.setOpacity(autosizeBtn, 100);
  2425 + });
  2426 +
  2427 + mxEvent.addListener(autosizeBtn, "mouseleave", function () {
  2428 + mxUtils.setOpacity(autosizeBtn, 50);
  2429 + });
  2430 +
  2431 + mxEvent.addListener(autosizeBtn, "click", function () {
  2432 + ui.actions.get("autosize").funct();
  2433 + });
  2434 +
  2435 + div.appendChild(autosizeBtn);
  2436 +
  2437 + if (rect.row) {
  2438 + width.style.visibility = "hidden";
  2439 + width.nextSibling.style.visibility = "hidden";
  2440 + } else {
  2441 + this.addLabel(div, mxResources.get("width"), 87);
  2442 + }
  2443 +
  2444 + this.addLabel(div, mxResources.get("height"), 16);
  2445 + mxUtils.br(div);
  2446 +
  2447 + var wrapper = document.createElement("div");
  2448 + wrapper.style.paddingTop = "8px";
  2449 + wrapper.style.paddingRight = "20px";
  2450 + wrapper.style.whiteSpace = "nowrap";
  2451 + wrapper.style.textAlign = "right";
  2452 + var opt = this.createCellOption(
  2453 + mxResources.get("constrainProportions"),
  2454 + mxConstants.STYLE_ASPECT,
  2455 + null,
  2456 + "fixed",
  2457 + "null",
  2458 + );
  2459 + opt.style.width = "210px";
  2460 + wrapper.appendChild(opt);
  2461 +
  2462 + if (!rect.cell && !rect.row) {
  2463 + div.appendChild(wrapper);
  2464 + } else {
  2465 + autosizeBtn.style.visibility = "hidden";
  2466 + }
  2467 +
  2468 + var constrainCheckbox = opt.getElementsByTagName("input")[0];
  2469 + this.addKeyHandler(width, listener);
  2470 + this.addKeyHandler(height, listener);
  2471 +
  2472 + widthUpdate = this.addGeometryHandler(width, function (geo, value, cell) {
  2473 + if (graph.isTableCell(cell)) {
  2474 + graph.setTableColumnWidth(cell, value - geo.width, true);
  2475 +
  2476 + // Blocks processing in caller
  2477 + return true;
  2478 + } else if (geo.width > 0) {
  2479 + var value = Math.max(1, panel.fromUnit(value));
  2480 +
  2481 + if (constrainCheckbox.checked) {
  2482 + geo.height = Math.round((geo.height * value * 100) / geo.width) / 100;
  2483 + }
  2484 +
  2485 + geo.width = value;
  2486 + }
  2487 + });
  2488 + heightUpdate = this.addGeometryHandler(height, function (geo, value, cell) {
  2489 + if (graph.isTableCell(cell)) {
  2490 + cell = graph.model.getParent(cell);
  2491 + }
  2492 +
  2493 + if (graph.isTableRow(cell)) {
  2494 + graph.setTableRowHeight(cell, value - geo.height);
  2495 +
  2496 + // Blocks processing in caller
  2497 + return true;
  2498 + } else if (geo.height > 0) {
  2499 + var value = Math.max(1, panel.fromUnit(value));
  2500 +
  2501 + if (constrainCheckbox.checked) {
  2502 + geo.width = Math.round((geo.width * value * 100) / geo.height) / 100;
  2503 + }
  2504 +
  2505 + geo.height = value;
  2506 + }
  2507 + });
  2508 +
  2509 + if (rect.resizable || rect.row || rect.cell) {
  2510 + container.appendChild(div);
  2511 + }
  2512 +
  2513 + var div2 = this.createPanel();
  2514 + div2.style.paddingBottom = "30px";
  2515 +
  2516 + var span = document.createElement("div");
  2517 + span.style.position = "absolute";
  2518 + span.style.width = "70px";
  2519 + span.style.marginTop = "0px";
  2520 + span.style.fontWeight = "bold";
  2521 + mxUtils.write(span, mxResources.get("position"));
  2522 + div2.appendChild(span);
  2523 +
  2524 + var left = this.addUnitInput(
  2525 + div2,
  2526 + this.getUnit(),
  2527 + 87,
  2528 + 52,
  2529 + function () {
  2530 + leftUpdate.apply(this, arguments);
  2531 + },
  2532 + this.getUnitStep(),
  2533 + null,
  2534 + null,
  2535 + this.isFloatUnit(),
  2536 + );
  2537 + var top = this.addUnitInput(
  2538 + div2,
  2539 + this.getUnit(),
  2540 + 16,
  2541 + 52,
  2542 + function () {
  2543 + topUpdate.apply(this, arguments);
  2544 + },
  2545 + this.getUnitStep(),
  2546 + null,
  2547 + null,
  2548 + this.isFloatUnit(),
  2549 + );
  2550 +
  2551 + mxUtils.br(div2);
  2552 +
  2553 + this.addLabel(div2, mxResources.get("left"), 87);
  2554 + this.addLabel(div2, mxResources.get("top"), 16);
  2555 +
  2556 + var listener = mxUtils.bind(this, function (sender, evt, force) {
  2557 + rect = ui.getSelectionState();
  2558 +
  2559 + if (
  2560 + !rect.containsLabel &&
  2561 + rect.vertices.length == graph.getSelectionCount() &&
  2562 + rect.width != null &&
  2563 + rect.height != null
  2564 + ) {
  2565 + div.style.display = "";
  2566 +
  2567 + if (force || document.activeElement != width) {
  2568 + width.value =
  2569 + this.inUnit(rect.width) +
  2570 + (rect.width == "" ? "" : " " + this.getUnit());
  2571 + }
  2572 +
  2573 + if (force || document.activeElement != height) {
  2574 + height.value =
  2575 + this.inUnit(rect.height) +
  2576 + (rect.height == "" ? "" : " " + this.getUnit());
  2577 + }
  2578 + } else {
  2579 + div.style.display = "none";
  2580 + }
  2581 +
  2582 + if (
  2583 + rect.vertices.length == graph.getSelectionCount() &&
  2584 + rect.x != null &&
  2585 + rect.y != null
  2586 + ) {
  2587 + div2.style.display = "";
  2588 +
  2589 + if (force || document.activeElement != left) {
  2590 + left.value =
  2591 + this.inUnit(rect.x) + (rect.x == "" ? "" : " " + this.getUnit());
  2592 + }
  2593 +
  2594 + if (force || document.activeElement != top) {
  2595 + top.value =
  2596 + this.inUnit(rect.y) + (rect.y == "" ? "" : " " + this.getUnit());
  2597 + }
  2598 + } else {
  2599 + div2.style.display = "none";
  2600 + }
  2601 + });
  2602 +
  2603 + this.addKeyHandler(left, listener);
  2604 + this.addKeyHandler(top, listener);
  2605 +
  2606 + model.addListener(mxEvent.CHANGE, listener);
  2607 + this.listeners.push({
  2608 + destroy: function () {
  2609 + model.removeListener(listener);
  2610 + },
  2611 + });
  2612 + listener();
  2613 +
  2614 + leftUpdate = this.addGeometryHandler(left, function (geo, value) {
  2615 + value = panel.fromUnit(value);
  2616 +
  2617 + if (geo.relative) {
  2618 + geo.offset.x = value;
  2619 + } else {
  2620 + geo.x = value;
  2621 + }
  2622 + });
  2623 + topUpdate = this.addGeometryHandler(top, function (geo, value) {
  2624 + value = panel.fromUnit(value);
  2625 +
  2626 + if (geo.relative) {
  2627 + geo.offset.y = value;
  2628 + } else {
  2629 + geo.y = value;
  2630 + }
  2631 + });
  2632 +
  2633 + if (rect.movable) {
  2634 + if (
  2635 + rect.edges.length == 0 &&
  2636 + rect.vertices.length == 1 &&
  2637 + model.isEdge(model.getParent(rect.vertices[0]))
  2638 + ) {
  2639 + var geo = graph.getCellGeometry(rect.vertices[0]);
  2640 +
  2641 + if (geo != null && geo.relative) {
  2642 + var btn = mxUtils.button(
  2643 + mxResources.get("center"),
  2644 + mxUtils.bind(this, function (evt) {
  2645 + model.beginUpdate();
  2646 + try {
  2647 + geo = geo.clone();
  2648 + geo.x = 0;
  2649 + geo.y = 0;
  2650 + geo.offset = new mxPoint();
  2651 + model.setGeometry(rect.vertices[0], geo);
  2652 + } finally {
  2653 + model.endUpdate();
  2654 + }
  2655 + }),
  2656 + );
  2657 +
  2658 + btn.setAttribute("title", mxResources.get("center"));
  2659 + btn.style.width = "210px";
  2660 + btn.style.position = "absolute";
  2661 + mxUtils.br(div2);
  2662 + mxUtils.br(div2);
  2663 + div2.appendChild(btn);
  2664 + }
  2665 + }
  2666 + container.appendChild(div2);
  2667 + }
  2668 +};
  2669 +
  2670 +/**
  2671 + *
  2672 + */
  2673 +ArrangePanel.prototype.addGeometryHandler = function (input, fn) {
  2674 + var ui = this.editorUi;
  2675 + var graph = ui.editor.graph;
  2676 + var initialValue = null;
  2677 + var panel = this;
  2678 +
  2679 + function update(evt) {
  2680 + if (input.value != "") {
  2681 + var value = parseFloat(input.value);
  2682 +
  2683 + if (isNaN(value)) {
  2684 + input.value = initialValue + " " + panel.getUnit();
  2685 + } else if (value != initialValue) {
  2686 + graph.getModel().beginUpdate();
  2687 + try {
  2688 + var cells = ui.getSelectionState().cells;
  2689 +
  2690 + for (var i = 0; i < cells.length; i++) {
  2691 + if (graph.getModel().isVertex(cells[i])) {
  2692 + var geo = graph.getCellGeometry(cells[i]);
  2693 +
  2694 + if (geo != null) {
  2695 + geo = geo.clone();
  2696 +
  2697 + if (!fn(geo, value, cells[i])) {
  2698 + var state = graph.view.getState(cells[i]);
  2699 +
  2700 + if (state != null && graph.isRecursiveVertexResize(state)) {
  2701 + graph.resizeChildCells(cells[i], geo);
  2702 + }
  2703 +
  2704 + graph.getModel().setGeometry(cells[i], geo);
  2705 + graph.constrainChildCells(cells[i]);
  2706 + }
  2707 + }
  2708 + }
  2709 + }
  2710 + } finally {
  2711 + graph.getModel().endUpdate();
  2712 + }
  2713 +
  2714 + initialValue = value;
  2715 + input.value = value + " " + panel.getUnit();
  2716 + }
  2717 + }
  2718 +
  2719 + mxEvent.consume(evt);
  2720 + }
  2721 +
  2722 + mxEvent.addListener(input, "blur", update);
  2723 + mxEvent.addListener(input, "change", update);
  2724 + mxEvent.addListener(input, "focus", function () {
  2725 + initialValue = input.value;
  2726 + });
  2727 +
  2728 + return update;
  2729 +};
  2730 +
  2731 +ArrangePanel.prototype.addEdgeGeometryHandler = function (input, fn) {
  2732 + var ui = this.editorUi;
  2733 + var graph = ui.editor.graph;
  2734 + var initialValue = null;
  2735 +
  2736 + function update(evt) {
  2737 + if (input.value != "") {
  2738 + var value = parseFloat(input.value);
  2739 +
  2740 + if (isNaN(value)) {
  2741 + input.value = initialValue + " pt";
  2742 + } else if (value != initialValue) {
  2743 + graph.getModel().beginUpdate();
  2744 + try {
  2745 + var cells = ui.getSelectionState().cells;
  2746 +
  2747 + for (var i = 0; i < cells.length; i++) {
  2748 + if (graph.getModel().isEdge(cells[i])) {
  2749 + var geo = graph.getCellGeometry(cells[i]);
  2750 +
  2751 + if (geo != null) {
  2752 + geo = geo.clone();
  2753 + fn(geo, value);
  2754 +
  2755 + graph.getModel().setGeometry(cells[i], geo);
  2756 + }
  2757 + }
  2758 + }
  2759 + } finally {
  2760 + graph.getModel().endUpdate();
  2761 + }
  2762 +
  2763 + initialValue = value;
  2764 + input.value = value + " pt";
  2765 + }
  2766 + }
  2767 +
  2768 + mxEvent.consume(evt);
  2769 + }
  2770 +
  2771 + mxEvent.addListener(input, "blur", update);
  2772 + mxEvent.addListener(input, "change", update);
  2773 + mxEvent.addListener(input, "focus", function () {
  2774 + initialValue = input.value;
  2775 + });
  2776 +
  2777 + return update;
  2778 +};
  2779 +
  2780 +/**
  2781 + *
  2782 + */
  2783 +ArrangePanel.prototype.addEdgeGeometry = function (container) {
  2784 + var ui = this.editorUi;
  2785 + var graph = ui.editor.graph;
  2786 + var rect = ui.getSelectionState();
  2787 + var div = this.createPanel();
  2788 +
  2789 + var span = document.createElement("div");
  2790 + span.style.position = "absolute";
  2791 + span.style.width = "70px";
  2792 + span.style.marginTop = "0px";
  2793 + span.style.fontWeight = "bold";
  2794 + mxUtils.write(span, mxResources.get("width"));
  2795 + div.appendChild(span);
  2796 +
  2797 + var widthUpdate, xtUpdate, ytUpdate, xsUpdate, ysUpdate;
  2798 + var width = this.addUnitInput(div, "pt", 12, 44, function () {
  2799 + widthUpdate.apply(this, arguments);
  2800 + });
  2801 +
  2802 + mxUtils.br(div);
  2803 + this.addKeyHandler(width, listener);
  2804 +
  2805 + var widthUpdate = mxUtils.bind(this, function (evt) {
  2806 + // Maximum stroke width is 999
  2807 + var value = parseInt(width.value);
  2808 + value = Math.min(999, Math.max(1, isNaN(value) ? 1 : value));
  2809 +
  2810 + if (
  2811 + value !=
  2812 + mxUtils.getValue(
  2813 + rect.style,
  2814 + "width",
  2815 + mxCellRenderer.defaultShapes["flexArrow"].prototype.defaultWidth,
  2816 + )
  2817 + ) {
  2818 + var cells = ui.getSelectionState().cells;
  2819 + graph.setCellStyles("width", value, cells);
  2820 + ui.fireEvent(
  2821 + new mxEventObject(
  2822 + "styleChanged",
  2823 + "keys",
  2824 + ["width"],
  2825 + "values",
  2826 + [value],
  2827 + "cells",
  2828 + cells,
  2829 + ),
  2830 + );
  2831 + }
  2832 +
  2833 + width.value = value + " pt";
  2834 + mxEvent.consume(evt);
  2835 + });
  2836 +
  2837 + mxEvent.addListener(width, "blur", widthUpdate);
  2838 + mxEvent.addListener(width, "change", widthUpdate);
  2839 +
  2840 + container.appendChild(div);
  2841 +
  2842 + var divs = this.createPanel();
  2843 + divs.style.paddingBottom = "30px";
  2844 +
  2845 + var span = document.createElement("div");
  2846 + span.style.position = "absolute";
  2847 + span.style.width = "70px";
  2848 + span.style.marginTop = "0px";
  2849 + mxUtils.write(span, mxResources.get("linestart"));
  2850 + divs.appendChild(span);
  2851 +
  2852 + var xs = this.addUnitInput(divs, "pt", 87, 52, function () {
  2853 + xsUpdate.apply(this, arguments);
  2854 + });
  2855 + var ys = this.addUnitInput(divs, "pt", 16, 52, function () {
  2856 + ysUpdate.apply(this, arguments);
  2857 + });
  2858 +
  2859 + mxUtils.br(divs);
  2860 + this.addLabel(divs, mxResources.get("left"), 87);
  2861 + this.addLabel(divs, mxResources.get("top"), 16);
  2862 + container.appendChild(divs);
  2863 + this.addKeyHandler(xs, listener);
  2864 + this.addKeyHandler(ys, listener);
  2865 +
  2866 + var divt = this.createPanel();
  2867 + divt.style.paddingBottom = "30px";
  2868 +
  2869 + var span = document.createElement("div");
  2870 + span.style.position = "absolute";
  2871 + span.style.width = "70px";
  2872 + span.style.marginTop = "0px";
  2873 + mxUtils.write(span, mxResources.get("lineend"));
  2874 + divt.appendChild(span);
  2875 +
  2876 + var xt = this.addUnitInput(divt, "pt", 87, 52, function () {
  2877 + xtUpdate.apply(this, arguments);
  2878 + });
  2879 + var yt = this.addUnitInput(divt, "pt", 16, 52, function () {
  2880 + ytUpdate.apply(this, arguments);
  2881 + });
  2882 +
  2883 + mxUtils.br(divt);
  2884 + this.addLabel(divt, mxResources.get("left"), 87);
  2885 + this.addLabel(divt, mxResources.get("top"), 16);
  2886 + container.appendChild(divt);
  2887 + this.addKeyHandler(xt, listener);
  2888 + this.addKeyHandler(yt, listener);
  2889 +
  2890 + var listener = mxUtils.bind(this, function (sender, evt, force) {
  2891 + rect = ui.getSelectionState();
  2892 + var cell = rect.cells[0];
  2893 +
  2894 + if (rect.style.shape == "link" || rect.style.shape == "flexArrow") {
  2895 + div.style.display = "";
  2896 +
  2897 + if (force || document.activeElement != width) {
  2898 + var value = mxUtils.getValue(
  2899 + rect.style,
  2900 + "width",
  2901 + mxCellRenderer.defaultShapes["flexArrow"].prototype.defaultWidth,
  2902 + );
  2903 + width.value = value + " pt";
  2904 + }
  2905 + } else {
  2906 + div.style.display = "none";
  2907 + }
  2908 +
  2909 + if (rect.cells.length == 1 && graph.model.isEdge(cell)) {
  2910 + var geo = graph.model.getGeometry(cell);
  2911 +
  2912 + if (
  2913 + geo.sourcePoint != null &&
  2914 + graph.model.getTerminal(cell, true) == null
  2915 + ) {
  2916 + xs.value = geo.sourcePoint.x;
  2917 + ys.value = geo.sourcePoint.y;
  2918 + } else {
  2919 + divs.style.display = "none";
  2920 + }
  2921 +
  2922 + if (
  2923 + geo.targetPoint != null &&
  2924 + graph.model.getTerminal(cell, false) == null
  2925 + ) {
  2926 + xt.value = geo.targetPoint.x;
  2927 + yt.value = geo.targetPoint.y;
  2928 + } else {
  2929 + divt.style.display = "none";
  2930 + }
  2931 + } else {
  2932 + divs.style.display = "none";
  2933 + divt.style.display = "none";
  2934 + }
  2935 + });
  2936 +
  2937 + xsUpdate = this.addEdgeGeometryHandler(xs, function (geo, value) {
  2938 + geo.sourcePoint.x = value;
  2939 + });
  2940 +
  2941 + ysUpdate = this.addEdgeGeometryHandler(ys, function (geo, value) {
  2942 + geo.sourcePoint.y = value;
  2943 + });
  2944 +
  2945 + xtUpdate = this.addEdgeGeometryHandler(xt, function (geo, value) {
  2946 + geo.targetPoint.x = value;
  2947 + });
  2948 +
  2949 + ytUpdate = this.addEdgeGeometryHandler(yt, function (geo, value) {
  2950 + geo.targetPoint.y = value;
  2951 + });
  2952 +
  2953 + graph.getModel().addListener(mxEvent.CHANGE, listener);
  2954 + this.listeners.push({
  2955 + destroy: function () {
  2956 + graph.getModel().removeListener(listener);
  2957 + },
  2958 + });
  2959 + listener();
  2960 +};
  2961 +
  2962 +/**
  2963 + * Adds the label menu items to the given menu and parent.
  2964 + */
  2965 +TextFormatPanel = function (format, editorUi, container) {
  2966 + BaseFormatPanel.call(this, format, editorUi, container);
  2967 + this.init();
  2968 +};
  2969 +
  2970 +mxUtils.extend(TextFormatPanel, BaseFormatPanel);
  2971 +
  2972 +/**
  2973 + * Adds the label menu items to the given menu and parent.
  2974 + */
  2975 +TextFormatPanel.prototype.init = function () {
  2976 + this.container.style.borderBottom = "none";
  2977 + this.addFont(this.container);
  2978 +};
  2979 +
  2980 +/**
  2981 + * Adds the label menu items to the given menu and parent.
  2982 + */
  2983 +TextFormatPanel.prototype.addFont = function (container) {
  2984 + var ui = this.editorUi;
  2985 + var editor = ui.editor;
  2986 + var graph = editor.graph;
  2987 + var ss = ui.getSelectionState();
  2988 +
  2989 + var title = this.createTitle(mxResources.get("font"));
  2990 + title.style.paddingLeft = "14px";
  2991 + title.style.paddingTop = "10px";
  2992 + title.style.paddingBottom = "6px";
  2993 + container.appendChild(title);
  2994 +
  2995 + var stylePanel = this.createPanel();
  2996 + stylePanel.style.paddingTop = "2px";
  2997 + stylePanel.style.paddingBottom = "2px";
  2998 + stylePanel.style.position = "relative";
  2999 + stylePanel.style.marginLeft = "-2px";
  3000 + stylePanel.style.borderWidth = "0px";
  3001 + stylePanel.className = "geToolbarContainer";
  3002 +
  3003 + if (graph.cellEditor.isContentEditing()) {
  3004 + var cssPanel = stylePanel.cloneNode();
  3005 +
  3006 + var cssMenu = this.editorUi.toolbar.addMenu(
  3007 + mxResources.get("style"),
  3008 + mxResources.get("style"),
  3009 + true,
  3010 + "formatBlock",
  3011 + cssPanel,
  3012 + null,
  3013 + true,
  3014 + );
  3015 + cssMenu.style.color = "rgb(112, 112, 112)";
  3016 + cssMenu.style.whiteSpace = "nowrap";
  3017 + cssMenu.style.overflow = "hidden";
  3018 + cssMenu.style.margin = "0px";
  3019 + this.addArrow(cssMenu);
  3020 + cssMenu.style.width = "200px";
  3021 + cssMenu.style.height = "15px";
  3022 +
  3023 + var arrow = cssMenu.getElementsByTagName("div")[0];
  3024 + arrow.style.cssFloat = "right";
  3025 + container.appendChild(cssPanel);
  3026 + }
  3027 +
  3028 + container.appendChild(stylePanel);
  3029 +
  3030 + var colorPanel = this.createPanel();
  3031 + colorPanel.style.marginTop = "8px";
  3032 + colorPanel.style.borderTop = "1px solid #c0c0c0";
  3033 + colorPanel.style.paddingTop = "6px";
  3034 + colorPanel.style.paddingBottom = "6px";
  3035 +
  3036 + var fontMenu = this.editorUi.toolbar.addMenu(
  3037 + "Helvetica",
  3038 + mxResources.get("fontFamily"),
  3039 + true,
  3040 + "fontFamily",
  3041 + stylePanel,
  3042 + null,
  3043 + true,
  3044 + );
  3045 + fontMenu.style.color = "rgb(112, 112, 112)";
  3046 + fontMenu.style.whiteSpace = "nowrap";
  3047 + fontMenu.style.overflow = "hidden";
  3048 + fontMenu.style.margin = "0px";
  3049 +
  3050 + this.addArrow(fontMenu);
  3051 + fontMenu.style.width = "200px";
  3052 + fontMenu.style.height = "15px";
  3053 +
  3054 + var stylePanel2 = stylePanel.cloneNode(false);
  3055 + stylePanel2.style.marginLeft = "-3px";
  3056 + var fontStyleItems = this.editorUi.toolbar.addItems(
  3057 + ["bold", "italic", "underline"],
  3058 + stylePanel2,
  3059 + true,
  3060 + );
  3061 + fontStyleItems[0].setAttribute(
  3062 + "title",
  3063 + mxResources.get("bold") +
  3064 + " (" +
  3065 + this.editorUi.actions.get("bold").shortcut +
  3066 + ")",
  3067 + );
  3068 + fontStyleItems[1].setAttribute(
  3069 + "title",
  3070 + mxResources.get("italic") +
  3071 + " (" +
  3072 + this.editorUi.actions.get("italic").shortcut +
  3073 + ")",
  3074 + );
  3075 + fontStyleItems[2].setAttribute(
  3076 + "title",
  3077 + mxResources.get("underline") +
  3078 + " (" +
  3079 + this.editorUi.actions.get("underline").shortcut +
  3080 + ")",
  3081 + );
  3082 +
  3083 + var verticalItem = this.editorUi.toolbar.addItems(
  3084 + ["vertical"],
  3085 + stylePanel2,
  3086 + true,
  3087 + )[0];
  3088 +
  3089 + container.appendChild(stylePanel2);
  3090 +
  3091 + this.styleButtons(fontStyleItems);
  3092 + this.styleButtons([verticalItem]);
  3093 +
  3094 + var stylePanel3 = stylePanel.cloneNode(false);
  3095 + stylePanel3.style.marginLeft = "-3px";
  3096 + stylePanel3.style.paddingBottom = "0px";
  3097 +
  3098 + // Helper function to return a wrapper function does not pass any arguments
  3099 + var callFn = function (fn) {
  3100 + return function () {
  3101 + return fn();
  3102 + };
  3103 + };
  3104 +
  3105 + var left = this.editorUi.toolbar.addButton(
  3106 + "geSprite-left",
  3107 + mxResources.get("left"),
  3108 + graph.cellEditor.isContentEditing()
  3109 + ? function (evt) {
  3110 + graph.cellEditor.alignText(mxConstants.ALIGN_LEFT, evt);
  3111 + ui.fireEvent(
  3112 + new mxEventObject(
  3113 + "styleChanged",
  3114 + "keys",
  3115 + [mxConstants.STYLE_ALIGN],
  3116 + "values",
  3117 + [mxConstants.ALIGN_LEFT],
  3118 + "cells",
  3119 + ss.cells,
  3120 + ),
  3121 + );
  3122 + }
  3123 + : callFn(
  3124 + this.editorUi.menus.createStyleChangeFunction(
  3125 + [mxConstants.STYLE_ALIGN],
  3126 + [mxConstants.ALIGN_LEFT],
  3127 + ),
  3128 + ),
  3129 + stylePanel3,
  3130 + );
  3131 + var center = this.editorUi.toolbar.addButton(
  3132 + "geSprite-center",
  3133 + mxResources.get("center"),
  3134 + graph.cellEditor.isContentEditing()
  3135 + ? function (evt) {
  3136 + graph.cellEditor.alignText(mxConstants.ALIGN_CENTER, evt);
  3137 + ui.fireEvent(
  3138 + new mxEventObject(
  3139 + "styleChanged",
  3140 + "keys",
  3141 + [mxConstants.STYLE_ALIGN],
  3142 + "values",
  3143 + [mxConstants.ALIGN_CENTER],
  3144 + "cells",
  3145 + ss.cells,
  3146 + ),
  3147 + );
  3148 + }
  3149 + : callFn(
  3150 + this.editorUi.menus.createStyleChangeFunction(
  3151 + [mxConstants.STYLE_ALIGN],
  3152 + [mxConstants.ALIGN_CENTER],
  3153 + ),
  3154 + ),
  3155 + stylePanel3,
  3156 + );
  3157 + var right = this.editorUi.toolbar.addButton(
  3158 + "geSprite-right",
  3159 + mxResources.get("right"),
  3160 + graph.cellEditor.isContentEditing()
  3161 + ? function (evt) {
  3162 + graph.cellEditor.alignText(mxConstants.ALIGN_RIGHT, evt);
  3163 + ui.fireEvent(
  3164 + new mxEventObject(
  3165 + "styleChanged",
  3166 + "keys",
  3167 + [mxConstants.STYLE_ALIGN],
  3168 + "values",
  3169 + [mxConstants.ALIGN_RIGHT],
  3170 + "cells",
  3171 + ss.cells,
  3172 + ),
  3173 + );
  3174 + }
  3175 + : callFn(
  3176 + this.editorUi.menus.createStyleChangeFunction(
  3177 + [mxConstants.STYLE_ALIGN],
  3178 + [mxConstants.ALIGN_RIGHT],
  3179 + ),
  3180 + ),
  3181 + stylePanel3,
  3182 + );
  3183 +
  3184 + this.styleButtons([left, center, right]);
  3185 +
  3186 + // Quick hack for strikethrough
  3187 + // TODO: Add translations and toggle state
  3188 + if (graph.cellEditor.isContentEditing()) {
  3189 + var strike = this.editorUi.toolbar.addButton(
  3190 + "geSprite-removeformat",
  3191 + mxResources.get("strikethrough"),
  3192 + function () {
  3193 + document.execCommand("strikeThrough", false, null);
  3194 + },
  3195 + stylePanel2,
  3196 + );
  3197 + this.styleButtons([strike]);
  3198 +
  3199 + strike.firstChild.style.background =
  3200 + "url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PHBhdGggaWQ9ImEiIGQ9Ik0wIDBoMjR2MjRIMFYweiIvPjwvZGVmcz48Y2xpcFBhdGggaWQ9ImIiPjx1c2UgeGxpbms6aHJlZj0iI2EiIG92ZXJmbG93PSJ2aXNpYmxlIi8+PC9jbGlwUGF0aD48cGF0aCBjbGlwLXBhdGg9InVybCgjYikiIGZpbGw9IiMwMTAxMDEiIGQ9Ik03LjI0IDguNzVjLS4yNi0uNDgtLjM5LTEuMDMtLjM5LTEuNjcgMC0uNjEuMTMtMS4xNi40LTEuNjcuMjYtLjUuNjMtLjkzIDEuMTEtMS4yOS40OC0uMzUgMS4wNS0uNjMgMS43LS44My42Ni0uMTkgMS4zOS0uMjkgMi4xOC0uMjkuODEgMCAxLjU0LjExIDIuMjEuMzQuNjYuMjIgMS4yMy41NCAxLjY5Ljk0LjQ3LjQuODMuODggMS4wOCAxLjQzLjI1LjU1LjM4IDEuMTUuMzggMS44MWgtMy4wMWMwLS4zMS0uMDUtLjU5LS4xNS0uODUtLjA5LS4yNy0uMjQtLjQ5LS40NC0uNjgtLjItLjE5LS40NS0uMzMtLjc1LS40NC0uMy0uMS0uNjYtLjE2LTEuMDYtLjE2LS4zOSAwLS43NC4wNC0xLjAzLjEzLS4yOS4wOS0uNTMuMjEtLjcyLjM2LS4xOS4xNi0uMzQuMzQtLjQ0LjU1LS4xLjIxLS4xNS40My0uMTUuNjYgMCAuNDguMjUuODguNzQgMS4yMS4zOC4yNS43Ny40OCAxLjQxLjdINy4zOWMtLjA1LS4wOC0uMTEtLjE3LS4xNS0uMjV6TTIxIDEydi0ySDN2Mmg5LjYyYy4xOC4wNy40LjE0LjU1LjIuMzcuMTcuNjYuMzQuODcuNTEuMjEuMTcuMzUuMzYuNDMuNTcuMDcuMi4xMS40My4xMS42OSAwIC4yMy0uMDUuNDUtLjE0LjY2LS4wOS4yLS4yMy4zOC0uNDIuNTMtLjE5LjE1LS40Mi4yNi0uNzEuMzUtLjI5LjA4LS42My4xMy0xLjAxLjEzLS40MyAwLS44My0uMDQtMS4xOC0uMTNzLS42Ni0uMjMtLjkxLS40MmMtLjI1LS4xOS0uNDUtLjQ0LS41OS0uNzUtLjE0LS4zMS0uMjUtLjc2LS4yNS0xLjIxSDYuNGMwIC41NS4wOCAxLjEzLjI0IDEuNTguMTYuNDUuMzcuODUuNjUgMS4yMS4yOC4zNS42LjY2Ljk4LjkyLjM3LjI2Ljc4LjQ4IDEuMjIuNjUuNDQuMTcuOS4zIDEuMzguMzkuNDguMDguOTYuMTMgMS40NC4xMy44IDAgMS41My0uMDkgMi4xOC0uMjhzMS4yMS0uNDUgMS42Ny0uNzljLjQ2LS4zNC44Mi0uNzcgMS4wNy0xLjI3cy4zOC0xLjA3LjM4LTEuNzFjMC0uNi0uMS0xLjE0LS4zMS0xLjYxLS4wNS0uMTEtLjExLS4yMy0uMTctLjMzSDIxeiIvPjwvc3ZnPg==)";
  3201 + strike.firstChild.style.backgroundPosition = "2px 2px";
  3202 + strike.firstChild.style.backgroundSize = "18px 18px";
  3203 +
  3204 + this.styleButtons([strike]);
  3205 + }
  3206 +
  3207 + var top = this.editorUi.toolbar.addButton(
  3208 + "geSprite-top",
  3209 + mxResources.get("top"),
  3210 + callFn(
  3211 + this.editorUi.menus.createStyleChangeFunction(
  3212 + [mxConstants.STYLE_VERTICAL_ALIGN],
  3213 + [mxConstants.ALIGN_TOP],
  3214 + ),
  3215 + ),
  3216 + stylePanel3,
  3217 + );
  3218 + var middle = this.editorUi.toolbar.addButton(
  3219 + "geSprite-middle",
  3220 + mxResources.get("middle"),
  3221 + callFn(
  3222 + this.editorUi.menus.createStyleChangeFunction(
  3223 + [mxConstants.STYLE_VERTICAL_ALIGN],
  3224 + [mxConstants.ALIGN_MIDDLE],
  3225 + ),
  3226 + ),
  3227 + stylePanel3,
  3228 + );
  3229 + var bottom = this.editorUi.toolbar.addButton(
  3230 + "geSprite-bottom",
  3231 + mxResources.get("bottom"),
  3232 + callFn(
  3233 + this.editorUi.menus.createStyleChangeFunction(
  3234 + [mxConstants.STYLE_VERTICAL_ALIGN],
  3235 + [mxConstants.ALIGN_BOTTOM],
  3236 + ),
  3237 + ),
  3238 + stylePanel3,
  3239 + );
  3240 +
  3241 + this.styleButtons([top, middle, bottom]);
  3242 +
  3243 + container.appendChild(stylePanel3);
  3244 +
  3245 + // Hack for updating UI state below based on current text selection
  3246 + // currentTable is the current selected DOM table updated below
  3247 + var sub, sup, full, tableWrapper, currentTable, tableCell, tableRow;
  3248 +
  3249 + if (graph.cellEditor.isContentEditing()) {
  3250 + top.style.display = "none";
  3251 + middle.style.display = "none";
  3252 + bottom.style.display = "none";
  3253 + verticalItem.style.display = "none";
  3254 +
  3255 + full = this.editorUi.toolbar.addButton(
  3256 + "geSprite-justifyfull",
  3257 + mxResources.get("block"),
  3258 + function () {
  3259 + if (full.style.opacity == 1) {
  3260 + document.execCommand("justifyfull", false, null);
  3261 + }
  3262 + },
  3263 + stylePanel3,
  3264 + );
  3265 + full.style.marginRight = "9px";
  3266 + full.style.opacity = 1;
  3267 +
  3268 + this.styleButtons([
  3269 + full,
  3270 + (sub = this.editorUi.toolbar.addButton(
  3271 + "geSprite-subscript",
  3272 + mxResources.get("subscript") + " (" + Editor.ctrlKey + "+,)",
  3273 + function () {
  3274 + document.execCommand("subscript", false, null);
  3275 + },
  3276 + stylePanel3,
  3277 + )),
  3278 + (sup = this.editorUi.toolbar.addButton(
  3279 + "geSprite-superscript",
  3280 + mxResources.get("superscript") + " (" + Editor.ctrlKey + "+.)",
  3281 + function () {
  3282 + document.execCommand("superscript", false, null);
  3283 + },
  3284 + stylePanel3,
  3285 + )),
  3286 + ]);
  3287 + sub.style.marginLeft = "10px";
  3288 +
  3289 + var tmp = stylePanel3.cloneNode(false);
  3290 + tmp.style.paddingTop = "4px";
  3291 + var btns = [
  3292 + this.editorUi.toolbar.addButton(
  3293 + "geSprite-orderedlist",
  3294 + mxResources.get("numberedList"),
  3295 + function () {
  3296 + document.execCommand("insertorderedlist", false, null);
  3297 + },
  3298 + tmp,
  3299 + ),
  3300 + this.editorUi.toolbar.addButton(
  3301 + "geSprite-unorderedlist",
  3302 + mxResources.get("bulletedList"),
  3303 + function () {
  3304 + document.execCommand("insertunorderedlist", false, null);
  3305 + },
  3306 + tmp,
  3307 + ),
  3308 + this.editorUi.toolbar.addButton(
  3309 + "geSprite-outdent",
  3310 + mxResources.get("decreaseIndent"),
  3311 + function () {
  3312 + document.execCommand("outdent", false, null);
  3313 + },
  3314 + tmp,
  3315 + ),
  3316 + this.editorUi.toolbar.addButton(
  3317 + "geSprite-indent",
  3318 + mxResources.get("increaseIndent"),
  3319 + function () {
  3320 + document.execCommand("indent", false, null);
  3321 + },
  3322 + tmp,
  3323 + ),
  3324 + this.editorUi.toolbar.addButton(
  3325 + "geSprite-removeformat",
  3326 + mxResources.get("removeFormat"),
  3327 + function () {
  3328 + document.execCommand("removeformat", false, null);
  3329 + },
  3330 + tmp,
  3331 + ),
  3332 + this.editorUi.toolbar.addButton(
  3333 + "geSprite-code",
  3334 + mxResources.get("html"),
  3335 + function () {
  3336 + graph.cellEditor.toggleViewMode();
  3337 + },
  3338 + tmp,
  3339 + ),
  3340 + ];
  3341 + this.styleButtons(btns);
  3342 + btns[btns.length - 2].style.marginLeft = "10px";
  3343 +
  3344 + container.appendChild(tmp);
  3345 + } else {
  3346 + fontStyleItems[2].style.marginRight = "12px";
  3347 + right.style.marginRight = "12px";
  3348 + }
  3349 +
  3350 + // Label position
  3351 + var stylePanel4 = stylePanel.cloneNode(false);
  3352 + stylePanel4.style.marginLeft = "0px";
  3353 + stylePanel4.style.paddingTop = "8px";
  3354 + stylePanel4.style.paddingBottom = "4px";
  3355 + stylePanel4.style.fontWeight = "normal";
  3356 +
  3357 + mxUtils.write(stylePanel4, mxResources.get("position"));
  3358 +
  3359 + // Adds label position options
  3360 + var positionSelect = document.createElement("select");
  3361 + positionSelect.style.position = "absolute";
  3362 + positionSelect.style.left = "126px";
  3363 + positionSelect.style.width = "98px";
  3364 + positionSelect.style.border = "1px solid rgb(160, 160, 160)";
  3365 + positionSelect.style.borderRadius = "4px";
  3366 + positionSelect.style.marginTop = "-2px";
  3367 +
  3368 + var directions = [
  3369 + "topLeft",
  3370 + "top",
  3371 + "topRight",
  3372 + "left",
  3373 + "center",
  3374 + "right",
  3375 + "bottomLeft",
  3376 + "bottom",
  3377 + "bottomRight",
  3378 + ];
  3379 + var lset = {
  3380 + topLeft: [
  3381 + mxConstants.ALIGN_LEFT,
  3382 + mxConstants.ALIGN_TOP,
  3383 + mxConstants.ALIGN_RIGHT,
  3384 + mxConstants.ALIGN_BOTTOM,
  3385 + ],
  3386 + top: [
  3387 + mxConstants.ALIGN_CENTER,
  3388 + mxConstants.ALIGN_TOP,
  3389 + mxConstants.ALIGN_CENTER,
  3390 + mxConstants.ALIGN_BOTTOM,
  3391 + ],
  3392 + topRight: [
  3393 + mxConstants.ALIGN_RIGHT,
  3394 + mxConstants.ALIGN_TOP,
  3395 + mxConstants.ALIGN_LEFT,
  3396 + mxConstants.ALIGN_BOTTOM,
  3397 + ],
  3398 + left: [
  3399 + mxConstants.ALIGN_LEFT,
  3400 + mxConstants.ALIGN_MIDDLE,
  3401 + mxConstants.ALIGN_RIGHT,
  3402 + mxConstants.ALIGN_MIDDLE,
  3403 + ],
  3404 + center: [
  3405 + mxConstants.ALIGN_CENTER,
  3406 + mxConstants.ALIGN_MIDDLE,
  3407 + mxConstants.ALIGN_CENTER,
  3408 + mxConstants.ALIGN_MIDDLE,
  3409 + ],
  3410 + right: [
  3411 + mxConstants.ALIGN_RIGHT,
  3412 + mxConstants.ALIGN_MIDDLE,
  3413 + mxConstants.ALIGN_LEFT,
  3414 + mxConstants.ALIGN_MIDDLE,
  3415 + ],
  3416 + bottomLeft: [
  3417 + mxConstants.ALIGN_LEFT,
  3418 + mxConstants.ALIGN_BOTTOM,
  3419 + mxConstants.ALIGN_RIGHT,
  3420 + mxConstants.ALIGN_TOP,
  3421 + ],
  3422 + bottom: [
  3423 + mxConstants.ALIGN_CENTER,
  3424 + mxConstants.ALIGN_BOTTOM,
  3425 + mxConstants.ALIGN_CENTER,
  3426 + mxConstants.ALIGN_TOP,
  3427 + ],
  3428 + bottomRight: [
  3429 + mxConstants.ALIGN_RIGHT,
  3430 + mxConstants.ALIGN_BOTTOM,
  3431 + mxConstants.ALIGN_LEFT,
  3432 + mxConstants.ALIGN_TOP,
  3433 + ],
  3434 + };
  3435 +
  3436 + for (var i = 0; i < directions.length; i++) {
  3437 + var positionOption = document.createElement("option");
  3438 + positionOption.setAttribute("value", directions[i]);
  3439 + mxUtils.write(positionOption, mxResources.get(directions[i]));
  3440 + positionSelect.appendChild(positionOption);
  3441 + }
  3442 +
  3443 + stylePanel4.appendChild(positionSelect);
  3444 +
  3445 + // Writing direction
  3446 + var stylePanel5 = stylePanel.cloneNode(false);
  3447 + stylePanel5.style.marginLeft = "0px";
  3448 + stylePanel5.style.paddingTop = "4px";
  3449 + stylePanel5.style.paddingBottom = "4px";
  3450 + stylePanel5.style.fontWeight = "normal";
  3451 +
  3452 + mxUtils.write(stylePanel5, mxResources.get("writingDirection"));
  3453 +
  3454 + // Adds writing direction options
  3455 + // LATER: Handle reselect of same option in all selects (change event
  3456 + // is not fired for same option so have opened state on click) and
  3457 + // handle multiple different styles for current selection
  3458 + var dirSelect = document.createElement("select");
  3459 + dirSelect.style.position = "absolute";
  3460 + dirSelect.style.border = "1px solid rgb(160, 160, 160)";
  3461 + dirSelect.style.left = "126px";
  3462 + dirSelect.style.width = "98px";
  3463 + dirSelect.style.borderRadius = "4px";
  3464 + dirSelect.style.marginTop = "-2px";
  3465 +
  3466 + // NOTE: For automatic we use the value null since automatic
  3467 + // requires the text to be non formatted and non-wrapped
  3468 + var dirs = ["automatic", "leftToRight", "rightToLeft"];
  3469 + var dirSet = {
  3470 + automatic: null,
  3471 + leftToRight: mxConstants.TEXT_DIRECTION_LTR,
  3472 + rightToLeft: mxConstants.TEXT_DIRECTION_RTL,
  3473 + };
  3474 +
  3475 + for (var i = 0; i < dirs.length; i++) {
  3476 + var dirOption = document.createElement("option");
  3477 + dirOption.setAttribute("value", dirs[i]);
  3478 + mxUtils.write(dirOption, mxResources.get(dirs[i]));
  3479 + dirSelect.appendChild(dirOption);
  3480 + }
  3481 +
  3482 + stylePanel5.appendChild(dirSelect);
  3483 +
  3484 + if (!graph.isEditing()) {
  3485 + container.appendChild(stylePanel4);
  3486 +
  3487 + mxEvent.addListener(positionSelect, "change", function (evt) {
  3488 + graph.getModel().beginUpdate();
  3489 + try {
  3490 + var vals = lset[positionSelect.value];
  3491 +
  3492 + if (vals != null) {
  3493 + graph.setCellStyles(
  3494 + mxConstants.STYLE_LABEL_POSITION,
  3495 + vals[0],
  3496 + ss.cells,
  3497 + );
  3498 + graph.setCellStyles(
  3499 + mxConstants.STYLE_VERTICAL_LABEL_POSITION,
  3500 + vals[1],
  3501 + ss.cells,
  3502 + );
  3503 + graph.setCellStyles(mxConstants.STYLE_ALIGN, vals[2], ss.cells);
  3504 + graph.setCellStyles(
  3505 + mxConstants.STYLE_VERTICAL_ALIGN,
  3506 + vals[3],
  3507 + ss.cells,
  3508 + );
  3509 + }
  3510 + } finally {
  3511 + graph.getModel().endUpdate();
  3512 + }
  3513 +
  3514 + mxEvent.consume(evt);
  3515 + });
  3516 +
  3517 + // LATER: Update dir in text editor while editing and update style with label
  3518 + // NOTE: The tricky part is handling and passing on the auto value
  3519 + container.appendChild(stylePanel5);
  3520 +
  3521 + mxEvent.addListener(dirSelect, "change", function (evt) {
  3522 + graph.setCellStyles(
  3523 + mxConstants.STYLE_TEXT_DIRECTION,
  3524 + dirSet[dirSelect.value],
  3525 + ss.cells,
  3526 + );
  3527 + mxEvent.consume(evt);
  3528 + });
  3529 + }
  3530 +
  3531 + // Fontsize
  3532 + var input = document.createElement("input");
  3533 + input.style.position = "absolute";
  3534 + input.style.border = "1px solid rgb(160, 160, 160)";
  3535 + input.style.textAlign = "right";
  3536 + input.style.marginTop = "4px";
  3537 + input.style.left = "161px";
  3538 + input.style.width = "53px";
  3539 + input.style.borderRadius = "4px";
  3540 + input.style.height = "23px";
  3541 + input.style.boxSizing = "border-box";
  3542 + stylePanel2.appendChild(input);
  3543 +
  3544 + // Workaround for font size 4 if no text is selected is update font size below
  3545 + // after first character was entered (as the font element is lazy created)
  3546 + var pendingFontSize = null;
  3547 +
  3548 + var inputUpdate = this.installInputHandler(
  3549 + input,
  3550 + mxConstants.STYLE_FONTSIZE,
  3551 + Menus.prototype.defaultFontSize,
  3552 + 1,
  3553 + 999,
  3554 + " pt",
  3555 + function (fontSize) {
  3556 + // IE does not support containsNode
  3557 + // KNOWN: Fixes font size issues but bypasses undo
  3558 + if (window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11) {
  3559 + var selection = window.getSelection();
  3560 + var container =
  3561 + selection.rangeCount > 0
  3562 + ? selection.getRangeAt(0).commonAncestorContainer
  3563 + : graph.cellEditor.textarea;
  3564 +
  3565 + function updateSize(elt, ignoreContains) {
  3566 + if (
  3567 + graph.cellEditor.textarea != null &&
  3568 + elt != graph.cellEditor.textarea &&
  3569 + graph.cellEditor.textarea.contains(elt) &&
  3570 + (ignoreContains || selection.containsNode(elt, true))
  3571 + ) {
  3572 + if (elt.nodeName == "FONT") {
  3573 + elt.removeAttribute("size");
  3574 + elt.style.fontSize = fontSize + "px";
  3575 + } else {
  3576 + var css = mxUtils.getCurrentStyle(elt);
  3577 +
  3578 + if (css.fontSize != fontSize + "px") {
  3579 + if (
  3580 + mxUtils.getCurrentStyle(elt.parentNode).fontSize !=
  3581 + fontSize + "px"
  3582 + ) {
  3583 + elt.style.fontSize = fontSize + "px";
  3584 + } else {
  3585 + elt.style.fontSize = "";
  3586 + }
  3587 + }
  3588 + }
  3589 + }
  3590 +
  3591 + ui.fireEvent(
  3592 + new mxEventObject(
  3593 + "styleChanged",
  3594 + "keys",
  3595 + [mxConstants.STYLE_FONTSIZE],
  3596 + "values",
  3597 + [fontSize],
  3598 + "cells",
  3599 + ss.cells,
  3600 + ),
  3601 + );
  3602 + }
  3603 +
  3604 + // Wraps text node or mixed selection with leading text in a font element
  3605 + if (
  3606 + container == graph.cellEditor.textarea ||
  3607 + container.nodeType != mxConstants.NODETYPE_ELEMENT
  3608 + ) {
  3609 + document.execCommand("fontSize", false, "1");
  3610 + }
  3611 +
  3612 + if (container != graph.cellEditor.textarea) {
  3613 + container = container.parentNode;
  3614 + }
  3615 +
  3616 + if (
  3617 + container != null &&
  3618 + container.nodeType == mxConstants.NODETYPE_ELEMENT
  3619 + ) {
  3620 + var elts = container.getElementsByTagName("*");
  3621 + updateSize(container);
  3622 +
  3623 + for (var i = 0; i < elts.length; i++) {
  3624 + updateSize(elts[i]);
  3625 + }
  3626 + }
  3627 +
  3628 + input.value = fontSize + " pt";
  3629 + } else if (window.getSelection || document.selection) {
  3630 + // Checks selection
  3631 + var par = null;
  3632 +
  3633 + if (document.selection) {
  3634 + par = document.selection.createRange().parentElement();
  3635 + } else {
  3636 + var selection = window.getSelection();
  3637 +
  3638 + if (selection.rangeCount > 0) {
  3639 + par = selection.getRangeAt(0).commonAncestorContainer;
  3640 + }
  3641 + }
  3642 +
  3643 + // Node.contains does not work for text nodes in IE11
  3644 + function isOrContains(container, node) {
  3645 + while (node != null) {
  3646 + if (node === container) {
  3647 + return true;
  3648 + }
  3649 +
  3650 + node = node.parentNode;
  3651 + }
  3652 +
  3653 + return false;
  3654 + }
  3655 +
  3656 + if (par != null && isOrContains(graph.cellEditor.textarea, par)) {
  3657 + pendingFontSize = fontSize;
  3658 +
  3659 + // Workaround for can't set font size in px is to change font size afterwards
  3660 + document.execCommand("fontSize", false, "4");
  3661 + var elts = graph.cellEditor.textarea.getElementsByTagName("font");
  3662 +
  3663 + for (var i = 0; i < elts.length; i++) {
  3664 + if (elts[i].getAttribute("size") == "4") {
  3665 + elts[i].removeAttribute("size");
  3666 + elts[i].style.fontSize = pendingFontSize + "px";
  3667 +
  3668 + // Overrides fontSize in input with the one just assigned as a workaround
  3669 + // for potential fontSize values of parent elements that don't match
  3670 + window.setTimeout(function () {
  3671 + input.value = pendingFontSize + " pt";
  3672 + pendingFontSize = null;
  3673 + }, 0);
  3674 +
  3675 + break;
  3676 + }
  3677 + }
  3678 + }
  3679 + }
  3680 + },
  3681 + true,
  3682 + );
  3683 +
  3684 + var stepper = this.createStepper(
  3685 + input,
  3686 + inputUpdate,
  3687 + 1,
  3688 + 10,
  3689 + true,
  3690 + Menus.prototype.defaultFontSize,
  3691 + );
  3692 + stepper.style.display = input.style.display;
  3693 + stepper.style.marginTop = "4px";
  3694 + stepper.style.left = "214px";
  3695 +
  3696 + stylePanel2.appendChild(stepper);
  3697 +
  3698 + var arrow = fontMenu.getElementsByTagName("div")[0];
  3699 + arrow.style.cssFloat = "right";
  3700 +
  3701 + var bgColorApply = null;
  3702 + var currentBgColor = graph.shapeBackgroundColor;
  3703 +
  3704 + var fontColorApply = null;
  3705 + var currentFontColor = graph.shapeForegroundColor;
  3706 +
  3707 + var bgPanel = graph.cellEditor.isContentEditing()
  3708 + ? this.createColorOption(
  3709 + mxResources.get("backgroundColor"),
  3710 + function () {
  3711 + return currentBgColor;
  3712 + },
  3713 + function (color) {
  3714 + document.execCommand(
  3715 + "backcolor",
  3716 + false,
  3717 + color != mxConstants.NONE ? color : "transparent",
  3718 + );
  3719 + ui.fireEvent(
  3720 + new mxEventObject(
  3721 + "styleChanged",
  3722 + "keys",
  3723 + [mxConstants.STYLE_LABEL_BACKGROUNDCOLOR],
  3724 + "values",
  3725 + [color],
  3726 + "cells",
  3727 + ss.cells,
  3728 + ),
  3729 + );
  3730 + },
  3731 + graph.shapeBackgroundColor,
  3732 + {
  3733 + install: function (apply) {
  3734 + bgColorApply = apply;
  3735 + },
  3736 + destroy: function () {
  3737 + bgColorApply = null;
  3738 + },
  3739 + },
  3740 + null,
  3741 + true,
  3742 + )
  3743 + : this.createCellColorOption(
  3744 + mxResources.get("backgroundColor"),
  3745 + mxConstants.STYLE_LABEL_BACKGROUNDCOLOR,
  3746 + "default",
  3747 + null,
  3748 + function (color) {
  3749 + graph.updateLabelElements(ss.cells, function (elt) {
  3750 + elt.style.backgroundColor = null;
  3751 + });
  3752 + },
  3753 + graph.shapeBackgroundColor,
  3754 + );
  3755 + bgPanel.style.fontWeight = "bold";
  3756 +
  3757 + var borderPanel = this.createCellColorOption(
  3758 + mxResources.get("borderColor"),
  3759 + mxConstants.STYLE_LABEL_BORDERCOLOR,
  3760 + "default",
  3761 + null,
  3762 + null,
  3763 + graph.shapeForegroundColor,
  3764 + );
  3765 + borderPanel.style.fontWeight = "bold";
  3766 +
  3767 + var defs =
  3768 + ss.vertices.length >= 1
  3769 + ? graph.stylesheet.getDefaultVertexStyle()
  3770 + : graph.stylesheet.getDefaultEdgeStyle();
  3771 +
  3772 + var panel = graph.cellEditor.isContentEditing()
  3773 + ? this.createColorOption(
  3774 + mxResources.get("fontColor"),
  3775 + function () {
  3776 + return currentFontColor;
  3777 + },
  3778 + function (color) {
  3779 + if (mxClient.IS_FF) {
  3780 + // Workaround for Firefox that adds the font element around
  3781 + // anchor elements which ignore inherited colors is to move
  3782 + // the font element inside anchor elements
  3783 + var tmp = graph.cellEditor.textarea.getElementsByTagName("font");
  3784 + var oldFonts = [];
  3785 +
  3786 + for (var i = 0; i < tmp.length; i++) {
  3787 + oldFonts.push({
  3788 + node: tmp[i],
  3789 + color: tmp[i].getAttribute("color"),
  3790 + });
  3791 + }
  3792 +
  3793 + document.execCommand(
  3794 + "forecolor",
  3795 + false,
  3796 + color != mxConstants.NONE ? color : "transparent",
  3797 + );
  3798 + ui.fireEvent(
  3799 + new mxEventObject(
  3800 + "styleChanged",
  3801 + "keys",
  3802 + [mxConstants.STYLE_FONTCOLOR],
  3803 + "values",
  3804 + [color],
  3805 + "cells",
  3806 + ss.cells,
  3807 + ),
  3808 + );
  3809 +
  3810 + // Finds the new or changed font element
  3811 + var newFonts =
  3812 + graph.cellEditor.textarea.getElementsByTagName("font");
  3813 +
  3814 + for (var i = 0; i < newFonts.length; i++) {
  3815 + if (
  3816 + i >= oldFonts.length ||
  3817 + newFonts[i] != oldFonts[i].node ||
  3818 + (newFonts[i] == oldFonts[i].node &&
  3819 + newFonts[i].getAttribute("color") != oldFonts[i].color)
  3820 + ) {
  3821 + var child = newFonts[i].firstChild;
  3822 +
  3823 + // Moves the font element to inside the anchor element and adopts all children
  3824 + if (
  3825 + child != null &&
  3826 + child.nodeName == "A" &&
  3827 + child.nextSibling == null &&
  3828 + child.firstChild != null
  3829 + ) {
  3830 + var parent = newFonts[i].parentNode;
  3831 + parent.insertBefore(child, newFonts[i]);
  3832 + var tmp = child.firstChild;
  3833 +
  3834 + while (tmp != null) {
  3835 + var next = tmp.nextSibling;
  3836 + newFonts[i].appendChild(tmp);
  3837 + tmp = next;
  3838 + }
  3839 +
  3840 + child.appendChild(newFonts[i]);
  3841 + }
  3842 +
  3843 + break;
  3844 + }
  3845 + }
  3846 + } else {
  3847 + document.execCommand(
  3848 + "forecolor",
  3849 + false,
  3850 + color != mxConstants.NONE ? color : "transparent",
  3851 + );
  3852 + ui.fireEvent(
  3853 + new mxEventObject(
  3854 + "styleChanged",
  3855 + "keys",
  3856 + [mxConstants.STYLE_FONTCOLOR],
  3857 + "values",
  3858 + [color],
  3859 + "cells",
  3860 + ss.cells,
  3861 + ),
  3862 + );
  3863 + }
  3864 + },
  3865 + defs[mxConstants.STYLE_FONTCOLOR] != null
  3866 + ? defs[mxConstants.STYLE_FONTCOLOR]
  3867 + : graph.shapeForegroundColor,
  3868 + {
  3869 + install: function (apply) {
  3870 + fontColorApply = apply;
  3871 + },
  3872 + destroy: function () {
  3873 + fontColorApply = null;
  3874 + },
  3875 + },
  3876 + null,
  3877 + true,
  3878 + )
  3879 + : this.createCellColorOption(
  3880 + mxResources.get("fontColor"),
  3881 + mxConstants.STYLE_FONTCOLOR,
  3882 + "default",
  3883 + function (color) {
  3884 + if (color == mxConstants.NONE) {
  3885 + bgPanel.style.display = "none";
  3886 + } else {
  3887 + bgPanel.style.display = "";
  3888 + }
  3889 +
  3890 + borderPanel.style.display = bgPanel.style.display;
  3891 + },
  3892 + function (color) {
  3893 + if (color == mxConstants.NONE) {
  3894 + graph.setCellStyles(mxConstants.STYLE_NOLABEL, "1", ss.cells);
  3895 + } else {
  3896 + graph.setCellStyles(mxConstants.STYLE_NOLABEL, null, ss.cells);
  3897 + }
  3898 +
  3899 + graph.setCellStyles(mxConstants.STYLE_FONTCOLOR, color, ss.cells);
  3900 +
  3901 + graph.updateLabelElements(ss.cells, function (elt) {
  3902 + elt.removeAttribute("color");
  3903 + elt.style.color = null;
  3904 + });
  3905 + },
  3906 + graph.shapeForegroundColor,
  3907 + );
  3908 + panel.style.fontWeight = "bold";
  3909 +
  3910 + colorPanel.appendChild(panel);
  3911 + colorPanel.appendChild(bgPanel);
  3912 +
  3913 + if (!graph.cellEditor.isContentEditing()) {
  3914 + colorPanel.appendChild(borderPanel);
  3915 + }
  3916 +
  3917 + container.appendChild(colorPanel);
  3918 +
  3919 + var extraPanel = this.createPanel();
  3920 + extraPanel.style.paddingTop = "2px";
  3921 + extraPanel.style.paddingBottom = "4px";
  3922 +
  3923 + var wwCells = graph.filterSelectionCells(
  3924 + mxUtils.bind(this, function (cell) {
  3925 + var state = graph.view.getState(cell);
  3926 +
  3927 + return (
  3928 + state == null ||
  3929 + graph.isAutoSizeState(state) ||
  3930 + graph.getModel().isEdge(cell) ||
  3931 + (!graph.isTableRow(cell) &&
  3932 + !graph.isTableCell(cell) &&
  3933 + !graph.isCellResizable(cell))
  3934 + );
  3935 + }),
  3936 + );
  3937 +
  3938 + var wwOpt = this.createCellOption(
  3939 + mxResources.get("wordWrap"),
  3940 + mxConstants.STYLE_WHITE_SPACE,
  3941 + null,
  3942 + "wrap",
  3943 + "null",
  3944 + null,
  3945 + null,
  3946 + true,
  3947 + wwCells,
  3948 + );
  3949 + wwOpt.style.fontWeight = "bold";
  3950 +
  3951 + // Word wrap in edge labels only supported via labelWidth style
  3952 + if (wwCells.length > 0) {
  3953 + extraPanel.appendChild(wwOpt);
  3954 + }
  3955 +
  3956 + // Delegates switch of style to formattedText action as it also convertes newlines
  3957 + var htmlOpt = this.createCellOption(
  3958 + mxResources.get("formattedText"),
  3959 + "html",
  3960 + 0,
  3961 + null,
  3962 + null,
  3963 + null,
  3964 + ui.actions.get("formattedText"),
  3965 + );
  3966 + htmlOpt.style.fontWeight = "bold";
  3967 + extraPanel.appendChild(htmlOpt);
  3968 +
  3969 + var spacingPanel = this.createPanel();
  3970 + spacingPanel.style.paddingTop = "10px";
  3971 + spacingPanel.style.paddingBottom = "28px";
  3972 + spacingPanel.style.fontWeight = "normal";
  3973 +
  3974 + var span = document.createElement("div");
  3975 + span.style.position = "absolute";
  3976 + span.style.width = "70px";
  3977 + span.style.marginTop = "0px";
  3978 + span.style.fontWeight = "bold";
  3979 + mxUtils.write(span, mxResources.get("spacing"));
  3980 + spacingPanel.appendChild(span);
  3981 +
  3982 + var topUpdate, globalUpdate, leftUpdate, bottomUpdate, rightUpdate;
  3983 + var topSpacing = this.addUnitInput(spacingPanel, "pt", 87, 52, function () {
  3984 + topUpdate.apply(this, arguments);
  3985 + });
  3986 + var globalSpacing = this.addUnitInput(
  3987 + spacingPanel,
  3988 + "pt",
  3989 + 16,
  3990 + 52,
  3991 + function () {
  3992 + globalUpdate.apply(this, arguments);
  3993 + },
  3994 + );
  3995 +
  3996 + mxUtils.br(spacingPanel);
  3997 + this.addLabel(spacingPanel, mxResources.get("top"), 87);
  3998 + this.addLabel(spacingPanel, mxResources.get("global"), 16);
  3999 + mxUtils.br(spacingPanel);
  4000 + mxUtils.br(spacingPanel);
  4001 +
  4002 + var leftSpacing = this.addUnitInput(spacingPanel, "pt", 158, 52, function () {
  4003 + leftUpdate.apply(this, arguments);
  4004 + });
  4005 + var bottomSpacing = this.addUnitInput(
  4006 + spacingPanel,
  4007 + "pt",
  4008 + 87,
  4009 + 52,
  4010 + function () {
  4011 + bottomUpdate.apply(this, arguments);
  4012 + },
  4013 + );
  4014 + var rightSpacing = this.addUnitInput(spacingPanel, "pt", 16, 52, function () {
  4015 + rightUpdate.apply(this, arguments);
  4016 + });
  4017 +
  4018 + mxUtils.br(spacingPanel);
  4019 + this.addLabel(spacingPanel, mxResources.get("left"), 158);
  4020 + this.addLabel(spacingPanel, mxResources.get("bottom"), 87);
  4021 + this.addLabel(spacingPanel, mxResources.get("right"), 16);
  4022 +
  4023 + if (!graph.cellEditor.isContentEditing()) {
  4024 + container.appendChild(extraPanel);
  4025 + container.appendChild(
  4026 + this.createRelativeOption(
  4027 + mxResources.get("opacity"),
  4028 + mxConstants.STYLE_TEXT_OPACITY,
  4029 + ),
  4030 + );
  4031 + container.appendChild(spacingPanel);
  4032 + } else {
  4033 + var selState = null;
  4034 + var lineHeightInput = null;
  4035 +
  4036 + container.appendChild(
  4037 + this.createRelativeOption(
  4038 + mxResources.get("lineheight"),
  4039 + null,
  4040 + null,
  4041 + function (input) {
  4042 + var value = input.value == "" ? 120 : parseInt(input.value);
  4043 + value = Math.max(0, isNaN(value) ? 120 : value);
  4044 +
  4045 + if (selState != null) {
  4046 + graph.cellEditor.restoreSelection(selState);
  4047 + selState = null;
  4048 + }
  4049 +
  4050 + var selectedElement = graph.getSelectedElement();
  4051 + var node = selectedElement;
  4052 +
  4053 + while (
  4054 + node != null &&
  4055 + node.nodeType != mxConstants.NODETYPE_ELEMENT
  4056 + ) {
  4057 + node = node.parentNode;
  4058 + }
  4059 +
  4060 + if (
  4061 + node != null &&
  4062 + node == graph.cellEditor.textarea &&
  4063 + graph.cellEditor.textarea.firstChild != null
  4064 + ) {
  4065 + if (graph.cellEditor.textarea.firstChild.nodeName != "P") {
  4066 + graph.cellEditor.textarea.innerHTML =
  4067 + "<p>" + graph.cellEditor.textarea.innerHTML + "</p>";
  4068 + }
  4069 +
  4070 + node = graph.cellEditor.textarea.firstChild;
  4071 + }
  4072 +
  4073 + if (
  4074 + node != null &&
  4075 + graph.cellEditor.textarea != null &&
  4076 + node != graph.cellEditor.textarea &&
  4077 + graph.cellEditor.textarea.contains(node)
  4078 + ) {
  4079 + node.style.lineHeight = value / 100;
  4080 + }
  4081 +
  4082 + input.value = value + " %";
  4083 + },
  4084 + function (input) {
  4085 + // Used in CSS handler to update current value
  4086 + lineHeightInput = input;
  4087 +
  4088 + // KNOWN: Arrow up/down clear selection text in quirks/IE 8
  4089 + // Text size via arrow button limits to 16 in IE11. Why?
  4090 + mxEvent.addListener(input, "mousedown", function () {
  4091 + if (document.activeElement == graph.cellEditor.textarea) {
  4092 + selState = graph.cellEditor.saveSelection();
  4093 + }
  4094 + });
  4095 +
  4096 + mxEvent.addListener(input, "touchstart", function () {
  4097 + if (document.activeElement == graph.cellEditor.textarea) {
  4098 + selState = graph.cellEditor.saveSelection();
  4099 + }
  4100 + });
  4101 +
  4102 + input.value = "120 %";
  4103 + },
  4104 + ),
  4105 + );
  4106 +
  4107 + var insertPanel = stylePanel.cloneNode(false);
  4108 + insertPanel.style.paddingLeft = "0px";
  4109 + var insertBtns = this.editorUi.toolbar.addItems(
  4110 + ["link", "image"],
  4111 + insertPanel,
  4112 + true,
  4113 + );
  4114 +
  4115 + var btns = [
  4116 + this.editorUi.toolbar.addButton(
  4117 + "geSprite-horizontalrule",
  4118 + mxResources.get("insertHorizontalRule"),
  4119 + function () {
  4120 + document.execCommand("inserthorizontalrule", false);
  4121 + },
  4122 + insertPanel,
  4123 + ),
  4124 + this.editorUi.toolbar.addMenuFunctionInContainer(
  4125 + insertPanel,
  4126 + "geSprite-table",
  4127 + mxResources.get("table"),
  4128 + false,
  4129 + mxUtils.bind(this, function (menu) {
  4130 + this.editorUi.menus.addInsertTableItem(menu, null, null, false);
  4131 + }),
  4132 + ),
  4133 + ];
  4134 + this.styleButtons(insertBtns);
  4135 + this.styleButtons(btns);
  4136 +
  4137 + var wrapper2 = this.createPanel();
  4138 + wrapper2.style.paddingTop = "10px";
  4139 + wrapper2.style.paddingBottom = "10px";
  4140 + wrapper2.appendChild(this.createTitle(mxResources.get("insert")));
  4141 + wrapper2.appendChild(insertPanel);
  4142 + container.appendChild(wrapper2);
  4143 +
  4144 + var tablePanel = stylePanel.cloneNode(false);
  4145 + tablePanel.style.paddingLeft = "0px";
  4146 +
  4147 + var btns = [
  4148 + this.editorUi.toolbar.addButton(
  4149 + "geSprite-insertcolumnbefore",
  4150 + mxResources.get("insertColumnBefore"),
  4151 + mxUtils.bind(this, function () {
  4152 + try {
  4153 + if (currentTable != null) {
  4154 + graph.insertColumn(
  4155 + currentTable,
  4156 + tableCell != null ? tableCell.cellIndex : 0,
  4157 + );
  4158 + }
  4159 + } catch (e) {
  4160 + this.editorUi.handleError(e);
  4161 + }
  4162 + }),
  4163 + tablePanel,
  4164 + ),
  4165 + this.editorUi.toolbar.addButton(
  4166 + "geSprite-insertcolumnafter",
  4167 + mxResources.get("insertColumnAfter"),
  4168 + mxUtils.bind(this, function () {
  4169 + try {
  4170 + if (currentTable != null) {
  4171 + graph.insertColumn(
  4172 + currentTable,
  4173 + tableCell != null ? tableCell.cellIndex + 1 : -1,
  4174 + );
  4175 + }
  4176 + } catch (e) {
  4177 + this.editorUi.handleError(e);
  4178 + }
  4179 + }),
  4180 + tablePanel,
  4181 + ),
  4182 + this.editorUi.toolbar.addButton(
  4183 + "geSprite-deletecolumn",
  4184 + mxResources.get("deleteColumn"),
  4185 + mxUtils.bind(this, function () {
  4186 + try {
  4187 + if (currentTable != null && tableCell != null) {
  4188 + graph.deleteColumn(currentTable, tableCell.cellIndex);
  4189 + }
  4190 + } catch (e) {
  4191 + this.editorUi.handleError(e);
  4192 + }
  4193 + }),
  4194 + tablePanel,
  4195 + ),
  4196 + this.editorUi.toolbar.addButton(
  4197 + "geSprite-insertrowbefore",
  4198 + mxResources.get("insertRowBefore"),
  4199 + mxUtils.bind(this, function () {
  4200 + try {
  4201 + if (currentTable != null && tableRow != null) {
  4202 + graph.insertRow(currentTable, tableRow.sectionRowIndex);
  4203 + }
  4204 + } catch (e) {
  4205 + this.editorUi.handleError(e);
  4206 + }
  4207 + }),
  4208 + tablePanel,
  4209 + ),
  4210 + this.editorUi.toolbar.addButton(
  4211 + "geSprite-insertrowafter",
  4212 + mxResources.get("insertRowAfter"),
  4213 + mxUtils.bind(this, function () {
  4214 + try {
  4215 + if (currentTable != null && tableRow != null) {
  4216 + graph.insertRow(currentTable, tableRow.sectionRowIndex + 1);
  4217 + }
  4218 + } catch (e) {
  4219 + this.editorUi.handleError(e);
  4220 + }
  4221 + }),
  4222 + tablePanel,
  4223 + ),
  4224 + this.editorUi.toolbar.addButton(
  4225 + "geSprite-deleterow",
  4226 + mxResources.get("deleteRow"),
  4227 + mxUtils.bind(this, function () {
  4228 + try {
  4229 + if (currentTable != null && tableRow != null) {
  4230 + graph.deleteRow(currentTable, tableRow.sectionRowIndex);
  4231 + }
  4232 + } catch (e) {
  4233 + this.editorUi.handleError(e);
  4234 + }
  4235 + }),
  4236 + tablePanel,
  4237 + ),
  4238 + ];
  4239 + this.styleButtons(btns);
  4240 + btns[2].style.marginRight = "10px";
  4241 +
  4242 + var wrapper3 = this.createPanel();
  4243 + wrapper3.style.paddingTop = "10px";
  4244 + wrapper3.style.paddingBottom = "10px";
  4245 + wrapper3.appendChild(this.createTitle(mxResources.get("table")));
  4246 + wrapper3.appendChild(tablePanel);
  4247 +
  4248 + var tablePanel2 = stylePanel.cloneNode(false);
  4249 + tablePanel2.style.paddingLeft = "0px";
  4250 +
  4251 + var btns = [
  4252 + this.editorUi.toolbar.addButton(
  4253 + "geSprite-strokecolor",
  4254 + mxResources.get("borderColor"),
  4255 + mxUtils.bind(this, function (evt) {
  4256 + if (currentTable != null) {
  4257 + // Converts rgb(r,g,b) values
  4258 + var color = currentTable.style.borderColor.replace(
  4259 + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
  4260 + function ($0, $1, $2, $3) {
  4261 + return (
  4262 + "#" +
  4263 + ("0" + Number($1).toString(16)).substr(-2) +
  4264 + ("0" + Number($2).toString(16)).substr(-2) +
  4265 + ("0" + Number($3).toString(16)).substr(-2)
  4266 + );
  4267 + },
  4268 + );
  4269 + this.editorUi.pickColor(color, function (newColor) {
  4270 + var targetElt =
  4271 + tableCell != null && (evt == null || !mxEvent.isShiftDown(evt))
  4272 + ? tableCell
  4273 + : currentTable;
  4274 +
  4275 + graph.processElements(targetElt, function (elt) {
  4276 + elt.style.border = null;
  4277 + });
  4278 +
  4279 + if (newColor == null || newColor == mxConstants.NONE) {
  4280 + targetElt.removeAttribute("border");
  4281 + targetElt.style.border = "";
  4282 + targetElt.style.borderCollapse = "";
  4283 + } else {
  4284 + targetElt.setAttribute("border", "1");
  4285 + targetElt.style.border = "1px solid " + newColor;
  4286 + targetElt.style.borderCollapse = "collapse";
  4287 + }
  4288 + });
  4289 + }
  4290 + }),
  4291 + tablePanel2,
  4292 + ),
  4293 + this.editorUi.toolbar.addButton(
  4294 + "geSprite-fillcolor",
  4295 + mxResources.get("backgroundColor"),
  4296 + mxUtils.bind(this, function (evt) {
  4297 + // Converts rgb(r,g,b) values
  4298 + if (currentTable != null) {
  4299 + var color = currentTable.style.backgroundColor.replace(
  4300 + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
  4301 + function ($0, $1, $2, $3) {
  4302 + return (
  4303 + "#" +
  4304 + ("0" + Number($1).toString(16)).substr(-2) +
  4305 + ("0" + Number($2).toString(16)).substr(-2) +
  4306 + ("0" + Number($3).toString(16)).substr(-2)
  4307 + );
  4308 + },
  4309 + );
  4310 + this.editorUi.pickColor(color, function (newColor) {
  4311 + var targetElt =
  4312 + tableCell != null && (evt == null || !mxEvent.isShiftDown(evt))
  4313 + ? tableCell
  4314 + : currentTable;
  4315 +
  4316 + graph.processElements(targetElt, function (elt) {
  4317 + elt.style.backgroundColor = null;
  4318 + });
  4319 +
  4320 + if (newColor == null || newColor == mxConstants.NONE) {
  4321 + targetElt.style.backgroundColor = "";
  4322 + } else {
  4323 + targetElt.style.backgroundColor = newColor;
  4324 + }
  4325 + });
  4326 + }
  4327 + }),
  4328 + tablePanel2,
  4329 + ),
  4330 + this.editorUi.toolbar.addButton(
  4331 + "geSprite-fit",
  4332 + mxResources.get("spacing"),
  4333 + function () {
  4334 + if (currentTable != null) {
  4335 + var value = currentTable.getAttribute("cellPadding") || 0;
  4336 +
  4337 + var dlg = new FilenameDialog(
  4338 + ui,
  4339 + value,
  4340 + mxResources.get("apply"),
  4341 + mxUtils.bind(this, function (newValue) {
  4342 + if (newValue != null && newValue.length > 0) {
  4343 + currentTable.setAttribute("cellPadding", newValue);
  4344 + } else {
  4345 + currentTable.removeAttribute("cellPadding");
  4346 + }
  4347 + }),
  4348 + mxResources.get("spacing"),
  4349 + );
  4350 + ui.showDialog(dlg.container, 300, 80, true, true);
  4351 + dlg.init();
  4352 + }
  4353 + },
  4354 + tablePanel2,
  4355 + ),
  4356 + this.editorUi.toolbar.addButton(
  4357 + "geSprite-left",
  4358 + mxResources.get("left"),
  4359 + function () {
  4360 + if (currentTable != null) {
  4361 + currentTable.setAttribute("align", "left");
  4362 + }
  4363 + },
  4364 + tablePanel2,
  4365 + ),
  4366 + this.editorUi.toolbar.addButton(
  4367 + "geSprite-center",
  4368 + mxResources.get("center"),
  4369 + function () {
  4370 + if (currentTable != null) {
  4371 + currentTable.setAttribute("align", "center");
  4372 + }
  4373 + },
  4374 + tablePanel2,
  4375 + ),
  4376 + this.editorUi.toolbar.addButton(
  4377 + "geSprite-right",
  4378 + mxResources.get("right"),
  4379 + function () {
  4380 + if (currentTable != null) {
  4381 + currentTable.setAttribute("align", "right");
  4382 + }
  4383 + },
  4384 + tablePanel2,
  4385 + ),
  4386 + ];
  4387 + this.styleButtons(btns);
  4388 + btns[2].style.marginRight = "10px";
  4389 +
  4390 + wrapper3.appendChild(tablePanel2);
  4391 + container.appendChild(wrapper3);
  4392 +
  4393 + tableWrapper = wrapper3;
  4394 + }
  4395 +
  4396 + function setSelected(elt, selected) {
  4397 + elt.style.backgroundImage = selected
  4398 + ? Editor.isDarkMode()
  4399 + ? "linear-gradient(rgb(0 161 241) 0px, rgb(0, 97, 146) 100%)"
  4400 + : "linear-gradient(#c5ecff 0px,#87d4fb 100%)"
  4401 + : "";
  4402 + }
  4403 +
  4404 + var listener = mxUtils.bind(this, function (sender, evt, force) {
  4405 + ss = ui.getSelectionState();
  4406 + var fontStyle = mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSTYLE, 0);
  4407 + setSelected(
  4408 + fontStyleItems[0],
  4409 + (fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD,
  4410 + );
  4411 + setSelected(
  4412 + fontStyleItems[1],
  4413 + (fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC,
  4414 + );
  4415 + setSelected(
  4416 + fontStyleItems[2],
  4417 + (fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE,
  4418 + );
  4419 + fontMenu.firstChild.nodeValue = mxUtils.getValue(
  4420 + ss.style,
  4421 + mxConstants.STYLE_FONTFAMILY,
  4422 + Menus.prototype.defaultFont,
  4423 + );
  4424 +
  4425 + setSelected(
  4426 + verticalItem,
  4427 + mxUtils.getValue(ss.style, mxConstants.STYLE_HORIZONTAL, "1") == "0",
  4428 + );
  4429 +
  4430 + if (force || document.activeElement != input) {
  4431 + var tmp = parseFloat(
  4432 + mxUtils.getValue(
  4433 + ss.style,
  4434 + mxConstants.STYLE_FONTSIZE,
  4435 + Menus.prototype.defaultFontSize,
  4436 + ),
  4437 + );
  4438 + input.value = isNaN(tmp) ? "" : tmp + " pt";
  4439 + }
  4440 +
  4441 + var align = mxUtils.getValue(
  4442 + ss.style,
  4443 + mxConstants.STYLE_ALIGN,
  4444 + mxConstants.ALIGN_CENTER,
  4445 + );
  4446 + setSelected(left, align == mxConstants.ALIGN_LEFT);
  4447 + setSelected(center, align == mxConstants.ALIGN_CENTER);
  4448 + setSelected(right, align == mxConstants.ALIGN_RIGHT);
  4449 +
  4450 + var valign = mxUtils.getValue(
  4451 + ss.style,
  4452 + mxConstants.STYLE_VERTICAL_ALIGN,
  4453 + mxConstants.ALIGN_MIDDLE,
  4454 + );
  4455 + setSelected(top, valign == mxConstants.ALIGN_TOP);
  4456 + setSelected(middle, valign == mxConstants.ALIGN_MIDDLE);
  4457 + setSelected(bottom, valign == mxConstants.ALIGN_BOTTOM);
  4458 +
  4459 + var pos = mxUtils.getValue(
  4460 + ss.style,
  4461 + mxConstants.STYLE_LABEL_POSITION,
  4462 + mxConstants.ALIGN_CENTER,
  4463 + );
  4464 + var vpos = mxUtils.getValue(
  4465 + ss.style,
  4466 + mxConstants.STYLE_VERTICAL_LABEL_POSITION,
  4467 + mxConstants.ALIGN_MIDDLE,
  4468 + );
  4469 +
  4470 + if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_TOP) {
  4471 + positionSelect.value = "topLeft";
  4472 + } else if (
  4473 + pos == mxConstants.ALIGN_CENTER &&
  4474 + vpos == mxConstants.ALIGN_TOP
  4475 + ) {
  4476 + positionSelect.value = "top";
  4477 + } else if (
  4478 + pos == mxConstants.ALIGN_RIGHT &&
  4479 + vpos == mxConstants.ALIGN_TOP
  4480 + ) {
  4481 + positionSelect.value = "topRight";
  4482 + } else if (
  4483 + pos == mxConstants.ALIGN_LEFT &&
  4484 + vpos == mxConstants.ALIGN_BOTTOM
  4485 + ) {
  4486 + positionSelect.value = "bottomLeft";
  4487 + } else if (
  4488 + pos == mxConstants.ALIGN_CENTER &&
  4489 + vpos == mxConstants.ALIGN_BOTTOM
  4490 + ) {
  4491 + positionSelect.value = "bottom";
  4492 + } else if (
  4493 + pos == mxConstants.ALIGN_RIGHT &&
  4494 + vpos == mxConstants.ALIGN_BOTTOM
  4495 + ) {
  4496 + positionSelect.value = "bottomRight";
  4497 + } else if (pos == mxConstants.ALIGN_LEFT) {
  4498 + positionSelect.value = "left";
  4499 + } else if (pos == mxConstants.ALIGN_RIGHT) {
  4500 + positionSelect.value = "right";
  4501 + } else {
  4502 + positionSelect.value = "center";
  4503 + }
  4504 +
  4505 + var dir = mxUtils.getValue(
  4506 + ss.style,
  4507 + mxConstants.STYLE_TEXT_DIRECTION,
  4508 + mxConstants.DEFAULT_TEXT_DIRECTION,
  4509 + );
  4510 +
  4511 + if (dir == mxConstants.TEXT_DIRECTION_RTL) {
  4512 + dirSelect.value = "rightToLeft";
  4513 + } else if (dir == mxConstants.TEXT_DIRECTION_LTR) {
  4514 + dirSelect.value = "leftToRight";
  4515 + } else if (dir == mxConstants.TEXT_DIRECTION_AUTO) {
  4516 + dirSelect.value = "automatic";
  4517 + }
  4518 +
  4519 + if (force || document.activeElement != globalSpacing) {
  4520 + var tmp = parseFloat(
  4521 + mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING, 2),
  4522 + );
  4523 + globalSpacing.value = isNaN(tmp) ? "" : tmp + " pt";
  4524 + }
  4525 +
  4526 + if (force || document.activeElement != topSpacing) {
  4527 + var tmp = parseFloat(
  4528 + mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_TOP, 0),
  4529 + );
  4530 + topSpacing.value = isNaN(tmp) ? "" : tmp + " pt";
  4531 + }
  4532 +
  4533 + if (force || document.activeElement != rightSpacing) {
  4534 + var tmp = parseFloat(
  4535 + mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_RIGHT, 0),
  4536 + );
  4537 + rightSpacing.value = isNaN(tmp) ? "" : tmp + " pt";
  4538 + }
  4539 +
  4540 + if (force || document.activeElement != bottomSpacing) {
  4541 + var tmp = parseFloat(
  4542 + mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_BOTTOM, 0),
  4543 + );
  4544 + bottomSpacing.value = isNaN(tmp) ? "" : tmp + " pt";
  4545 + }
  4546 +
  4547 + if (force || document.activeElement != leftSpacing) {
  4548 + var tmp = parseFloat(
  4549 + mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_LEFT, 0),
  4550 + );
  4551 + leftSpacing.value = isNaN(tmp) ? "" : tmp + " pt";
  4552 + }
  4553 + });
  4554 +
  4555 + globalUpdate = this.installInputHandler(
  4556 + globalSpacing,
  4557 + mxConstants.STYLE_SPACING,
  4558 + 2,
  4559 + -999,
  4560 + 999,
  4561 + " pt",
  4562 + );
  4563 + topUpdate = this.installInputHandler(
  4564 + topSpacing,
  4565 + mxConstants.STYLE_SPACING_TOP,
  4566 + 0,
  4567 + -999,
  4568 + 999,
  4569 + " pt",
  4570 + );
  4571 + rightUpdate = this.installInputHandler(
  4572 + rightSpacing,
  4573 + mxConstants.STYLE_SPACING_RIGHT,
  4574 + 0,
  4575 + -999,
  4576 + 999,
  4577 + " pt",
  4578 + );
  4579 + bottomUpdate = this.installInputHandler(
  4580 + bottomSpacing,
  4581 + mxConstants.STYLE_SPACING_BOTTOM,
  4582 + 0,
  4583 + -999,
  4584 + 999,
  4585 + " pt",
  4586 + );
  4587 + leftUpdate = this.installInputHandler(
  4588 + leftSpacing,
  4589 + mxConstants.STYLE_SPACING_LEFT,
  4590 + 0,
  4591 + -999,
  4592 + 999,
  4593 + " pt",
  4594 + );
  4595 +
  4596 + this.addKeyHandler(input, listener);
  4597 + this.addKeyHandler(globalSpacing, listener);
  4598 + this.addKeyHandler(topSpacing, listener);
  4599 + this.addKeyHandler(rightSpacing, listener);
  4600 + this.addKeyHandler(bottomSpacing, listener);
  4601 + this.addKeyHandler(leftSpacing, listener);
  4602 +
  4603 + graph.getModel().addListener(mxEvent.CHANGE, listener);
  4604 + this.listeners.push({
  4605 + destroy: function () {
  4606 + graph.getModel().removeListener(listener);
  4607 + },
  4608 + });
  4609 + listener();
  4610 +
  4611 + if (graph.cellEditor.isContentEditing()) {
  4612 + var updating = false;
  4613 +
  4614 + var updateCssHandler = function () {
  4615 + if (!updating) {
  4616 + updating = true;
  4617 +
  4618 + window.setTimeout(function () {
  4619 + var node = graph.getSelectedEditingElement();
  4620 +
  4621 + if (node != null) {
  4622 + function getRelativeLineHeight(fontSize, css, elt) {
  4623 + if (elt.style != null && css != null) {
  4624 + var lineHeight = css.lineHeight;
  4625 +
  4626 + if (
  4627 + elt.style.lineHeight != null &&
  4628 + elt.style.lineHeight.substring(
  4629 + elt.style.lineHeight.length - 1,
  4630 + ) == "%"
  4631 + ) {
  4632 + return parseInt(elt.style.lineHeight) / 100;
  4633 + } else {
  4634 + return lineHeight.substring(lineHeight.length - 2) == "px"
  4635 + ? parseFloat(lineHeight) / fontSize
  4636 + : parseInt(lineHeight);
  4637 + }
  4638 + } else {
  4639 + return "";
  4640 + }
  4641 + }
  4642 +
  4643 + function getAbsoluteFontSize(css) {
  4644 + var fontSize = css != null ? css.fontSize : null;
  4645 +
  4646 + if (
  4647 + fontSize != null &&
  4648 + fontSize.substring(fontSize.length - 2) == "px"
  4649 + ) {
  4650 + return parseFloat(fontSize);
  4651 + } else {
  4652 + return mxConstants.DEFAULT_FONTSIZE;
  4653 + }
  4654 + }
  4655 +
  4656 + var css = mxUtils.getCurrentStyle(node);
  4657 + var fontSize = getAbsoluteFontSize(css);
  4658 + var lineHeight = getRelativeLineHeight(fontSize, css, node);
  4659 +
  4660 + // Finds common font size
  4661 + var elts = node.getElementsByTagName("*");
  4662 +
  4663 + // IE does not support containsNode
  4664 + if (
  4665 + elts.length > 0 &&
  4666 + window.getSelection &&
  4667 + !mxClient.IS_IE &&
  4668 + !mxClient.IS_IE11
  4669 + ) {
  4670 + var selection = window.getSelection();
  4671 +
  4672 + for (var i = 0; i < elts.length; i++) {
  4673 + if (selection.containsNode(elts[i], true)) {
  4674 + temp = mxUtils.getCurrentStyle(elts[i]);
  4675 + fontSize = Math.max(getAbsoluteFontSize(temp), fontSize);
  4676 + var lh = getRelativeLineHeight(fontSize, temp, elts[i]);
  4677 +
  4678 + if (lh != lineHeight || isNaN(lh)) {
  4679 + lineHeight = "";
  4680 + }
  4681 + }
  4682 + }
  4683 + }
  4684 +
  4685 + function hasParentOrOnlyChild(name) {
  4686 + if (
  4687 + graph.getParentByName(node, name, graph.cellEditor.textarea) !=
  4688 + null
  4689 + ) {
  4690 + return true;
  4691 + } else {
  4692 + var child = node;
  4693 +
  4694 + while (child != null && child.childNodes.length == 1) {
  4695 + child = child.childNodes[0];
  4696 +
  4697 + if (child.nodeName == name) {
  4698 + return true;
  4699 + }
  4700 + }
  4701 + }
  4702 +
  4703 + return false;
  4704 + }
  4705 +
  4706 + function isEqualOrPrefixed(str, value) {
  4707 + if (str != null && value != null) {
  4708 + if (str == value) {
  4709 + return true;
  4710 + } else if (str.length > value.length + 1) {
  4711 + return (
  4712 + str.substring(str.length - value.length - 1, str.length) ==
  4713 + "-" + value
  4714 + );
  4715 + }
  4716 + }
  4717 +
  4718 + return false;
  4719 + }
  4720 +
  4721 + if (css != null) {
  4722 + setSelected(
  4723 + fontStyleItems[0],
  4724 + css.fontWeight == "bold" ||
  4725 + css.fontWeight > 400 ||
  4726 + hasParentOrOnlyChild("B") ||
  4727 + hasParentOrOnlyChild("STRONG"),
  4728 + );
  4729 + setSelected(
  4730 + fontStyleItems[1],
  4731 + css.fontStyle == "italic" ||
  4732 + hasParentOrOnlyChild("I") ||
  4733 + hasParentOrOnlyChild("EM"),
  4734 + );
  4735 + setSelected(fontStyleItems[2], hasParentOrOnlyChild("U"));
  4736 + setSelected(sup, hasParentOrOnlyChild("SUP"));
  4737 + setSelected(sub, hasParentOrOnlyChild("SUB"));
  4738 +
  4739 + if (!graph.cellEditor.isTableSelected()) {
  4740 + var align =
  4741 + graph.cellEditor.align ||
  4742 + mxUtils.getValue(
  4743 + ss.style,
  4744 + mxConstants.STYLE_ALIGN,
  4745 + mxConstants.ALIGN_CENTER,
  4746 + );
  4747 +
  4748 + if (isEqualOrPrefixed(css.textAlign, "justify")) {
  4749 + setSelected(
  4750 + full,
  4751 + isEqualOrPrefixed(css.textAlign, "justify"),
  4752 + );
  4753 + setSelected(left, false);
  4754 + setSelected(center, false);
  4755 + setSelected(right, false);
  4756 + } else {
  4757 + setSelected(full, false);
  4758 + setSelected(left, align == mxConstants.ALIGN_LEFT);
  4759 + setSelected(center, align == mxConstants.ALIGN_CENTER);
  4760 + setSelected(right, align == mxConstants.ALIGN_RIGHT);
  4761 + }
  4762 + } else {
  4763 + setSelected(full, isEqualOrPrefixed(css.textAlign, "justify"));
  4764 + setSelected(left, isEqualOrPrefixed(css.textAlign, "left"));
  4765 + setSelected(center, isEqualOrPrefixed(css.textAlign, "center"));
  4766 + setSelected(right, isEqualOrPrefixed(css.textAlign, "right"));
  4767 + }
  4768 +
  4769 + currentTable = graph.getParentByName(
  4770 + node,
  4771 + "TABLE",
  4772 + graph.cellEditor.textarea,
  4773 + );
  4774 + tableRow =
  4775 + currentTable == null
  4776 + ? null
  4777 + : graph.getParentByName(node, "TR", currentTable);
  4778 + tableCell =
  4779 + currentTable == null
  4780 + ? null
  4781 + : graph.getParentByNames(node, ["TD", "TH"], currentTable);
  4782 + tableWrapper.style.display = currentTable != null ? "" : "none";
  4783 +
  4784 + if (document.activeElement != input) {
  4785 + if (
  4786 + node.nodeName == "FONT" &&
  4787 + node.getAttribute("size") == "4" &&
  4788 + pendingFontSize != null
  4789 + ) {
  4790 + node.removeAttribute("size");
  4791 + node.style.fontSize = pendingFontSize + " pt";
  4792 + pendingFontSize = null;
  4793 + } else {
  4794 + input.value = isNaN(fontSize) ? "" : fontSize + " pt";
  4795 + }
  4796 +
  4797 + var lh = parseFloat(lineHeight);
  4798 +
  4799 + if (!isNaN(lh)) {
  4800 + lineHeightInput.value = Math.round(lh * 100) + " %";
  4801 + } else {
  4802 + lineHeightInput.value = "100 %";
  4803 + }
  4804 + }
  4805 +
  4806 + // Updates the color picker for the current font
  4807 + if (fontColorApply != null) {
  4808 + if (
  4809 + css.color == "rgba(0, 0, 0, 0)" ||
  4810 + css.color == "transparent"
  4811 + ) {
  4812 + currentFontColor = mxConstants.NONE;
  4813 + } else {
  4814 + currentFontColor = mxUtils.rgba2hex(css.color);
  4815 + }
  4816 +
  4817 + fontColorApply(currentFontColor, true);
  4818 + }
  4819 +
  4820 + if (bgColorApply != null) {
  4821 + if (
  4822 + css.backgroundColor == "rgba(0, 0, 0, 0)" ||
  4823 + css.backgroundColor == "transparent"
  4824 + ) {
  4825 + currentBgColor = mxConstants.NONE;
  4826 + } else {
  4827 + currentBgColor = mxUtils.rgba2hex(css.backgroundColor);
  4828 + }
  4829 +
  4830 + bgColorApply(currentBgColor, true);
  4831 + }
  4832 +
  4833 + // Workaround for firstChild is null or not an object
  4834 + // in the log which seems to be IE8- only / 29.01.15
  4835 + if (fontMenu.firstChild != null) {
  4836 + fontMenu.firstChild.nodeValue = Graph.stripQuotes(
  4837 + css.fontFamily,
  4838 + );
  4839 + }
  4840 + }
  4841 + }
  4842 +
  4843 + updating = false;
  4844 + }, 0);
  4845 + }
  4846 + };
  4847 +
  4848 + if (
  4849 + mxClient.IS_FF ||
  4850 + mxClient.IS_EDGE ||
  4851 + mxClient.IS_IE ||
  4852 + mxClient.IS_IE11
  4853 + ) {
  4854 + mxEvent.addListener(
  4855 + graph.cellEditor.textarea,
  4856 + "DOMSubtreeModified",
  4857 + updateCssHandler,
  4858 + );
  4859 + }
  4860 +
  4861 + mxEvent.addListener(graph.cellEditor.textarea, "input", updateCssHandler);
  4862 + mxEvent.addListener(
  4863 + graph.cellEditor.textarea,
  4864 + "touchend",
  4865 + updateCssHandler,
  4866 + );
  4867 + mxEvent.addListener(graph.cellEditor.textarea, "mouseup", updateCssHandler);
  4868 + mxEvent.addListener(graph.cellEditor.textarea, "keyup", updateCssHandler);
  4869 + this.listeners.push({
  4870 + destroy: function () {
  4871 + // No need to remove listener since textarea is destroyed after edit
  4872 + },
  4873 + });
  4874 + updateCssHandler();
  4875 + }
  4876 +
  4877 + return container;
  4878 +};
  4879 +
  4880 +/**
  4881 + * 数据绑定格式化面板
  4882 + */
  4883 +DataFormatPanel = function (format, editorUi, container) {
  4884 + BaseFormatPanel.call(this, format, editorUi, container);
  4885 + this.init();
  4886 +};
  4887 +
  4888 +mxUtils.extend(DataFormatPanel, BaseFormatPanel);
  4889 +
  4890 +/**
  4891 + * 数据绑定面板初始化
  4892 + */
  4893 +DataFormatPanel.prototype.init = function () {
  4894 + this.container.style.borderBottom = "none";
  4895 + this.addDataFont(this.container);
  4896 +};
  4897 +
  4898 +// TODO 数据绑定
  4899 +DataFormatPanel.prototype.addDataFont = function (container) {
  4900 + const ui = this.editorUi;
  4901 + const editor = ui.editor;
  4902 + const graph = editor.graph;
  4903 + const ss = ui.getSelectionState();
  4904 + const vertices = ss.vertices || []
  4905 + const sidebarInstance = ui.sidebar
  4906 + console.log(vertices)
  4907 + console.log(ui)
  4908 +
  4909 + const hasModifyNotSave = editor.status
  4910 +
  4911 + /**
  4912 + * @description 不是单一节点则不进入数据绑定
  4913 + */
  4914 + if (!isSingleNode(vertices)) return
  4915 +
  4916 + // 组态id
  4917 + const configurationId = getRequest().configurationId;
  4918 + // 当前内容页的id
  4919 + const currentPageId = ui.currentPage.node;
  4920 +
  4921 + // 图形的id
  4922 + const graphId = vertices[0].id;
  4923 +
  4924 + // 解构全局属性layui要用到的模块
  4925 + const { layer, form, jquery: $, colorpicker, upload, element } = layui;
  4926 +
  4927 + const CONTAINER_FILTER = 'containerFilter'
  4928 + $(container).addClass('layui-form').attr('lay-filter', CONTAINER_FILTER)
  4929 +
  4930 + /**
  4931 + * @description 创建面版
  4932 + */
  4933 + const createPanel = this.createPanel
  4934 +
  4935 + /**
  4936 + * @description 创建标题
  4937 + */
  4938 + const createTitle = this.createTitle
  4939 +
  4940 +
  4941 + /**
  4942 + * @description 选中节点信息来源
  4943 + * @param {string} nodeInfo.id - 节点ID
  4944 + */
  4945 + const nodeInfo = getNodeInfo(vertices)
  4946 +
  4947 +
  4948 + /**
  4949 + * @description 组织树
  4950 + * @type {*[]}
  4951 + */
  4952 + let treeList = []
  4953 +
  4954 + /**
  4955 + * @description 当前选中的组织树节点
  4956 + * @type {null | string}
  4957 + */
  4958 + let currentCheckedOrgNode = null
  4959 +
  4960 + /**
  4961 + * @description 当前节点绑定数据
  4962 + * @type {null | {act: [], event: [], dataSources: []}}
  4963 + */
  4964 + let currentNodeData = null
  4965 +
  4966 + /**
  4967 + * @description 覆盖当前节点数据
  4968 + * @param {'act' | 'dataSources' | 'event'} key
  4969 + * @param {string}
  4970 + */
  4971 + function overrideCurrentData(key, uuid = 'id', value = {}) {
  4972 + if (!currentNodeData[key]) currentNodeData[key] = []
  4973 + const index = currentNodeData[key].findIndex(item => item[uuid] === value[uuid])
  4974 + ~index ? currentNodeData[key][index] = value : currentNodeData[key].push(value)
  4975 + }
  4976 +
  4977 + /**
  4978 + * @description 保存页面信息
  4979 + */
  4980 + async function autoSaveGraphInfo() {
  4981 + if (!hasModifyNotSave) return
  4982 + ui.actions.get(
  4983 + (ui.currentFile.mode == null || !ui.currentFile.isEditable())
  4984 + ? 'saveAs'
  4985 + : 'save')
  4986 + .funct();
  4987 + }
  4988 +
  4989 + /**
  4990 + * @description 获取弹出层绑定信息
  4991 + * @param {'DOWN' | 'UP' | 'SINGLE' | 'DOUBLE' | 'DISPLAY' | 'FLASH' | 'ROTATE' | 'IMAGE'} type - 类型
  4992 + * @param {'event' | 'act' } category - 类别
  4993 + */
  4994 + function getLayerBindInfo(category, type) {
  4995 + if (currentNodeData) {
  4996 + return currentNodeData[category]?.find(item => item.type === type) || {}
  4997 + }
  4998 + return {}
  4999 + }
  5000 +
  5001 + const enumCategory = {
  5002 + ACT: 'act',
  5003 + EVENT: 'event',
  5004 + DATA_SOURCE: 'dataSources',
  5005 + }
  5006 +
  5007 + const enumInteractionType = {
  5008 + DOWN: 'DOWN',
  5009 + UP: 'UP',
  5010 + SINGLE: 'SINGLE',
  5011 + DOUBLE: 'DOUBLE',
  5012 + }
  5013 +
  5014 + const enumDynamicEffectType = {
  5015 + DISPLAY: 'DISPLAY',
  5016 + FLASH: 'FLASH',
  5017 + ROTATE: 'ROTATE',
  5018 + IMAGE: 'IMAGE',
  5019 + SWITCH: 'SWITCH',
  5020 + RUNNING: 'RUNNING'
  5021 + }
  5022 +
  5023 + const interactionList = [
  5024 + {
  5025 + label: "按下",
  5026 + type: enumInteractionType.DOWN,
  5027 + category: enumCategory.EVENT,
  5028 + },
  5029 + {
  5030 + label: "抬起",
  5031 + type: enumInteractionType.UP,
  5032 + category: enumCategory.EVENT,
  5033 + },
  5034 + {
  5035 + label: "单击",
  5036 + type: enumInteractionType.SINGLE,
  5037 + category: enumCategory.EVENT,
  5038 + },
  5039 + {
  5040 + label: "双击",
  5041 + type: enumInteractionType.DOUBLE,
  5042 + category: enumCategory.EVENT,
  5043 + },
  5044 + ];
  5045 +
  5046 + const dynamicEffectList = [
  5047 + {
  5048 + label: "闪烁",
  5049 + type: enumDynamicEffectType.FLASH,
  5050 + category: enumCategory.ACT,
  5051 + },
  5052 + {
  5053 + label: "显示/隐藏",
  5054 + type: enumDynamicEffectType.DISPLAY,
  5055 + category: enumCategory.ACT,
  5056 + },
  5057 + {
  5058 + label: "旋转",
  5059 + type: enumDynamicEffectType.ROTATE,
  5060 + category: enumCategory.ACT,
  5061 + }
  5062 + ];
  5063 +
  5064 + const enumDataSourceConst = {
  5065 + ORG_ID: 'orgId',
  5066 + DEVICE_ID: 'deviceId',
  5067 + SLAVE_DEVICE_ID: 'slaveDeviceId',
  5068 + ATTR: 'attr',
  5069 + GATEWAY: 'GATEWAY',
  5070 + ADDITIONAL: 'additional'
  5071 + }
  5072 +
  5073 + /**
  5074 + * @description 刷新页面 用于在其他位置调用更新数据面板的操作,默认是个空函数,在生成数据源面板中进行了拓展
  5075 + */
  5076 + function echoRefreshFn() {
  5077 + }
  5078 +
  5079 + /**
  5080 + * @description 用于在其他位置获取数据源绑定的数据,在生成数据源面板中进行了改写
  5081 + * @return {{orgId: string, attr: string, deviceId: string, slaveDeviceId: string}}
  5082 + */
  5083 + function getDataSourceBindValue() {
  5084 +
  5085 + }
  5086 +
  5087 +
  5088 + // 获取url的请求参数函数
  5089 + function getRequest() {
  5090 + let url = location.search; //获取url中"?"符后的字串
  5091 + let theRequest = new Object();
  5092 + if (url.indexOf("?") != -1) {
  5093 + var str = url.substring(1);
  5094 + string = str.split("&");
  5095 + for (var i = 0; i < string.length; i++) {
  5096 + theRequest[string[i].split("=")[0]] = decodeURI(string[i].split("=")[1]);
  5097 + }
  5098 + }
  5099 + return theRequest;
  5100 + }
  5101 +
  5102 + /**
  5103 + * @description 单一节点
  5104 + */
  5105 + function isSingleNode(vertices) {
  5106 + return Array.isArray(vertices) && vertices.length === 1
  5107 + }
  5108 +
  5109 + /**
  5110 + * @description 获取节点信息
  5111 + * @returns {{id: string}} - 当前选中节点源信息
  5112 + */
  5113 + function getNodeInfo(node) {
  5114 + if (isSingleNode(node)) {
  5115 + return node[0]
  5116 + }
  5117 + return {}
  5118 + }
  5119 +
  5120 + /**
  5121 + * @description 初始化节点
  5122 + */
  5123 + async function initNode() {
  5124 + const basicAttr = sidebarInstance.enumCellBasicAttribute
  5125 + const permissionKey = sidebarInstance.enumPermissionPanel
  5126 + const { LINE_CHART, BAR_CHART, DASHBOARD_CHART } = Sidebar.prototype.enumComponentType
  5127 +
  5128 + const renderMapping = {
  5129 + [permissionKey.DATA_SOURCE]: createDataSourcePanel,
  5130 + [permissionKey.LINE_CHART_EXPAND]: createChartBindPanel.bind(this, LINE_CHART),
  5131 + [permissionKey.BAR_CHART_EXPAND]: createChartBindPanel.bind(this, BAR_CHART),
  5132 + [permissionKey.DASHBOARD_CHART_EXPAND]: createChartBindPanel.bind(this, DASHBOARD_CHART),
  5133 + [permissionKey.INTERACTION]: createInteractionPanel,
  5134 + [permissionKey.DYNAMIC_EFFECT]: createDynamicEffectPanel,
  5135 + [permissionKey.VAR_IMAGE]: createVarImagePanel,
  5136 + [permissionKey.VIDEO]: createVideoBindPanel,
  5137 + [permissionKey.SWITCH_STATE_SETTING]: createSwitchStateSettingPanel,
  5138 + [permissionKey.ONLY_SINGLE_EVENT]: createParamsSettingButtonPanel,
  5139 + [permissionKey.RUNNING_AND_STOP]: createRunningAndStopPanel
  5140 + }
  5141 +
  5142 +
  5143 + function permissionRender() {
  5144 + try {
  5145 + const cell = vertices[0]
  5146 + const permission = graph.getAttributeForCell(cell, basicAttr.COMPONENT_TYPE)
  5147 + const needDisplayPanel = sidebarInstance.getComponentPermission(permission)
  5148 + for (const key of needDisplayPanel) {
  5149 + renderMapping[key]()
  5150 + }
  5151 + if (needDisplayPanel.length) createSubmitPanel()
  5152 + UseLayUi.nextTick(() => form.render())
  5153 + } catch (e) {
  5154 + throw Error('component permission setting has some problem, please check your component permission bind on "Sidebar.prototype.init" method setComponentPermission')
  5155 + }
  5156 + }
  5157 +
  5158 + permissionRender()
  5159 + await getNodeBindInfo()
  5160 + }
  5161 +
  5162 + initNode();
  5163 +
  5164 +
  5165 + /**
  5166 + * @description 获取节点绑定信息
  5167 + */
  5168 + async function getNodeBindInfo() {
  5169 + const { id } = nodeInfo
  5170 + const [err, res] = await to(ConfigurationNodeApi.getConfigurationInfo('NODE', id))
  5171 + currentNodeData = res
  5172 + if (echoRefreshFn && typeof echoRefreshFn === 'function') echoRefreshFn()
  5173 + await echoActionType()
  5174 + }
  5175 +
  5176 + /**
  5177 + * @description 回显动效类型
  5178 + */
  5179 + async function echoActionType() {
  5180 + const act = currentNodeData.act ?? []
  5181 + const event = currentNodeData.event ?? []
  5182 + const actionType = {}
  5183 +
  5184 + const hasExistEl = $(`.layui-form[lay-filter="${CONTAINER_FILTER}"]`).find('input[type="checkbox"]')
  5185 + $(hasExistEl).each((i) => {
  5186 + $(hasExistEl[i]).attr('disabled', true)
  5187 + })
  5188 +
  5189 + for (const item of act) {
  5190 + const flag = !item.condition || !item.condition?.length
  5191 + $(`.interaction__container input[name="${item.type}"]`).attr('disabled', flag)
  5192 + actionType[item.type] = item.enabled
  5193 + }
  5194 + for (const item of event) {
  5195 + const flag = !item.content
  5196 + $(`.interaction__container input[name="${item.type}"]`).attr('disabled', flag)
  5197 + actionType[item.type] = item.enabled
  5198 + }
  5199 +
  5200 + form.val(CONTAINER_FILTER, actionType)
  5201 + }
  5202 +
  5203 + /**
  5204 + * @description 生成操作节点
  5205 + * @param {object[]} list
  5206 + */
  5207 + function generateActionEventNode(list) {
  5208 + const eventList = []
  5209 + const eventNodeCls = 'interaction__container'
  5210 + for (const item of list) {
  5211 + const checkbox = UseLayUi.createCheckBox({
  5212 + dataSource: item,
  5213 + layFilter: item.type,
  5214 + valueField: 'type',
  5215 + labelField: 'label',
  5216 + })
  5217 + const template = `
  5218 + <div class="${eventNodeCls}">
  5219 + <div>${checkbox}</div>
  5220 + <i id="${item.type}"></i>
  5221 + </div>`
  5222 + eventList.push(template)
  5223 + }
  5224 + return eventList
  5225 + }
  5226 +
  5227 + /**
  5228 + * @description 创建数据面板
  5229 + */
  5230 + function createDataSourcePanel() {
  5231 + const fragment = document.createDocumentFragment()
  5232 + const defaultPanel = createPanel()
  5233 + const title = createTitle('数据源')
  5234 + $(defaultPanel).addClass('override__panel--default')
  5235 + $(title).css({ padding: '6px 0 6px 6px' })
  5236 +
  5237 +
  5238 + async function mount() {
  5239 + const { component, echoDataSource, getValue } = generateDataSourceComponent({ validate: false })
  5240 + $(fragment).append(title).append(component)
  5241 + $(container).append(fragment)
  5242 +
  5243 + /**
  5244 + * @description 回显数据
  5245 + * @type {Function}
  5246 + */
  5247 + const refreshFn = echoRefreshFn
  5248 + echoRefreshFn = function () {
  5249 + refreshFn.apply(this)
  5250 + const { dataSources: [dataSource] = [] } = currentNodeData || {}
  5251 + echoDataSource(dataSource)
  5252 + form.render(null, CONTAINER_FILTER)
  5253 + }
  5254 +
  5255 + // 改写获取数据源绑定的值
  5256 + getDataSourceBindValue = getValue
  5257 + }
  5258 +
  5259 + mount()
  5260 + }
  5261 +
  5262 + function createLineChartPanel() {
  5263 + const { LINE_CHART } = Sidebar.prototype.enumComponentType
  5264 + createChartBindPanel(true)
  5265 + }
  5266 +
  5267 + function createBarChartPanel() {
  5268 + const { LINE_CHART } = Sidebar.prototype.enumComponentType
  5269 +
  5270 + createChartBindPanel(false)
  5271 + }
  5272 +
  5273 + function createDashboardPanel() {
  5274 + const { LINE_CHART } = Sidebar.prototype.enumComponentType
  5275 +
  5276 + }
  5277 + /**
  5278 + * @description 创建视频绑定面板
  5279 + */
  5280 + function createVideoBindPanel() {
  5281 +
  5282 + const enumConst = {
  5283 + ORG_ID: 'orgId',
  5284 + RECORD_ID: 'id',
  5285 + VIDEO_URL: 'videoUrl',
  5286 + ACCESSMODE: 'accessMode',
  5287 + VIDEO_FLAG: 'videoComponentFlag'
  5288 + }
  5289 +
  5290 + const enumActionEL = {
  5291 + VIDEO_FILTER: 'videoFilter',
  5292 + PANEL_EL: 'videoPanelControl',
  5293 + ORG_EL: 'orgTreeEl'
  5294 + }
  5295 +
  5296 + const fragment = document.createDocumentFragment()
  5297 + const title = createTitle('视频绑定')
  5298 + $(title).addClass('override__title--default')
  5299 +
  5300 + const defaultPanel = createPanel()
  5301 + $(defaultPanel).addClass('override__panel--default')
  5302 + $(defaultPanel).append(`<div id="${enumActionEL.ORG_EL}" class="video-panel-org__override"></div>`)
  5303 + $(defaultPanel).append(`<div class="layui-form-item" style="display: none;"><input name="${enumConst.ACCESSMODE}" /></div>`)
  5304 + $(defaultPanel).append(`<div class="layui-form-item" style="display: none;"><input name="${enumConst.VIDEO_FLAG}" value="true" /></div>`)
  5305 + $(defaultPanel).append(`<div class="layui-form-item" style="display: none;"><input name="${enumConst.VIDEO_URL}" /></div>`)
  5306 +
  5307 + const videoBindSelect = UseLayUi.createSelect({
  5308 + label: '视频流',
  5309 + layFilter: enumActionEL.VIDEO_FILTER,
  5310 + className: 'data-source__component-select',
  5311 + bindValueFiled: enumConst.RECORD_ID
  5312 + })
  5313 + $(defaultPanel).append(`<div id="${enumActionEL.PANEL_EL}">${videoBindSelect}</div>`)
  5314 +
  5315 + fragment.append(title)
  5316 + fragment.append(defaultPanel)
  5317 + $(container).append(fragment)
  5318 +
  5319 + function init() {
  5320 +
  5321 + let orgId = null;
  5322 +
  5323 + let recordOptions = []
  5324 +
  5325 + let treeList = []
  5326 +
  5327 + const refreshFN = echoRefreshFn
  5328 + echoRefreshFn = async function () {
  5329 + refreshFN.apply(this, arguments)
  5330 + await echoData()
  5331 + }
  5332 + async function echoData() {
  5333 + const { videoUrl, orgId: organizationId, accessMode, id } = currentNodeData?.dataSources?.[0]?.additional || {}
  5334 + orgId = organizationId
  5335 + await getStreamingMediaByOrgId()
  5336 + form.val(CONTAINER_FILTER, { videoUrl, accessMode, id })
  5337 + UseLayUi.nextTick(() => {
  5338 + const node = UseLayUi.findTreeObjectByField(treeList, organizationId)
  5339 + $(`#${enumActionEL.ORG_EL} input[name="${enumConst.ORG_ID}"]`).val(organizationId).parent().find('span').html(node?.name)
  5340 + })
  5341 + }
  5342 +
  5343 +
  5344 +
  5345 + /**
  5346 + * @description 创建组织树
  5347 + */
  5348 + async function createOrgTreeSelect() {
  5349 + const [err, res] = await to(ConfigurationNodeApi.getOrgTree())
  5350 + treeList = res
  5351 + if (err) return
  5352 + UseLayUi.createTreeSelect({
  5353 + elem: `#${enumActionEL.ORG_EL}`,
  5354 + layFilter: enumConst.ORG_ID,
  5355 + label: '组织',
  5356 + singleUsage: false,
  5357 + layVerify: 'required',
  5358 + layVerType: 'tips',
  5359 + treeProps: {
  5360 + data: res,
  5361 + onlyIconControl: true,
  5362 + click(node) {
  5363 + orgId = node.data.id
  5364 + getStreamingMediaByOrgId()
  5365 + form.val(CONTAINER_FILTER, {
  5366 + [enumConst.RECORD_ID]: null
  5367 + })
  5368 + },
  5369 + },
  5370 + })
  5371 + }
  5372 +
  5373 + async function getStreamingMediaByOrgId() {
  5374 + const [err, res] = await to(ConfigurationNodeApi.getStreamingMediaList(orgId))
  5375 + recordOptions = res.items
  5376 + const options = UseLayUi.generateOptionTemplate({ dataSource: res.items || [] })
  5377 + $(`#${enumActionEL.PANEL_EL} select[name="${enumConst.RECORD_ID}"]`).html(options)
  5378 + form.render()
  5379 + }
  5380 +
  5381 + function createLinsten() {
  5382 + form.on(`select(${enumActionEL.VIDEO_FILTER})`, event => {
  5383 + const { value } = event
  5384 + const item = recordOptions.find(item => item.id === value)
  5385 + form.val(CONTAINER_FILTER, {
  5386 + [enumConst.ACCESSMODE]: item[enumConst.ACCESSMODE],
  5387 + [enumConst.VIDEO_URL]: item[enumConst.VIDEO_URL],
  5388 + })
  5389 + })
  5390 + }
  5391 +
  5392 + createOrgTreeSelect()
  5393 + createLinsten()
  5394 + }
  5395 +
  5396 + init()
  5397 + }
  5398 +
  5399 + /**
  5400 + * @description 创建开关组件的状态设置面板
  5401 + */
  5402 + function createSwitchStateSettingPanel() {
  5403 + dynamicEffectList.length = 0
  5404 + dynamicEffectList.push({
  5405 + label: '状态设置',
  5406 + type: enumDynamicEffectType.SWITCH,
  5407 + category: enumCategory.ACT,
  5408 + })
  5409 + createDynamicEffectPanel()
  5410 + createParamsSettingButtonPanel()
  5411 + }
  5412 +
  5413 + /**
  5414 + * @description 参数设置按钮面板创建
  5415 + */
  5416 + function createParamsSettingButtonPanel() {
  5417 + const singleClick = interactionList.find(item => item.type === enumInteractionType.SINGLE)
  5418 + interactionList.length = 0
  5419 + interactionList.push(singleClick)
  5420 + createInteractionPanel()
  5421 + }
  5422 +
  5423 + /**
  5424 + * @description 水流动效
  5425 + */
  5426 + function createRunningAndStopPanel() {
  5427 + dynamicEffectList.push({
  5428 + label: '水流动效',
  5429 + type: enumDynamicEffectType.RUNNING,
  5430 + category: enumCategory.ACT
  5431 + })
  5432 + }
  5433 +
  5434 + /**
  5435 + * @description 是否是折线图
  5436 + * @param {boolean} isLineChart
  5437 + */
  5438 + function createChartBindPanel(chartType) {
  5439 + const fragment = document.createDocumentFragment()
  5440 + const chartBindContainer = document.createElement('div')
  5441 + chartBindContainer.setAttribute('class', 'chart-panel__style')
  5442 + chartBindContainer.style.padding = '10px'
  5443 + const title = createTitle('展示类型')
  5444 + $(title).addClass('override__title--default')
  5445 +
  5446 + const enumActionEL = {
  5447 + SCOPE_FILTER: 'bindChartScopeFilter',
  5448 +
  5449 + INTERVAL_EL: 'bindChartIntervalSelect',
  5450 +
  5451 + UNIT_EL: 'bindChartUnit'
  5452 + }
  5453 + const { LINE_CHART, BAR_CHART, DASHBOARD_CHART } = Sidebar.prototype.enumComponentType
  5454 +
  5455 + const enumBindKey = HandleDataSource.enumConst
  5456 + const enumDataType = HandleDataSource.enumDataBindType
  5457 + const enumConst = {
  5458 + DATA_TYPE: enumBindKey.DATA_TYPE,
  5459 +
  5460 + REAL: enumDataType.REAL,
  5461 +
  5462 + HISTORY: enumDataType.HISTORY,
  5463 +
  5464 + /**
  5465 + * @description 范围
  5466 + */
  5467 + EFFECT_SCOPE: enumBindKey.EFFECT_SCOPE,
  5468 +
  5469 + /**
  5470 + * @description 聚合
  5471 + */
  5472 + AGG: enumBindKey.AGG,
  5473 +
  5474 + /**
  5475 + * @description 间隔
  5476 + */
  5477 + INTERVAL: enumBindKey.INTERVAL,
  5478 +
  5479 + /**
  5480 + * @description 单位
  5481 + */
  5482 + UNIT: enumBindKey.UNIT
  5483 + }
  5484 +
  5485 + const enumTimeUnit = {
  5486 + SECOND: 'second',
  5487 + MINUTE: 'MINUTE',
  5488 + HOUR: 'HOUR',
  5489 + DAY: 'DAY',
  5490 + }
  5491 +
  5492 + const unitMapping = {
  5493 + [enumTimeUnit.SECOND]: '秒',
  5494 + [enumTimeUnit.MINUTE]: '分',
  5495 + [enumTimeUnit.HOUR]: '小时',
  5496 + [enumTimeUnit.DAY]: '天',
  5497 + }
  5498 +
  5499 + const unitConversion = {
  5500 + [enumTimeUnit.SECOND]: 1 * 1000,
  5501 + [enumTimeUnit.MINUTE]: 1 * 60 * 1000,
  5502 + [enumTimeUnit.HOUR]: 1 * 60 * 60 * 1000,
  5503 + [enumTimeUnit.DAY]: 1 * 60 * 60 * 24 * 1000,
  5504 + }
  5505 +
  5506 + const defaultIntervalOptions = [
  5507 + {
  5508 + id: 1,
  5509 + unit: enumTimeUnit.SECOND,
  5510 + linkage: [
  5511 + { id: 1, unit: enumTimeUnit.SECOND }
  5512 + ]
  5513 + },
  5514 + {
  5515 + id: 5,
  5516 + unit: enumTimeUnit.SECOND,
  5517 + linkage: [
  5518 + { id: 1, unit: enumTimeUnit.SECOND }
  5519 + ]
  5520 + },
  5521 + {
  5522 + id: 10,
  5523 + unit: enumTimeUnit.SECOND,
  5524 + linkage: [
  5525 + { id: 1, unit: enumTimeUnit.SECOND }
  5526 + ]
  5527 + },
  5528 + {
  5529 + id: 15,
  5530 + unit: enumTimeUnit.SECOND,
  5531 + linkage: [
  5532 + { id: 1, unit: enumTimeUnit.SECOND }
  5533 + ]
  5534 + },
  5535 + {
  5536 + id: 30,
  5537 + unit: enumTimeUnit.SECOND,
  5538 + linkage: [
  5539 + { id: 1, unit: enumTimeUnit.SECOND }
  5540 + ]
  5541 + },
  5542 + {
  5543 + id: 1,
  5544 + unit: enumTimeUnit.MINUTE,
  5545 + linkage: [
  5546 + { id: 1, unit: enumTimeUnit.SECOND },
  5547 + { id: 5, unit: enumTimeUnit.SECOND },
  5548 + ]
  5549 + },
  5550 + {
  5551 + id: 2,
  5552 + unit: enumTimeUnit.MINUTE,
  5553 + linkage: [
  5554 + { id: 1, unit: enumTimeUnit.SECOND },
  5555 + { id: 5, unit: enumTimeUnit.SECOND },
  5556 + { id: 10, unit: enumTimeUnit.SECOND },
  5557 + { id: 15, unit: enumTimeUnit.SECOND },
  5558 + ]
  5559 + },
  5560 + {
  5561 + id: 5,
  5562 + unit: enumTimeUnit.MINUTE,
  5563 + linkage: [
  5564 + { id: 1, unit: enumTimeUnit.SECOND },
  5565 + { id: 5, unit: enumTimeUnit.SECOND },
  5566 + { id: 10, unit: enumTimeUnit.SECOND },
  5567 + { id: 15, unit: enumTimeUnit.SECOND },
  5568 + { id: 30, unit: enumTimeUnit.SECOND },
  5569 + ]
  5570 + },
  5571 + {
  5572 + id: 10,
  5573 + unit: enumTimeUnit.MINUTE,
  5574 + linkage: [
  5575 + { id: 5, unit: enumTimeUnit.SECOND },
  5576 + { id: 10, unit: enumTimeUnit.SECOND },
  5577 + { id: 15, unit: enumTimeUnit.SECOND },
  5578 + { id: 30, unit: enumTimeUnit.SECOND },
  5579 + { id: 1, unit: enumTimeUnit.MINUTE },
  5580 + ]
  5581 + },
  5582 + {
  5583 + id: 15,
  5584 + unit: enumTimeUnit.MINUTE,
  5585 + linkage: [
  5586 + { id: 5, unit: enumTimeUnit.SECOND },
  5587 + { id: 10, unit: enumTimeUnit.SECOND },
  5588 + { id: 15, unit: enumTimeUnit.SECOND },
  5589 + { id: 30, unit: enumTimeUnit.SECOND },
  5590 + { id: 1, unit: enumTimeUnit.MINUTE },
  5591 + { id: 2, unit: enumTimeUnit.MINUTE },
  5592 + ]
  5593 + },
  5594 + {
  5595 + id: 30,
  5596 + unit: enumTimeUnit.MINUTE,
  5597 + linkage: [
  5598 + { id: 5, unit: enumTimeUnit.SECOND },
  5599 + { id: 10, unit: enumTimeUnit.SECOND },
  5600 + { id: 15, unit: enumTimeUnit.SECOND },
  5601 + { id: 30, unit: enumTimeUnit.SECOND },
  5602 + { id: 1, unit: enumTimeUnit.MINUTE },
  5603 + { id: 2, unit: enumTimeUnit.MINUTE },
  5604 + ]
  5605 + },
  5606 + {
  5607 + id: 1,
  5608 + unit: enumTimeUnit.HOUR,
  5609 + linkage: [
  5610 + { id: 10, unit: enumTimeUnit.SECOND },
  5611 + { id: 15, unit: enumTimeUnit.SECOND },
  5612 + { id: 30, unit: enumTimeUnit.SECOND },
  5613 + { id: 1, unit: enumTimeUnit.MINUTE },
  5614 + { id: 2, unit: enumTimeUnit.MINUTE },
  5615 + { id: 5, unit: enumTimeUnit.MINUTE },
  5616 + ]
  5617 + },
  5618 + {
  5619 + id: 2,
  5620 + unit: enumTimeUnit.HOUR,
  5621 + linkage: [
  5622 + { id: 15, unit: enumTimeUnit.SECOND },
  5623 + { id: 30, unit: enumTimeUnit.SECOND },
  5624 + { id: 1, unit: enumTimeUnit.MINUTE },
  5625 + { id: 2, unit: enumTimeUnit.MINUTE },
  5626 + { id: 5, unit: enumTimeUnit.MINUTE },
  5627 + { id: 10, unit: enumTimeUnit.MINUTE },
  5628 + { id: 15, unit: enumTimeUnit.MINUTE },
  5629 + ]
  5630 + },
  5631 + {
  5632 + id: 5,
  5633 + unit: enumTimeUnit.HOUR,
  5634 + linkage: [
  5635 + { id: 1, unit: enumTimeUnit.MINUTE },
  5636 + { id: 2, unit: enumTimeUnit.MINUTE },
  5637 + { id: 5, unit: enumTimeUnit.MINUTE },
  5638 + { id: 10, unit: enumTimeUnit.MINUTE },
  5639 + { id: 15, unit: enumTimeUnit.MINUTE },
  5640 + { id: 30, unit: enumTimeUnit.MINUTE },
  5641 + ]
  5642 + },
  5643 + {
  5644 + id: 10,
  5645 + unit: enumTimeUnit.HOUR,
  5646 + linkage: [
  5647 + { id: 2, unit: enumTimeUnit.MINUTE },
  5648 + { id: 5, unit: enumTimeUnit.MINUTE },
  5649 + { id: 10, unit: enumTimeUnit.MINUTE },
  5650 + { id: 15, unit: enumTimeUnit.MINUTE },
  5651 + { id: 30, unit: enumTimeUnit.MINUTE },
  5652 + { id: 1, unit: enumTimeUnit.HOUR },
  5653 + ]
  5654 + },
  5655 + {
  5656 + id: 12,
  5657 + unit: enumTimeUnit.HOUR,
  5658 + linkage: [
  5659 + { id: 2, unit: enumTimeUnit.MINUTE },
  5660 + { id: 5, unit: enumTimeUnit.MINUTE },
  5661 + { id: 10, unit: enumTimeUnit.MINUTE },
  5662 + { id: 15, unit: enumTimeUnit.MINUTE },
  5663 + { id: 30, unit: enumTimeUnit.MINUTE },
  5664 + { id: 1, unit: enumTimeUnit.HOUR },
  5665 + ]
  5666 + },
  5667 + {
  5668 + id: 1,
  5669 + unit: enumTimeUnit.DAY,
  5670 + linkage: [
  5671 + { id: 5, unit: enumTimeUnit.MINUTE },
  5672 + { id: 10, unit: enumTimeUnit.MINUTE },
  5673 + { id: 15, unit: enumTimeUnit.MINUTE },
  5674 + { id: 30, unit: enumTimeUnit.MINUTE },
  5675 + { id: 1, unit: enumTimeUnit.HOUR },
  5676 + { id: 2, unit: enumTimeUnit.HOUR },
  5677 + ]
  5678 + },
  5679 + {
  5680 + id: 7,
  5681 + unit: enumTimeUnit.DAY,
  5682 + linkage: [
  5683 + { id: 30, unit: enumTimeUnit.MINUTE },
  5684 + { id: 1, unit: enumTimeUnit.HOUR },
  5685 + { id: 2, unit: enumTimeUnit.HOUR },
  5686 + { id: 5, unit: enumTimeUnit.HOUR },
  5687 + { id: 10, unit: enumTimeUnit.HOUR },
  5688 + { id: 12, unit: enumTimeUnit.HOUR },
  5689 + { id: 1, unit: enumTimeUnit.DAY },
  5690 + ]
  5691 + },
  5692 + {
  5693 + id: 30,
  5694 + unit: enumTimeUnit.DAY,
  5695 + linkage: [
  5696 + { id: 2, unit: enumTimeUnit.HOUR },
  5697 + { id: 5, unit: enumTimeUnit.HOUR },
  5698 + { id: 10, unit: enumTimeUnit.HOUR },
  5699 + { id: 12, unit: enumTimeUnit.HOUR },
  5700 + { id: 1, unit: enumTimeUnit.DAY },
  5701 + ]
  5702 + },
  5703 + ].map(item => {
  5704 + return {
  5705 + id: item.id * unitConversion[item.unit],
  5706 + name: item.id + unitMapping[item.unit],
  5707 + linkage: item.linkage.map(item => {
  5708 + return {
  5709 + id: item.id * unitConversion[item.unit],
  5710 + name: item.id + unitMapping[item.unit],
  5711 + }
  5712 + })
  5713 + }
  5714 + })
  5715 +
  5716 + const aggergationOptions = [
  5717 + { id: 'AVG', name: '平均值' },
  5718 + { id: 'MIN', name: '最小值' },
  5719 + { id: 'MAX', name: '最大值' },
  5720 + { id: 'SUM', name: '求和' },
  5721 + { id: 'COUNT', name: '计数' },
  5722 + { id: 'NONE', name: '空' },
  5723 + ]
  5724 +
  5725 + function generateDataType() {
  5726 + const realOption = (checked) => `<input type="radio" name="${enumConst.DATA_TYPE}" value="${enumConst.REAL}" title="实时" ${checked ? 'checked' : ''}>`
  5727 + const historyOption = (checked) => `<input type="radio" name="${enumConst.DATA_TYPE}" value="${enumConst.HISTORY}" title="历史" ${checked ? 'checked' : ''}>`
  5728 +
  5729 + if (chartType === LINE_CHART) {
  5730 + return realOption() + historyOption(true)
  5731 + } else if (chartType === BAR_CHART) {
  5732 + return historyOption(true)
  5733 + } else if (chartType === DASHBOARD_CHART) {
  5734 + return realOption(true)
  5735 + }
  5736 + return realOption() + historyOption(true)
  5737 + }
  5738 +
  5739 + function createSwitchBindTypeRadio() {
  5740 + return `
  5741 + <div class="layui-form-item">
  5742 + <label class="layui-form-label">数据类型</label>
  5743 + <div class="layui-input-block">
  5744 + ${generateDataType()}
  5745 + </div>
  5746 + </div>`
  5747 + }
  5748 +
  5749 + function createQueryScopeSelect() {
  5750 + const template = UseLayUi.generateOptionTemplate({
  5751 + dataSource: defaultIntervalOptions,
  5752 + addPlaceholderOption: false
  5753 + })
  5754 + return `
  5755 + <div class="layui-form-item">
  5756 + <label class="layui-form-label">时间周期</label>
  5757 + <div class="layui-input-block">
  5758 + <select name="${enumConst.EFFECT_SCOPE}" lay-filter="${enumActionEL.SCOPE_FILTER}">
  5759 + ${template}
  5760 + </select>
  5761 + </div>
  5762 + </div>`
  5763 + }
  5764 +
  5765 + function createAggregationSelect() {
  5766 + const template = UseLayUi.generateOptionTemplate({
  5767 + dataSource: aggergationOptions,
  5768 + addPlaceholderOption: false
  5769 + })
  5770 + return `
  5771 + <div class="layui-form-item">
  5772 + <label class="layui-form-label">聚合方式</label>
  5773 + <div class="layui-input-block">
  5774 + <select name="${enumConst.AGG}">
  5775 + ${template}
  5776 + </select>
  5777 + </div>
  5778 + </div>`
  5779 + }
  5780 +
  5781 + function createIntervalSelect() {
  5782 + return `
  5783 + <div class="layui-form-item">
  5784 + <label class="layui-form-label">间隔时间</label>
  5785 + <div class="layui-input-block">
  5786 + <select id="${enumActionEL.INTERVAL_EL}" name="${enumConst.INTERVAL}" lay-filter="${enumConst.INTERVAL}">
  5787 + <option value="1000">1</option>
  5788 + </select>
  5789 + </div>
  5790 + </div>`
  5791 + }
  5792 +
  5793 + function createUnitInput() {
  5794 + return `
  5795 + <div class="layui-form-item">
  5796 + <label class="layui-form-label">单位</label>
  5797 + <div class="layui-input-block" style="flex: 1;">
  5798 + <input id="${enumActionEL.UNIT_EL}" class="layui-input" value="°C" name="${enumConst.UNIT}" lay-filter="${enumConst.UNIT}" />
  5799 + </div>
  5800 + </div>`
  5801 + }
  5802 +
  5803 + function generatorEventlisten() {
  5804 + form.on(`select(${enumActionEL.SCOPE_FILTER})`, event => {
  5805 + const value = event.value || 0
  5806 + linkageIntervalSelect(value)
  5807 + })
  5808 + }
  5809 +
  5810 + function linkageIntervalSelect(value) {
  5811 + const options = defaultIntervalOptions.find(item => Number(item.id) === Number(value))?.linkage
  5812 + const template = UseLayUi.generateOptionTemplate({
  5813 + dataSource: options,
  5814 + addPlaceholderOption: false
  5815 + })
  5816 + $(`#${enumActionEL.INTERVAL_EL}`).html(template)
  5817 + form.render('select')
  5818 + }
  5819 +
  5820 + /**
  5821 + * @description 回显
  5822 + */
  5823 + function echoData() {
  5824 + const { dataSources = [] } = currentNodeData
  5825 + const { additional = {} } = dataSources[0] || {}
  5826 + const { [enumBindKey.EFFECT_SCOPE]: effectScope = 1000 } = additional
  5827 + linkageIntervalSelect(effectScope)
  5828 + form.val(CONTAINER_FILTER, additional)
  5829 + }
  5830 +
  5831 + const refresh = echoRefreshFn
  5832 + echoRefreshFn = function () {
  5833 + refresh.apply(this, arguments)
  5834 + echoData()
  5835 + }
  5836 +
  5837 + fragment.append(title)
  5838 + $(chartBindContainer).append(createSwitchBindTypeRadio())
  5839 + $(chartBindContainer).append(createQueryScopeSelect())
  5840 + $(chartBindContainer).append(createAggregationSelect())
  5841 + $(chartBindContainer).append(createIntervalSelect())
  5842 + chartType === DASHBOARD_CHART && $(chartBindContainer).append(createUnitInput())
  5843 + fragment.append(chartBindContainer)
  5844 + $(container).append(fragment)
  5845 + generatorEventlisten()
  5846 + }
  5847 +
  5848 + /**
  5849 + * @description 创建数据交互模块
  5850 + */
  5851 + function createInteractionPanel() {
  5852 + const fragment = document.createDocumentFragment()
  5853 + const title = createTitle('数据交互')
  5854 + $(title).addClass('override__title--default')
  5855 + fragment.append(title)
  5856 + generateActionEventNode(interactionList).forEach(item => {
  5857 + const panel = createPanel()
  5858 + $(panel).addClass('override__panel--default').append(item)
  5859 + $(fragment).append(panel)
  5860 + })
  5861 + $(container).append(fragment)
  5862 + }
  5863 +
  5864 +
  5865 + /**
  5866 + * @description 创建动效面版
  5867 + */
  5868 + function createDynamicEffectPanel() {
  5869 + const fragment = document.createDocumentFragment()
  5870 + const title = createTitle('数据动效')
  5871 + $(title).addClass('override__title--default')
  5872 + fragment.append(title)
  5873 + generateActionEventNode(dynamicEffectList).forEach(item => {
  5874 + const panel = createPanel()
  5875 + $(panel).addClass('override__panel--default').append(item)
  5876 + $(fragment).append(panel)
  5877 + })
  5878 + $(container).append(fragment)
  5879 + }
  5880 +
  5881 + /**
  5882 + * @description 变量图片动效面板
  5883 + */
  5884 + function createVarImagePanel() {
  5885 + dynamicEffectList.push({
  5886 + label: '设置变量值图片',
  5887 + type: enumDynamicEffectType.IMAGE,
  5888 + category: enumCategory.ACT,
  5889 + })
  5890 + createDynamicEffectPanel()
  5891 + }
  5892 +
  5893 + /**
  5894 + * @description 创建提交按钮
  5895 + */
  5896 + function createSubmitPanel() {
  5897 + const panel = createPanel()
  5898 + $(panel).addClass('data-source__submit-panel').append(`<button type="button" lay-submit lay-filter="formDataSource" class="layui-btn">保存</button>`)
  5899 + $(container).append(panel)
  5900 + form.on('submit(formDataSource)', async function (data) {
  5901 + if (!hasSavePermission()) {
  5902 + UseLayUi.topErrorMsg('没有权限')
  5903 + return
  5904 + }
  5905 + const { field } = data
  5906 + const value = getValueOnSubmit(field)
  5907 + await to(autoSaveGraphInfo())
  5908 + const [err, res] = await to(ConfigurationNodeApi.updateNodeInfo(value))
  5909 + if (err) return
  5910 + UseLayUi.successMsg()
  5911 + await getNodeBindInfo()
  5912 + return false;
  5913 + });
  5914 + }
  5915 +
  5916 + function getValueOnSubmit(field) {
  5917 + const basicAttr = sidebarInstance.enumCellBasicAttribute
  5918 + const componentType = sidebarInstance.enumComponentType
  5919 +
  5920 + const renderMapping = {
  5921 + [componentType.VAR_IMAGE]: getSubmitValue,
  5922 + [componentType.CHARTS]: getSubmitValue,
  5923 + [componentType.TITLE]: getSubmitValue,
  5924 + [componentType.VARIABLE]: getSubmitValue,
  5925 + [componentType.LINE]: getSubmitValue,
  5926 + [componentType.REAL_TIME]: getSubmitValue,
  5927 + [componentType.LINE_CHART]: getSubmitValue,
  5928 + [componentType.BAR_CHART]: getSubmitValue,
  5929 + [componentType.DASHBOARD_CHART]: getSubmitValue,
  5930 + [componentType.DEFAULT]: getSubmitValue,
  5931 + [componentType.VIDEO]: getVideoSubmitValue,
  5932 + [componentType.SWITCH]: getSwitchSubmitValue,
  5933 + [componentType.PARAMS_SETTING_BUTTON]: getSwitchSubmitValue,
  5934 + [componentType.IMAGE]: getSubmitValue
  5935 + }
  5936 +
  5937 + const cell = vertices[0]
  5938 + const type = graph.getAttributeForCell(cell, basicAttr.COMPONENT_TYPE)
  5939 + return renderMapping[type]?.(field) || {}
  5940 +
  5941 + function getSubmitValue(field) {
  5942 + const ENABLED_FLAG = 'on'
  5943 + const additionalKey = HandleDataSource.enumConst
  5944 +
  5945 + const value = {
  5946 + configurationId,
  5947 + contentId: currentPageId.id,
  5948 + nodeId: graphId,
  5949 + [enumCategory.ACT]: [],
  5950 + [enumCategory.EVENT]: [],
  5951 + [enumCategory.DATA_SOURCE]: {
  5952 + [enumDataSourceConst.ORG_ID]: field[enumDataSourceConst.ORG_ID],
  5953 + [enumDataSourceConst.DEVICE_ID]: field[enumDataSourceConst.DEVICE_ID],
  5954 + [enumDataSourceConst.SLAVE_DEVICE_ID]: field[enumDataSourceConst.SLAVE_DEVICE_ID] ? field[enumDataSourceConst.SLAVE_DEVICE_ID] : '',
  5955 + [enumDataSourceConst.ATTR]: field[enumDataSourceConst.ATTR],
  5956 + [enumDataSourceConst.ADDITIONAL]: field[additionalKey.DATA_TYPE] ? {
  5957 + [additionalKey.AGG]: field[additionalKey.AGG],
  5958 + [additionalKey.DATA_TYPE]: field[additionalKey.DATA_TYPE],
  5959 + [additionalKey.INTERVAL]: field[additionalKey.INTERVAL],
  5960 + [additionalKey.EFFECT_SCOPE]: field[additionalKey.EFFECT_SCOPE],
  5961 + ...(field[additionalKey.UNIT] ? { [additionalKey.UNIT]: field[additionalKey.UNIT] } : {})
  5962 + } : null
  5963 + },
  5964 + }
  5965 + const allType = [...interactionList, ...dynamicEffectList]
  5966 + for (const item of allType) {
  5967 + if (field[item.type] === ENABLED_FLAG) {
  5968 + const enableItem = currentNodeData[item.category].find(each => each.type === item.type)
  5969 + if (!enableItem) continue
  5970 + value[item.category].push({
  5971 + type: item.type,
  5972 + enabled: true,
  5973 + })
  5974 + } else {
  5975 + value[item.category].push({
  5976 + type: item.type,
  5977 + enabled: false,
  5978 + })
  5979 + }
  5980 + }
  5981 +
  5982 + return value
  5983 + }
  5984 +
  5985 + function getVideoSubmitValue() {
  5986 + const value = {
  5987 + configurationId,
  5988 + contentId: currentPageId.id,
  5989 + nodeId: graphId,
  5990 + [enumCategory.DATA_SOURCE]: {
  5991 + [enumDataSourceConst.ORG_ID]: 'b4dd6e2b-6e0f-413c-bf5a-70133bd571e8',
  5992 + [enumDataSourceConst.DEVICE_ID]: '6d9043f0-f1f7-11ec-98ad-a9680487d1e0',
  5993 + [enumDataSourceConst.ATTR]: 'attr',
  5994 + [enumDataSourceConst.ADDITIONAL]: field
  5995 + },
  5996 + }
  5997 + return value
  5998 + }
  5999 +
  6000 + /**
  6001 + * @description 处理开关组件的保存值
  6002 + * @returns
  6003 + */
  6004 + function getSwitchSubmitValue(field) {
  6005 + const dataSources = getDataSourceBindValue()
  6006 + const enableStatus = getEnableStatus(field)
  6007 + return { configurationId, contentId: currentPageId.id, nodeId: graphId, dataSources, ...enableStatus }
  6008 + }
  6009 +
  6010 +
  6011 + /**
  6012 + * @description 获取开启状态
  6013 + */
  6014 + function getEnableStatus(field = {}) {
  6015 + const ENABLED_FLAG = 'on'
  6016 + const value = {}
  6017 + const hasExistEl = $(`.layui-form[lay-filter="${CONTAINER_FILTER}"]`).find('input[type="checkbox"]')
  6018 +
  6019 + // 筛选页面中存在的数据动效事件交互复选框
  6020 + const allState = []
  6021 + $(hasExistEl).each((i) => {
  6022 + const state = $(hasExistEl[i]).attr('name')
  6023 + allState.push(state)
  6024 + })
  6025 + const allType = [...interactionList, ...dynamicEffectList].filter(item => allState.includes(item.type))
  6026 +
  6027 + for (const item of allType) {
  6028 + !value[item.category] && (value[item.category] = [])
  6029 + value[item.category].push({
  6030 + type: item.type,
  6031 + enabled: field[item.type] === ENABLED_FLAG,
  6032 + })
  6033 + }
  6034 + return value
  6035 + }
  6036 + }
  6037 +
  6038 + /**
  6039 + * @description 处理设置变量图片
  6040 + */
  6041 + function handleSettingVarImage(event) {
  6042 +
  6043 + const enumActionEl = {
  6044 + /**
  6045 + * @description layer 保存按钮
  6046 + */
  6047 + LAYER_SUBMIT_FILTER: 'varImageLayerFilter',
  6048 +
  6049 + /**
  6050 + * @description 上传图片layer 按钮控制
  6051 + */
  6052 + IMAGE_LAYER_FILTER: 'uploadImageLayerFilter',
  6053 +
  6054 + /**
  6055 + * @description 行控制
  6056 + */
  6057 + ROW_FILTER: 'varImageTableRowFilter',
  6058 +
  6059 + /**
  6060 + * @description 颜色控制
  6061 + */
  6062 + COLOR_PICKER_FILTER: 'varImageTableColorPickerFilter',
  6063 +
  6064 + /**
  6065 + * @description 最小值控制
  6066 + */
  6067 + MIN_FILTER: 'varImageTableMinFilter',
  6068 +
  6069 + /**
  6070 + * @description 最大值控制
  6071 + */
  6072 + MAX_FILTER: 'varImageTableMaxFilter',
  6073 +
  6074 + /**
  6075 + * @description 表体
  6076 + */
  6077 + TABLE_BODY_EL: 'variableImageTable',
  6078 +
  6079 + /**
  6080 + * @description 添加行控制
  6081 + */
  6082 + ADD_ROW_EL: 'variableImageTableAddRow',
  6083 +
  6084 + /**
  6085 + * @description 设置图片控制
  6086 + */
  6087 + SET_IMG_EL: 'variableImageTableSetImgEl',
  6088 +
  6089 + /**
  6090 + * @description 预览区域控制
  6091 + */
  6092 + PREVIEW_IMG_CONTAINER: 'img__container',
  6093 +
  6094 + /**
  6095 + * @description 删除图片控制
  6096 + */
  6097 + DEL_PREVIEW_IMG: 'img__delete',
  6098 +
  6099 + /**
  6100 + * @description 删除行控制
  6101 + */
  6102 + DEL_ROW_EL: 'variableImageTableDelRow',
  6103 +
  6104 + /**
  6105 + * @description
  6106 + */
  6107 + DATA_SOURCE_COMP_EL: 'varImgDataSourceEl',
  6108 +
  6109 + /**
  6110 + * @description 默认图片
  6111 + */
  6112 + DEFAULT_IMAGE_EL: 'defaultImageEl',
  6113 +
  6114 + /**
  6115 + * @description 行预览
  6116 + */
  6117 + ROW_PREVIEW_IMG: 'rowPreviewImg',
  6118 +
  6119 + /**
  6120 + * @description 默认图片filter
  6121 + */
  6122 + DEFAULT_IMAGE_FILTER: 'defaultImageFilter'
  6123 + }
  6124 +
  6125 + const enumConst = HandleDynamicEffect.enumVarImageConst
  6126 +
  6127 + let addRowNumber = 0
  6128 +
  6129 + let getDataSourceValue = () => {
  6130 + }
  6131 +
  6132 + const getRowFilter = (rowNumber) => `${enumActionEl.ROW_FILTER}${rowNumber}`
  6133 +
  6134 + /**
  6135 + * @description 监听表格中的事件
  6136 + */
  6137 + function generatorEventListen() {
  6138 + createInputListener()
  6139 + createAddRowEvent()
  6140 + createDelRowEvent()
  6141 + }
  6142 +
  6143 + /**
  6144 + * @description 删除行
  6145 + */
  6146 + function createDelRowEvent() {
  6147 + $(`#${enumActionEl.TABLE_BODY_EL}`).on('click', `.${enumActionEl.DEL_ROW_EL}`, (event) => {
  6148 + $(event.target).parents('tr').remove()
  6149 + })
  6150 + }
  6151 +
  6152 + /**
  6153 + * @description 添加行事件
  6154 + */
  6155 + function createAddRowEvent() {
  6156 + $(`#${enumActionEl.ADD_ROW_EL}`).on('click', () => {
  6157 + addRecord()
  6158 + })
  6159 + }
  6160 +
  6161 + /**
  6162 + * @description 创建输入验证
  6163 + */
  6164 + function createInputListener() {
  6165 + $(`#${enumActionEl.TABLE_BODY_EL}`)
  6166 + .on('input', `.${enumActionEl.MIN_FILTER}`, event => {
  6167 + const minVal = $(event.target).val()
  6168 + const maxVal = $(event.target).parents('tr').find(`input.${enumActionEl.MAX_FILTER}`).val()
  6169 + if (maxVal !== '' && Number(minVal) > Number(maxVal)) {
  6170 + layer.tips('输入值大于最大值', $(event.target), {
  6171 + tips: 1,
  6172 + });
  6173 + }
  6174 + })
  6175 + .on('input', `.${enumActionEl.MAX_FILTER}`, event => {
  6176 + const maxVal = $(event.target).val()
  6177 + const minVal = $(event.target).parents('tr').find(`input.${enumActionEl.MIN_FILTER}`).val()
  6178 + if (minVal !== '' && Number(maxVal) < Number(minVal)) {
  6179 + layer.tips('输入值小于最小值', $(event.target), {
  6180 + tips: 1,
  6181 + });
  6182 + }
  6183 + })
  6184 + }
  6185 +
  6186 + /**
  6187 + * @description 添加记录
  6188 + */
  6189 + function addRecord() {
  6190 + const rowFormFilter = getRowFilter(addRowNumber)
  6191 + const content = `
  6192 + <tr class="layui-form" lay-filter="${rowFormFilter}">
  6193 + <td>
  6194 + <div class="layui-form-item">
  6195 + <input class="layui-input ${enumActionEl.MIN_FILTER}" autocomplete="off" type="number" name="${enumConst.MIN}" lay-filter="${enumConst.MIN}" lay-verType="tips" lay-verify="required" />
  6196 + </div>
  6197 + </td>
  6198 + <td>
  6199 + <div class="layui-form-item">
  6200 + <input class="layui-input ${enumActionEl.MAX_FILTER}"" autocomplete="off" type="number" name="${enumConst.MAX}" lay-filter="${enumConst.MAX}" lay-verType="tips" lay-verify="required" />
  6201 + </div>
  6202 + </td>
  6203 + <td>
  6204 + <div class="${enumActionEl.ROW_PREVIEW_IMG}"></div>
  6205 + </td>
  6206 + <td style="text-align: center">
  6207 + <button type="button" class="layui-btn layui-btn-primary layui-border-red ${enumActionEl.DEL_ROW_EL}">删除</button>
  6208 + </td>
  6209 + </tr>
  6210 + `
  6211 + $(`#${enumActionEl.TABLE_BODY_EL}`).append(content)
  6212 + form.render()
  6213 + const el = $(`#${enumActionEl.TABLE_BODY_EL} tr[lay-filter="${rowFormFilter}"] .${enumActionEl.ROW_PREVIEW_IMG}`)
  6214 + generateUploadImgContainer({ el })
  6215 + addRowNumber++
  6216 + }
  6217 +
  6218 +
  6219 + /**
  6220 + * @description 回显数据
  6221 + */
  6222 + function echoFormData(info) {
  6223 + const { condition = [] } = info
  6224 + for (let index = 0; index < condition.length; index++) {
  6225 + const item = condition[index]
  6226 + if (item[enumConst.DEFAULT_IMAGE_FLAG]) continue
  6227 + addRecord()
  6228 + const rowFilter = getRowFilter(index)
  6229 + $(`#${enumActionEl.TABLE_BODY_EL}`).find(`tr[lay-filter="${rowFilter}"] .${enumActionEl.PREVIEW_IMG_CONTAINER}>img`).attr('src', item[enumConst.IMAGE_GALLERY_IMAGE_PATH])
  6230 + form.val(rowFilter, { ...item })
  6231 + }
  6232 + }
  6233 +
  6234 + function echoDefaultImage(info) {
  6235 + const { condition } = info
  6236 + const data = condition.find(item => item[enumConst.DEFAULT_IMAGE_FLAG])
  6237 + if (data) {
  6238 + $(`#${enumActionEl.DEFAULT_IMAGE_EL}`).find(`.${enumActionEl.PREVIEW_IMG_CONTAINER}>img`).attr('src', data[enumConst.IMAGE_GALLERY_IMAGE_PATH])
  6239 + form.val(enumActionEl.DEFAULT_IMAGE_FILTER, { ...data })
  6240 + }
  6241 + }
  6242 +
  6243 + /**
  6244 + * @description 验证最大最小值
  6245 + * @param tableData
  6246 + * @returns {boolean}
  6247 + */
  6248 + function validate(tableData) {
  6249 + let validateFlag = true
  6250 + for (let i = 0; i < tableData.length; i++) {
  6251 + const { min, max } = tableData[i]
  6252 + if (Number(min) > Number(max)) {
  6253 + validateFlag = false
  6254 + layer.tips('输入值大于最大值', $(`#${enumActionEl.TABLE_BODY_EL} tr`).eq(i).find(`input[name="${enumConst.MIN}"]`))
  6255 + break
  6256 + } else if (Number(max) < Number(min)) {
  6257 + validateFlag = false
  6258 + layer.tips('输入值小于最小值', $(`#${enumActionEl.TABLE_BODY_EL} tr`).eq(i).find(`input[name="${enumConst.MAX}"]`))
  6259 + break
  6260 + }
  6261 + }
  6262 + return validateFlag
  6263 + }
  6264 +
  6265 + /**
  6266 + * @description
  6267 + */
  6268 + async function submit(callback) {
  6269 + if (!hasPermission()) return
  6270 + const data = Array.from({ length: addRowNumber }).map((_, row) => form.val(getRowFilter(row))).filter(item => Object.keys(item).length)
  6271 + const defaultImageConfig = form.val(enumActionEl.DEFAULT_IMAGE_FILTER)
  6272 + defaultImageConfig[enumConst.DEFAULT_IMAGE_FLAG] = true
  6273 + data.push(defaultImageConfig)
  6274 + if (!validate(data)) return
  6275 + const formVal = getDataSourceValue()
  6276 + const formModel = {
  6277 + configurationId,
  6278 + contentId: currentPageId.id,
  6279 + ...formVal,
  6280 + id: graphId,
  6281 + condition: data,
  6282 + type: event.data.type,
  6283 + }
  6284 + await to(autoSaveGraphInfo())
  6285 + const [err, res] = await to(ConfigurationNodeApi.updateNodeAct(formModel))
  6286 + if (err) return
  6287 + UseLayUi.successMsg()
  6288 + callback()
  6289 + }
  6290 +
  6291 + function createLayerForm(type) {
  6292 + const content = `
  6293 + <div class="layui-form" lay-filter="${enumActionEl.DEFAULT_IMAGE_FILTER}" style="display: flex; align-items: center; padding-top: 10px;">
  6294 + <div style="width: 60px; padding: 9px 10px 9px 20px;">默认图片</div>
  6295 + <div id="${enumActionEl.DEFAULT_IMAGE_EL}" style="padding: 0;"></div>
  6296 + </div>
  6297 + <div style="width: 300px; padding: 0 0 0 20px;" id="${enumActionEl.DATA_SOURCE_COMP_EL}"></div>
  6298 + <div class="override__table" style="padding: 0 20px">
  6299 + <table class="layui-table" >
  6300 + <thead>
  6301 + <tr>
  6302 + <th style="text-align:center">最小值(>=)</th>
  6303 + <th style="text-align:center">最大值(<=)</th>
  6304 + <th style="text-align:center">对应图片</th>
  6305 + <th style="text-align:center">操作</th>
  6306 + </tr>
  6307 + </thead>
  6308 + <tbody id="${enumActionEl.TABLE_BODY_EL}"></tbody>
  6309 + </table>
  6310 + </div>
  6311 + <div style="display:flex;justify-content:center;">
  6312 + <button type="button" class="layui-btn layui-btn-primary layui-border-blue" id="${enumActionEl.ADD_ROW_EL}">添加一条</button>
  6313 + </div>
  6314 + `
  6315 +
  6316 + layer.open({
  6317 + title: '变量值图片功能',
  6318 + type: 1,
  6319 + content,
  6320 + skin: 'event-layer__override',
  6321 + area: '700px',
  6322 + btn: ["保存", "取消"],
  6323 + shade: ["0.7", "#fafafa"],
  6324 + offset: '100px',
  6325 + yes(index) {
  6326 + form.on(`submit(${enumActionEl.LAYER_SUBMIT_FILTER})`, data => {
  6327 + submit(() => {
  6328 + layer.close(index)
  6329 + getNodeBindInfo()
  6330 + })
  6331 + })
  6332 + },
  6333 + but2(index, layero) {
  6334 + return false
  6335 + },
  6336 + async success(layero, index) {
  6337 + $(layero).addClass('layui-form').find('.layui-layer-btn0').attr({
  6338 + 'lay-submit': '',
  6339 + 'lay-filter': enumActionEl.LAYER_SUBMIT_FILTER,
  6340 + })
  6341 + const { component, echoDataSource, getValue } = generateDataSourceComponent()
  6342 + getDataSourceValue = getValue
  6343 +
  6344 + const el = $(`#${enumActionEl.DEFAULT_IMAGE_EL}`)
  6345 + generateUploadImgContainer({ el })
  6346 +
  6347 + $(`#${enumActionEl.DATA_SOURCE_COMP_EL}`).append(component)
  6348 + form.render()
  6349 + const info = getLayerBindInfo('act', type)
  6350 + const { condition = [] } = info
  6351 + if (!condition.length) {
  6352 + addRecord()
  6353 + } else {
  6354 + echoDataSource(info)
  6355 + echoFormData(info)
  6356 + echoDefaultImage(info)
  6357 + }
  6358 + generatorEventListen()
  6359 + },
  6360 + })
  6361 + }
  6362 +
  6363 + createLayerForm(event.data.type)
  6364 + }
  6365 +
  6366 + /**
  6367 + * @description 处理数据交互 - down || up 按下与抬起
  6368 + */
  6369 + function handleDownOrUpEvent(event) {
  6370 +
  6371 + // 事件类型
  6372 + const enumEventType = {
  6373 + DOWN: "按下",
  6374 + UP: "抬起",
  6375 + SINGLE: "单击",
  6376 + DOUBLE: "双击",
  6377 + };
  6378 +
  6379 + const enumActionEl = {
  6380 +
  6381 + /**
  6382 + * @description 表单控制 filter
  6383 + */
  6384 + FORM_FILTER: 'interactionFormFilter',
  6385 +
  6386 + /**
  6387 + * @description 表体节点
  6388 + */
  6389 + DEVICE_DATA_BODY_EL: 'deviceDataTbody',
  6390 +
  6391 + /**
  6392 + * @description 删除行节点
  6393 + */
  6394 + DEL_ROW_EL: 'deleteRow',
  6395 +
  6396 + /**
  6397 + * @description 增加行节点
  6398 + */
  6399 + ADD_ROW_EL: 'addRow',
  6400 +
  6401 + /**
  6402 + * @descripton
  6403 + */
  6404 + ROW_FILTER: 'interactionRowFilter',
  6405 +
  6406 + /**
  6407 + * @description json editor
  6408 + */
  6409 + EDITOR_JSON: 'editor__json-',
  6410 +
  6411 + /**
  6412 + * @description 弹出层保存 filter
  6413 + */
  6414 + LAYER_SUBMIT_FILTER: 'interactionLayerSubmit',
  6415 +
  6416 + /**
  6417 + * @description ace builder
  6418 + */
  6419 + JSON: 'ace__builder-json',
  6420 +
  6421 + /**
  6422 + * @description 组织树节点
  6423 + */
  6424 + ORG_EL: 'interactionOrgTree',
  6425 + }
  6426 +
  6427 +
  6428 + /**
  6429 + * @description 所有设备选项
  6430 + * @type {{id: string, name: string, deviceType: string}[]}
  6431 + */
  6432 + let allDeviceOptions = []
  6433 +
  6434 + let addRowNumber = 0
  6435 +
  6436 + /**
  6437 + * @description 组织id
  6438 + * @type {null}
  6439 + */
  6440 + let orgId = null
  6441 +
  6442 + const recordData = {
  6443 + orgId: null,
  6444 + enabled: false
  6445 + }
  6446 +
  6447 + const getRowFilter = (rowNumber) => `${enumActionEl.ROW_FILTER}${rowNumber}`
  6448 +
  6449 + /**
  6450 + * @description 枚举常量
  6451 + * @enum DEVICE
  6452 + */
  6453 + const enumConst = {
  6454 + /**
  6455 + * @description 设备
  6456 + */
  6457 + DEVICE: 'deviceId',
  6458 + /**
  6459 + * @description 子设备
  6460 + */
  6461 + SLAVE_DEVICE: 'slaveDeviceId',
  6462 + /**
  6463 + * @description 变量
  6464 + */
  6465 + ATTR: 'attr',
  6466 + /**
  6467 + * @description 下发值
  6468 + */
  6469 + VALUE: 'value',
  6470 +
  6471 + /**
  6472 + * @description 方式
  6473 + */
  6474 + WAY: 'way',
  6475 +
  6476 + /**
  6477 + * @description 组织id
  6478 + */
  6479 + ORG_ID: 'orgId',
  6480 + }
  6481 +
  6482 + const sendInstructionWay = {
  6483 + ONE_WAY: 'oneway',
  6484 + TWO_WAY: 'twoway',
  6485 + }
  6486 +
  6487 + /**
  6488 + * @description 创建回显数据 查询出所有网关设备和直连设备
  6489 + */
  6490 + async function getAllGatewayDeviceAndConnectionDevice() {
  6491 + if (!recordData.orgId) return
  6492 + const [err, res] = await to(ConfigurationNodeApi.getAllGatewayDeviceAndConnectionDevice(recordData.orgId))
  6493 + allDeviceOptions = res
  6494 + mountAllDeviceToSelect()
  6495 + }
  6496 +
  6497 + /**
  6498 + * @description 将设备选项挂载到节点中
  6499 + */
  6500 + function mountAllDeviceToSelect() {
  6501 + const generateOption = UseLayUi.generateOptionTemplate({ dataSource: allDeviceOptions })
  6502 + $(`#${enumActionEl.DEVICE_DATA_BODY_EL}`).find(`select[name="${enumConst.DEVICE}"]`).html(generateOption)
  6503 + UseLayUi.nextTick(() => form.render('select'))
  6504 + }
  6505 +
  6506 + /**
  6507 + * @description 创建JSON编辑器
  6508 + * @param el
  6509 + * @param datum
  6510 + */
  6511 + function createEditor(el, datum = {}) {
  6512 + const editor = ace.edit(el, {
  6513 + maxLines: 10, // 最大行数,超过会自动出现滚动条
  6514 + minLines: 5, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小
  6515 + fontSize: 14, // 编辑器内字体大小
  6516 + tabSize: 2, // 制表符设置为 4 个空格大小
  6517 + });
  6518 + editor.session.setMode("ace/mode/json");
  6519 + editor.getSession().on('change', (event, editor) => {
  6520 + $(`#${el}`).parent().find(`textarea[name="${enumConst.VALUE}"]`).val(editor.getValue())
  6521 + })
  6522 + editor.setValue(datum[enumConst.VALUE] ? datum[enumConst.VALUE] : "{}")
  6523 + }
  6524 +
  6525 + /**
  6526 + * @description 创建组织树
  6527 + */
  6528 + async function createOrgTreeSelect() {
  6529 + const [err, res] = await to(ConfigurationNodeApi.getOrgTree())
  6530 + treeList = res
  6531 + if (err) return
  6532 + UseLayUi.createTreeSelect({
  6533 + elem: `#${enumActionEl.ORG_EL}`,
  6534 + layFilter: enumConst.ORG_ID,
  6535 + label: '组织',
  6536 + singleUsage: false,
  6537 + layVerify: 'required',
  6538 + layVerType: 'tips',
  6539 + treeProps: {
  6540 + data: res,
  6541 + onlyIconControl: true,
  6542 + click(node) {
  6543 + recordData.orgId = node.data.id
  6544 + getAllGatewayDeviceAndConnectionDevice()
  6545 + },
  6546 + },
  6547 + })
  6548 + }
  6549 +
  6550 +
  6551 +
  6552 + /**
  6553 + * @description 添加行
  6554 + * @param datum
  6555 + */
  6556 + function addRecord(datum) {
  6557 + const content = `
  6558 + <tr class="layui-form" lay-filter="${getRowFilter(addRowNumber)}">
  6559 + <td>
  6560 + <select name="${enumConst.DEVICE}" lay-filter="${enumConst.DEVICE}" lay-verType="tips" lay-verify="required"></select>
  6561 + </td>
  6562 + <td>
  6563 + <form action="" style="display: flex">
  6564 + <div class="override__radio-default">
  6565 + <input id="${getRowFilter(addRowNumber)}${sendInstructionWay.ONE_WAY}" type="radio" name="${enumConst.WAY}" lay-ignore value="${sendInstructionWay.ONE_WAY}" title="单向" checked="">
  6566 + <label for="${getRowFilter(addRowNumber)}${sendInstructionWay.ONE_WAY}" class="override__radio-label">单向</label>
  6567 + </div>
  6568 + <div class="override__radio-default">
  6569 + <input id="${getRowFilter(addRowNumber)}${sendInstructionWay.TWO_WAY}" type="radio" name="${enumConst.WAY}" lay-ignore value="${sendInstructionWay.TWO_WAY}" title="双向">
  6570 + <label for="${getRowFilter(addRowNumber)}${sendInstructionWay.TWO_WAY}" class="override__radio-label">双向</label>
  6571 + </div>
  6572 + </form>
  6573 + </td>
  6574 + <td class="${enumActionEl.JSON}">
  6575 + <div style="width: 100%;height: 100%;">
  6576 + <div id="${enumActionEl.EDITOR_JSON}${addRowNumber}"></div>
  6577 + <textarea style="display: none" name="${enumConst.VALUE}" placeholder="请输入命令" lay-verType="tips" class="layui-textarea"></textarea>
  6578 + </div>
  6579 + </td>
  6580 + <td>
  6581 + <button type="button" class="layui-btn layui-btn-primary layui-border-red ${enumActionEl.DEL_ROW_EL}">删除</button>
  6582 + </td>
  6583 + </tr>
  6584 + `
  6585 + $(`#${enumActionEl.DEVICE_DATA_BODY_EL}`).append(content)
  6586 + setDeviceOptions(addRowNumber)
  6587 + createEditor(`${enumActionEl.EDITOR_JSON}${addRowNumber}`, datum)
  6588 + form.render('select', getRowFilter(addRowNumber))
  6589 + addRowNumber++
  6590 + }
  6591 +
  6592 +
  6593 + /**
  6594 + * @description 删除行
  6595 + */
  6596 + function createDeleteRowListenEvent() {
  6597 + $(`#${enumActionEl.DEVICE_DATA_BODY_EL}`).on('click', `.${enumActionEl.DEL_ROW_EL}`, (event) => {
  6598 + // $(event.target).parents('tr').find('editor')
  6599 + $(event.target).parents('tr').remove()
  6600 + })
  6601 + }
  6602 +
  6603 + /**
  6604 + * @description 新增行
  6605 + */
  6606 + function createAddRowListenEvent() {
  6607 + $(`#${enumActionEl.ADD_ROW_EL}`).on('click', () => {
  6608 + addRecord()
  6609 + const generateOption = UseLayUi.generateOptionTemplate({ dataSource: allDeviceOptions })
  6610 + $(`#${enumActionEl.DEVICE_DATA_BODY_EL} tr`).last().find(`select[name="${enumConst.DEVICE}"]`).html(generateOption)
  6611 + form.render('select')
  6612 + })
  6613 + }
  6614 +
  6615 + /**
  6616 + * @description 设置设备选项
  6617 + */
  6618 + function setDeviceOptions(row) {
  6619 + const generateOption = UseLayUi.generateOptionTemplate({ dataSource: allDeviceOptions })
  6620 + $(`#${enumActionEl.DEVICE_DATA_BODY_EL} tr[lay-filter="${getRowFilter(row)}"]`).find(`select[name="${enumConst.DEVICE}"]`).html(generateOption)
  6621 + }
  6622 +
  6623 + /**
  6624 + * @description 下拉选项挂载
  6625 + */
  6626 + function createSelectOptionMountEvent() {
  6627 + $(`#${enumActionEl.DEVICE_DATA_BODY_EL}`).on('click', '.layui-form-select', event => {
  6628 + const width = event.currentTarget.offsetWidth || 200
  6629 + const height = event.currentTarget.offsetHeight || 38
  6630 + const offset = $(event.currentTarget).offset()
  6631 + $(event.currentTarget).find('dl').css({
  6632 + position: 'fixed',
  6633 + 'min-width': width + 'px',
  6634 + top: offset.top + height + 'px',
  6635 + left: offset.left + 'px'
  6636 + })
  6637 + })
  6638 + }
  6639 +
  6640 + /**
  6641 + * @description 生成事件监听
  6642 + */
  6643 + function generatorEventListen() {
  6644 + createDeleteRowListenEvent()
  6645 + createAddRowListenEvent()
  6646 + createSelectOptionMountEvent()
  6647 + }
  6648 +
  6649 + /**
  6650 + * @description 回显表单数据
  6651 + */
  6652 + function echoFormData(info) {
  6653 + const { content: { data = [] } = {} } = info
  6654 + data.forEach((datum, index) => {
  6655 + addRecord(datum)
  6656 + form.val(getRowFilter(index), datum)
  6657 + })
  6658 + }
  6659 +
  6660 + /**
  6661 + * @description 回显组织树
  6662 + */
  6663 + function echoOrgTree(id) {
  6664 + recordData.orgId = id
  6665 + const node = UseLayUi.findTreeObjectByField(treeList, id)
  6666 + UseLayUi.nextTick(() => {
  6667 + $(`#${enumActionEl.ORG_EL} input[name="${enumConst.ORG_ID}"]`).val(recordData.orgId).parent().find('span').html(node?.name)
  6668 + })
  6669 + }
  6670 +
  6671 + /**
  6672 + * @description 判断是否为正确的JSON结构
  6673 + * @param {string} string
  6674 + * @returns
  6675 + */
  6676 + function isJson(string) {
  6677 + if (typeof string === 'string') {
  6678 + try {
  6679 + const obj = JSON.parse(string)
  6680 + if (typeof obj === 'object' && obj !== null) {
  6681 + return true
  6682 + }
  6683 + } catch (e) {
  6684 + return false
  6685 + }
  6686 + }
  6687 + return false
  6688 + }
  6689 +
  6690 + /**
  6691 + * @description 表单验证
  6692 + * @param tableData
  6693 + * @returns {boolean}
  6694 + */
  6695 + function validate(tableData) {
  6696 + let validateFlag = true
  6697 +
  6698 + const formModel = form.val(enumActionEl.FORM_FILTER)
  6699 + if (!formModel[enumConst.ORG_ID]) return false
  6700 +
  6701 + for (let i = 0; i < tableData.length; i++) {
  6702 + const { value } = tableData[i]
  6703 + if (!isJson(value)) {
  6704 + validateFlag = false
  6705 + layer.tips('下发值不正确', $(`#${enumActionEl.DEVICE_DATA_BODY_EL} tr`).eq(i).find(`td.${enumActionEl.JSON}`), { tips: 1 })
  6706 + break
  6707 + }
  6708 + }
  6709 + return validateFlag
  6710 + }
  6711 +
  6712 +
  6713 +
  6714 + /**
  6715 + * @description 保存
  6716 + */
  6717 + async function submit(callback) {
  6718 + if (!hasPermission()) return
  6719 + const data = Array.from({ length: addRowNumber }).map((_, row) => form.val(getRowFilter(row))).filter(item => Object.keys(item).length)
  6720 + if (!validate(data)) return
  6721 + const formModal = {
  6722 + ...recordData,
  6723 + configurationId,
  6724 + // orgId,
  6725 + contentId: currentPageId.id,
  6726 + id: graphId,
  6727 + content: {
  6728 + data,
  6729 + },
  6730 + type: event.data.type,
  6731 + };
  6732 + await to(autoSaveGraphInfo())
  6733 + const [err, res] = await to(ConfigurationNodeApi.updateNodeEvent(formModal))
  6734 + if (err) return
  6735 + UseLayUi.successMsg()
  6736 + callback()
  6737 + }
  6738 +
  6739 + /**
  6740 + * @description 创建弹出层表单
  6741 + * @param event
  6742 + */
  6743 + function createLayerForm(type) {
  6744 + const content = `
  6745 + <form class="layui-form" lay-filter="${enumActionEl.FORM_FILTER}">
  6746 + <div style="width:400px">
  6747 + <div class="layui-form-item">
  6748 + <label class="layui-form-label">事件</label>
  6749 + <div class="layui-input-block">
  6750 + <input type="text" name="event" class="layui-input" value="${enumEventType[type]}" disabled>
  6751 + </div>
  6752 + </div>
  6753 + <div class="layui-form-item">
  6754 + <label class="layui-form-label">动作</label>
  6755 + <div class="layui-input-block">
  6756 + <select name="action" lay-verify="required">
  6757 + <option value="0" selected>给变量赋值</option>
  6758 + </select>
  6759 + </div>
  6760 + </div>
  6761 + <div class="layui-form-item">
  6762 + <label class="layui-form-label">类型</label>
  6763 + <div class="layui-input-block">
  6764 + <input type="radio" name="type" value="1" title="联网设备" checked>
  6765 + <input type="radio" name="type" value="2" title="产品/场景" disabled>
  6766 + </div>
  6767 + </div>
  6768 + <div id="${enumActionEl.ORG_EL}"></div>
  6769 + </div>
  6770 + </form>
  6771 + <div class="override__table">
  6772 + <table class="layui-table" >
  6773 + <thead>
  6774 + <tr>
  6775 + <th style="text-align:center">选择设备</th>
  6776 + <th style="text-align:center">单向 / 双向</th>
  6777 + <th style="text-align:center">下发值</th>
  6778 + <th style="text-align:center">操作</th>
  6779 + </tr>
  6780 + </thead>
  6781 + <tbody id="${enumActionEl.DEVICE_DATA_BODY_EL}"></tbody>
  6782 + </table>
  6783 + <div style="display:flex;justify-content:center;">
  6784 + <button type="button" class="layui-btn layui-btn-primary layui-border-blue" id="${enumActionEl.ADD_ROW_EL}">添加一条</button>
  6785 + </div>
  6786 + </div>`
  6787 +
  6788 + layer.open({
  6789 + title: '创建交互',
  6790 + content,
  6791 + skin: 'event-layer__override',
  6792 + // area: ["1100px", "700px"],
  6793 + area: '1100px',
  6794 + offset: '100px',
  6795 + btn: ["保存", "取消"],
  6796 + shade: ["0.7", "#fafafa"],
  6797 + yes(index) {
  6798 + form.on(`submit(${enumActionEl.LAYER_SUBMIT_FILTER})`, data => {
  6799 + submit(() => {
  6800 + layer.close(index)
  6801 + getNodeBindInfo()
  6802 + })
  6803 + })
  6804 + },
  6805 + but2(index, layero) {
  6806 + return false
  6807 + },
  6808 + async success(layero, index) {
  6809 + $(layero).addClass('layui-form').find('.layui-layer-btn0').attr({
  6810 + 'lay-submit': '',
  6811 + 'lay-filter': enumActionEl.LAYER_SUBMIT_FILTER,
  6812 + })
  6813 + await createOrgTreeSelect()
  6814 + const info = getLayerBindInfo('event', type)
  6815 + const { content: { data = [] } = {}, orgId, enabled } = info
  6816 + Object.assign(recordData, { orgId, enabled })
  6817 + await getAllGatewayDeviceAndConnectionDevice()
  6818 + if (!info || !data.length) {
  6819 + addRecord()
  6820 + } else {
  6821 + echoFormData(info)
  6822 + }
  6823 + generatorEventListen()
  6824 + form.render()
  6825 + echoOrgTree(orgId)
  6826 + },
  6827 + })
  6828 + }
  6829 +
  6830 + createLayerForm(event.data.type)
  6831 + }
  6832 +
  6833 + /**
  6834 + * @description 处理单击 or 双击事件
  6835 + */
  6836 + function handleClickOrDbClick(event) {
  6837 + const basicAttr = sidebarInstance.enumCellBasicAttribute
  6838 + const componentType = sidebarInstance.enumComponentType
  6839 + const currentNodeType = nodeInfo.getAttribute(basicAttr.COMPONENT_TYPE)
  6840 + const isControlComponent = [componentType.SWITCH, componentType.PARAMS_SETTING_BUTTON].includes(currentNodeType)
  6841 +
  6842 + const enumEventType = {
  6843 + DOWN: "按下",
  6844 + UP: "抬起",
  6845 + SINGLE: "单击",
  6846 + DOUBLE: "双击",
  6847 + };
  6848 +
  6849 + /**
  6850 + * @description
  6851 + */
  6852 + const enumConst = {
  6853 + EVENT: 'event',
  6854 + ACTION: 'actionType',
  6855 + PAGE_VALUE: 'pageValue',
  6856 + LINK_VALUE: 'linkValue',
  6857 +
  6858 + /**
  6859 + * @description 下发指令
  6860 + */
  6861 + COMMAND: 'command',
  6862 +
  6863 + /**
  6864 + * @description 属性占位符
  6865 + */
  6866 + ATTR_PLACEHOLDER: 'attrPlaceholder',
  6867 +
  6868 + WAY: 'way'
  6869 + }
  6870 +
  6871 + /**
  6872 + * @description 动作类型
  6873 + */
  6874 + const enumActionType = DispatchCenter.enumPageType
  6875 +
  6876 + const enumGetValue = {
  6877 + [enumActionType.PAGE]: enumConst.PAGE_VALUE,
  6878 + [enumActionType.LINK]: enumConst.LINK_VALUE,
  6879 + }
  6880 +
  6881 + const enumWayType = {
  6882 + ONE_WAY: 'oneway',
  6883 + TWO_WAY: 'twoway'
  6884 + }
  6885 +
  6886 + const enumActionEl = {
  6887 + /**
  6888 + * @description 表单 lay-filter
  6889 + */
  6890 + FORM_FILTER: 'formModelFilter',
  6891 +
  6892 + /**
  6893 + * @description 动作 lay-filter
  6894 + */
  6895 + ACTION_SELECT_FILTER: 'actionSelectFilter',
  6896 +
  6897 + /**
  6898 + * @description 链接 ID
  6899 + */
  6900 + LINK_EL_ID: 'staticlinkPage',
  6901 +
  6902 + /**
  6903 + * @description 页面 ID
  6904 + */
  6905 + PAGE_EL_ID: 'dynamicInputPage',
  6906 +
  6907 + /**
  6908 + * @description layer submit filter
  6909 + */
  6910 + LAYER_SUBMIT_FILTER: 'dynamicLinkLayerFilter',
  6911 +
  6912 + /**
  6913 + * @description JSON 编辑器容器
  6914 + */
  6915 + EDITOR_CONTAINER: 'editorContainerEL',
  6916 +
  6917 + /**
  6918 + * @description JSON 编辑器
  6919 + */
  6920 + EDITOR: 'EDITOR',
  6921 +
  6922 + WAY_SELECT: 'dynamicWaySelectEl'
  6923 + }
  6924 +
  6925 + /**
  6926 + * @description
  6927 + */
  6928 + const recordData = {
  6929 + enabled: false
  6930 + }
  6931 +
  6932 + /**
  6933 + * @description 回显数据
  6934 + */
  6935 + function echoFormData(data) {
  6936 + const { content = {} } = data
  6937 + const val = {
  6938 + [enumConst.ACTION]: content.type,
  6939 + [enumGetValue[content.type]]: content.value,
  6940 + [enumConst.WAY]: content[enumConst.WAY]
  6941 + }
  6942 + controlFormDisplay(content.type)
  6943 + form.val(enumActionEl.FORM_FILTER, val)
  6944 + }
  6945 +
  6946 + /**
  6947 + * @description 控制form
  6948 + * @param {enumActionType} value
  6949 + */
  6950 + function controlFormDisplay(value) {
  6951 + if (value === enumActionType.PAGE) {
  6952 + $(`#${enumActionEl.LINK_EL_ID}`).css({ display: 'none' })
  6953 + $(`#${enumActionEl.PAGE_EL_ID}`).css({ display: 'block' })
  6954 + $(`#${enumActionEl.EDITOR_CONTAINER}`).css({ display: 'none' })
  6955 + $(`#${enumActionEl.WAY_SELECT}`).css({ display: 'none' })
  6956 + } else if (value === enumActionType.LINK) {
  6957 + $(`#${enumActionEl.PAGE_EL_ID}`).css({ display: 'none' })
  6958 + $(`#${enumActionEl.LINK_EL_ID}`).css({ display: 'block' })
  6959 + $(`#${enumActionEl.EDITOR_CONTAINER}`).css({ display: 'none' })
  6960 + $(`#${enumActionEl.WAY_SELECT}`).css({ display: 'none' })
  6961 + } else if (value === enumActionType.PARAMS_SETTING) {
  6962 + $(`#${enumActionEl.LINK_EL_ID}`).css({ display: 'none' })
  6963 + $(`#${enumActionEl.PAGE_EL_ID}`).css({ display: 'none' })
  6964 + $(`#${enumActionEl.EDITOR_CONTAINER}`).css({ display: 'flex' })
  6965 + $(`#${enumActionEl.WAY_SELECT}`).css({ display: 'block' })
  6966 + }
  6967 + }
  6968 +
  6969 + /**
  6970 + * @description 编辑数据交互
  6971 + */
  6972 + async function submit(callback) {
  6973 + if (!hasPermission()) return
  6974 + const formVal = form.val(enumActionEl.FORM_FILTER)
  6975 + const isParamsSetting = formVal[enumConst.ACTION] === enumActionType.PARAMS_SETTING
  6976 + const { COMPONENT_TYPE } = Sidebar.prototype.enumCellBasicAttribute
  6977 + const { SWITCH } = Sidebar.prototype.enumComponentType
  6978 + const isSwitchComponent = vertices[0].getAttribute(COMPONENT_TYPE) === SWITCH
  6979 +
  6980 + if (isParamsSetting && isSwitchComponent && (!currentNodeData.act || !currentNodeData.act.find(item => item.type === enumDynamicEffectType.SWITCH))) {
  6981 + UseLayUi.topErrorMsg('请先配置数据动效中的状态设置')
  6982 + return
  6983 + }
  6984 +
  6985 + if (isParamsSetting) {
  6986 + if (!isJson(formVal[enumConst.COMMAND])) {
  6987 + UseLayUi.topErrorMsg('命令配置存在错误')
  6988 + return
  6989 + }
  6990 + }
  6991 +
  6992 + const data = {
  6993 + type: event.data.type,
  6994 + configurationId,
  6995 + contentId: currentPageId.id,
  6996 + id: graphId,
  6997 + orgId: 'b4dd6e2b-6e0f-413c-bf5a-70133bd571e8',
  6998 + ...recordData,
  6999 + content: {
  7000 + type: formVal[enumConst.ACTION],
  7001 + value: formVal[enumGetValue[formVal[enumConst.ACTION]]],
  7002 + ...(isParamsSetting ? {
  7003 + [enumConst.COMMAND]: formVal[enumConst.COMMAND],
  7004 + [enumConst.WAY]: formVal[enumConst.WAY]
  7005 + } : {}),
  7006 + },
  7007 + }
  7008 + await to(autoSaveGraphInfo())
  7009 + const [err, res] = await to(ConfigurationNodeApi.updateNodeEvent(data))
  7010 + if (err) return
  7011 + isControlComponent && overrideCurrentData('event', 'type', res)
  7012 + isControlComponent && echoActionType()
  7013 + UseLayUi.successMsg()
  7014 + callback()
  7015 + }
  7016 +
  7017 + /**
  7018 + * @description 生成 动作选项
  7019 + */
  7020 + function generatorActionOptions() {
  7021 + const options = [
  7022 + { name: '打开链接', id: enumActionType.LINK },
  7023 + { name: '打开页面', id: enumActionType.PAGE },
  7024 + ...(isControlComponent ? [{ name: '参数设置', id: enumActionType.PARAMS_SETTING }] : []),
  7025 + { name: '给变量赋值', id: enumActionType.PROPS, disabled: true },
  7026 + ]
  7027 + return UseLayUi.generateOptionTemplate({ dataSource: options, addPlaceholderOption: false })
  7028 + }
  7029 +
  7030 + /**
  7031 + * @description 生产页面选项
  7032 + */
  7033 + function generatorPageOptions() {
  7034 + const options = ui.pages.map(item => ({ name: item.getName(), id: item.getId() }))
  7035 + return UseLayUi.generateOptionTemplate({ dataSource: options, addPlaceholderOption: false })
  7036 + }
  7037 +
  7038 + function generatorEventListen() {
  7039 + form.on(`select(${enumActionEl.ACTION_SELECT_FILTER})`, (data) => {
  7040 + const { value } = data
  7041 + controlFormDisplay(value)
  7042 + })
  7043 + }
  7044 +
  7045 + function isJson(string) {
  7046 + if (typeof string === 'string') {
  7047 + try {
  7048 + const obj = JSON.parse(string)
  7049 + if (typeof obj === 'object' && obj !== null) {
  7050 + return true
  7051 + }
  7052 + } catch (e) {
  7053 + return false
  7054 + }
  7055 + }
  7056 + return false
  7057 + }
  7058 +
  7059 + function jsonParse(value) {
  7060 + try {
  7061 + return JSON.parse(value)
  7062 + } catch (error) {
  7063 + return {}
  7064 + }
  7065 + }
  7066 +
  7067 +
  7068 + /**
  7069 + * @description 创建JSON编辑器
  7070 + * @param el
  7071 + * @param datum
  7072 + */
  7073 + function createEditor(record) {
  7074 + let defaultValue = { [enumConst.ATTR_PLACEHOLDER]: 0 }
  7075 + const editor = ace.edit(enumActionEl.EDITOR, {
  7076 + maxLines: 18, // 最大行数,超过会自动出现滚动条
  7077 + minLines: 10, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小
  7078 + fontSize: 14, // 编辑器内字体大小
  7079 + tabSize: 2, // 制表符设置为 4 个空格大小
  7080 + });
  7081 + if (record.content && record.content[enumConst.COMMAND]) defaultValue = jsonParse(record.content[enumConst.COMMAND])
  7082 + const stringValue = JSON.stringify(defaultValue, null, 2)
  7083 + editor.insert(stringValue)
  7084 + $(`#${enumActionEl.EDITOR_CONTAINER}`).parent().find(`textarea[name="${enumConst.COMMAND}"]`).val(stringValue)
  7085 + editor.session.setMode("ace/mode/json");
  7086 + editor.getSession().on('change', (event, editor) => {
  7087 + $(`#${enumActionEl.EDITOR_CONTAINER}`).parent().find(`textarea[name="${enumConst.COMMAND}"]`).val(editor.getValue())
  7088 + })
  7089 + }
  7090 +
  7091 + /**
  7092 + * @description 创建弹窗表单
  7093 + */
  7094 + function createLayerForm(type) {
  7095 +
  7096 + const content = `
  7097 + <form class="layui-form" lay-filter="${enumActionEl.FORM_FILTER}">
  7098 + <div style="width: 450px">
  7099 + <div class="layui-form-item">
  7100 + <label class="layui-form-label">事件</label>
  7101 + <div class="layui-input-block">
  7102 + <input type="text" name="${enumConst.EVENT}" disabled class="layui-input" value="${enumEventType[type]}">
  7103 + </div>
  7104 + </div>
  7105 + <div class="layui-form-item">
  7106 + <label class="layui-form-label">动作</label>
  7107 + <div class="layui-input-block">
  7108 + <select name="${enumConst.ACTION}" lay-verType="tips" lay-verify="required" lay-filter="${enumActionEl.ACTION_SELECT_FILTER}">
  7109 + ${generatorActionOptions()}
  7110 + </select>
  7111 + </div>
  7112 + </div>
  7113 + <div class="layui-form-item" id="${enumActionEl.LINK_EL_ID}">
  7114 + <label class="layui-form-label">链接</label>
  7115 + <div class="layui-input-block">
  7116 + <input type="text" lay-verType="tips" name="${enumConst.LINK_VALUE}" class="layui-input">
  7117 + </div>
  7118 + </div>
  7119 + <div class="layui-form-item" id="${enumActionEl.PAGE_EL_ID}" style="display:none">
  7120 + <label class="layui-form-label">页面</label>
  7121 + <div class="layui-input-block">
  7122 + <select name="${enumConst.PAGE_VALUE}" lay-verType="tips" id="pageSelect">
  7123 + ${generatorPageOptions()}
  7124 + </select>
  7125 + </div>
  7126 + </div>
  7127 + <div class="layui-form-item" id="${enumActionEl.WAY_SELECT}" style="display:none">
  7128 + <label class="layui-form-label">单向/双向 ${createHelpMessage(`单向:服务器向网关设备、直连设备发送指令。发送指令后,设备不会返回任何信息。\n
  7129 + 双向:服务器向网关设备、直连设备发送指令。发送指令后,设备返回响应信息。`, 'way')}</label>
  7130 + <div class="layui-input-block">
  7131 + <input type="radio" name="${enumConst.WAY}" value="${enumWayType.ONE_WAY}" title="单向" checked="">
  7132 + <input type="radio" name="${enumConst.WAY}" value="${enumWayType.TWO_WAY}" title="双向">
  7133 + </div>
  7134 + </div>
  7135 + <div id="${enumActionEl.EDITOR_CONTAINER}" style="display: none;">
  7136 + <div style="width: 80px; text-align: right; padding: 9px 15px;flex: 0 0 80px;">命令 ${createHelpMessage('用户预览模式下,点击参数设置后。输入的变量值将作为"attrPlaceholder"的值,并以JSON格式下发给服务器。', 'command')}</div>
  7137 + <div id="${enumActionEl.EDITOR}" style="width: 100%; height: 100%;border: 2px solid #eee;"></div>
  7138 + <textarea name="${enumConst.COMMAND}" style="display: none;" />
  7139 + </div>
  7140 + </div>
  7141 + </form>
  7142 + `
  7143 +
  7144 + layer.open({
  7145 + title: "创建交互",
  7146 + content,
  7147 + area: ["800px", "500px"],
  7148 + btn: ["保存", "取消"],
  7149 + yes(index) {
  7150 + form.on(`submit(${enumActionEl.LAYER_SUBMIT_FILTER})`, data => {
  7151 + submit(() => {
  7152 + layer.close(index)
  7153 + !isControlComponent && getNodeBindInfo()
  7154 + })
  7155 + })
  7156 + },
  7157 + btn2(index) {
  7158 + layer.close(index);
  7159 + },
  7160 + success(layero) {
  7161 + $(layero).addClass('layui-form').find('.layui-layer-btn0').attr({
  7162 + 'lay-submit': '',
  7163 + 'lay-filter': enumActionEl.LAYER_SUBMIT_FILTER,
  7164 + })
  7165 + generatorEventListen()
  7166 + const info = getLayerBindInfo('event', type)
  7167 + Object.assign(recordData, { enabled: info.enabled })
  7168 + createEditor(info)
  7169 + form.render(null, enumActionEl.FORM_FILTER)
  7170 + if (info) echoFormData(info)
  7171 + },
  7172 + shade: ["0.7", "#fafafa"],
  7173 + });
  7174 + }
  7175 +
  7176 + createLayerForm(event.data.type)
  7177 + }
  7178 +
  7179 + /**
  7180 + * @description 处理数据动效
  7181 + */
  7182 + function handleDataDynamicEffect(event) {
  7183 +
  7184 + const IS_DISPLAY = event.data.type === enumDynamicEffectType.DISPLAY
  7185 + const IS_RUNNING = event.data.type === enumDynamicEffectType.RUNNING
  7186 +
  7187 + const enumEventType = {
  7188 + FLASH: '闪烁',
  7189 + DISPLAY: '显示/隐藏',
  7190 + ROTATE: '旋转',
  7191 + RUNNING: '水流动效'
  7192 + }
  7193 +
  7194 + const enumActionEl = {
  7195 + ROW_FILTER: 'dataDynamicEffectRowFilter',
  7196 + FORM_FILTER: 'dataDynamicEffectFormFilter',
  7197 + TABLE_BODY_EL: 'dataDynamicEffectTbody',
  7198 + TYPE_EL: 'dataDynamicEffectType',
  7199 + DEL_BTN_EL: 'dataDynamicEffectDelBtn',
  7200 + ADD_BTN_EL: 'dataDynamicEffectAddBtn',
  7201 + DISPLAY_SWITCH_EL: 'visibleOrHidden',
  7202 + LAYER_SUBMIT_FILTER: 'dynamicEffectLayerFilter',
  7203 + MIN_FILTER: 'dynamicEffectMinFilter',
  7204 + MAX_FILTER: 'dynamicEffectMaxFilter',
  7205 + DATA_SOURCE_COMP_EL: 'dynamicDataSourceCompEl',
  7206 + }
  7207 +
  7208 + const getRowFilter = (rowNumber) => `${enumActionEl.ROW_FILTER}${rowNumber}`
  7209 +
  7210 + /**
  7211 + * @description
  7212 + */
  7213 + const enumConst = {
  7214 + MIN: 'min',
  7215 + MAX: 'max',
  7216 + TYPE: 'type',
  7217 + ORG_ID: 'orgId',
  7218 + DEVICE_ID: 'deviceId',
  7219 + SLAVE_DEVICE_ID: 'slaveDeviceId',
  7220 + ATTR: 'attr',
  7221 + GATEWAY: 'GATEWAY',
  7222 + }
  7223 +
  7224 + const enumDisplayType = HandleDynamicEffect.enumDisplayType
  7225 +
  7226 + const enumRunningType = HandleDynamicEffect.enumRunningType
  7227 +
  7228 + const displayOptions = [
  7229 + { name: '显示', id: enumDisplayType.SHOW },
  7230 + { name: '隐藏', id: enumDisplayType.HIDDEN },
  7231 + ]
  7232 +
  7233 + const runningOptions = [
  7234 + { name: '流动', id: enumRunningType.RUN },
  7235 + { name: '静止', id: enumRunningType.STOP },
  7236 + ]
  7237 +
  7238 + /**
  7239 + * @description
  7240 + */
  7241 + let addRowNumber = 0
  7242 +
  7243 + const recordData = {
  7244 + enabled: false
  7245 + }
  7246 +
  7247 + function generateStateOptions(options = []) {
  7248 + const template = UseLayUi.generateOptionTemplate({ dataSource: options, addPlaceholderOption: false })
  7249 + return `
  7250 + <div class="layui-form-item" style="margin-bottom: 0px">
  7251 + <div class="layui-input-block" style="margin-left: 0px;">
  7252 + <select name="${enumConst.TYPE}" lay-verType="tips" lay-verify="required">${template}</select>
  7253 + </div>
  7254 + </div>
  7255 + `
  7256 + }
  7257 +
  7258 + /**
  7259 + * @description 添加一条记录
  7260 + */
  7261 + function addRecord() {
  7262 + const content = `
  7263 + <tr class="layui-form" lay-filter="${getRowFilter(addRowNumber)}">
  7264 + ${IS_DISPLAY && `<td>${generateStateOptions(displayOptions)}</td>`}
  7265 + ${IS_RUNNING && `<td>${generateStateOptions(runningOptions)}</td>`}
  7266 + <td>
  7267 + <input autocomplete="off" lay-verType="tips" lay-verify="required" type="number" name="${enumConst.MIN}" class="layui-input ${enumActionEl.MIN_FILTER}">
  7268 + </td>
  7269 + <td>
  7270 + <input autocomplete="off" lay-verType="tips" lay-verify="required" type="number" name="${enumConst.MAX}" class="layui-input ${enumActionEl.MAX_FILTER}">
  7271 + </td>
  7272 + <td style="text-align: center;">
  7273 + <button type="button" class="layui-btn layui-btn-primary layui-border-red ${enumActionEl.DEL_BTN_EL}">删除</button>
  7274 + </td>
  7275 + </tr>
  7276 + `
  7277 + addRowNumber++
  7278 + $(`#${enumActionEl.TABLE_BODY_EL}`).append(content)
  7279 + if (IS_DISPLAY || IS_RUNNING) {
  7280 + form.render()
  7281 + }
  7282 + }
  7283 +
  7284 + /**
  7285 + * @description 创建添加记录事件
  7286 + */
  7287 + function createAddRowListenEvent() {
  7288 + $(`#${enumActionEl.ADD_BTN_EL}`).on('click', () => {
  7289 + addRecord()
  7290 + })
  7291 + }
  7292 +
  7293 + /**
  7294 + * @description 创建删除行事件
  7295 + */
  7296 + function createDeleteRowListenEvent() {
  7297 + $(`#${enumActionEl.TABLE_BODY_EL}`).on('click', `.${enumActionEl.DEL_BTN_EL}`, (event) => {
  7298 + $(event.target).parents('tr').remove()
  7299 + })
  7300 + }
  7301 +
  7302 +
  7303 + /**
  7304 + * @description 创建输入监听
  7305 + */
  7306 + function createInputListener() {
  7307 + $(`#${enumActionEl.TABLE_BODY_EL}`)
  7308 + .on('input', `.${enumActionEl.MIN_FILTER}`, event => {
  7309 + const minVal = $(event.target).val()
  7310 + const maxVal = $(event.target).parents('tr').find(`input.${enumActionEl.MAX_FILTER}`).val()
  7311 + if (maxVal !== '' && Number(minVal) > Number(maxVal)) {
  7312 + layer.tips('输入值大于最大值', $(event.target), {
  7313 + tips: 1,
  7314 + });
  7315 + }
  7316 + })
  7317 + .on('input', `.${enumActionEl.MAX_FILTER}`, event => {
  7318 + const maxVal = $(event.target).val()
  7319 + const minVal = $(event.target).parents('tr').find(`input.${enumActionEl.MIN_FILTER}`).val()
  7320 + if (minVal !== '' && Number(maxVal) < Number(minVal)) {
  7321 + layer.tips('输入值小于最小值', $(event.target), {
  7322 + tips: 1,
  7323 + });
  7324 + }
  7325 + })
  7326 + }
  7327 +
  7328 +
  7329 + /**
  7330 + * @description 下拉选项挂载
  7331 + */
  7332 + function createSelectOptionMountEvent() {
  7333 + $(`#${enumActionEl.TABLE_BODY_EL}`).on('click', '.layui-form-select', event => {
  7334 + const width = event.currentTarget.offsetWidth || 200
  7335 + const height = event.currentTarget.offsetHeight || 38
  7336 + const offset = $(event.currentTarget).offset()
  7337 + $(event.currentTarget).find('dl').css({
  7338 + position: 'fixed',
  7339 + 'min-width': width + 'px',
  7340 + top: offset.top + height + 'px',
  7341 + left: offset.left + 'px'
  7342 + })
  7343 + })
  7344 + }
  7345 +
  7346 + /**
  7347 + * @description 创建事件监听
  7348 + */
  7349 + function createEventListen() {
  7350 + createInputListener()
  7351 + createAddRowListenEvent()
  7352 + createDeleteRowListenEvent()
  7353 + createSelectOptionMountEvent()
  7354 + }
  7355 +
  7356 + /**
  7357 + * @description 回显数据
  7358 + */
  7359 + function echoData(info) {
  7360 + const { condition = [] } = info
  7361 + echoEachData(condition)
  7362 + }
  7363 +
  7364 + /**
  7365 + * @description 回显行数据
  7366 + */
  7367 + function echoEachData(condition = []) {
  7368 + condition.forEach((item, index) => {
  7369 + addRecord()
  7370 + form.val(getRowFilter(index), { ...item })
  7371 + })
  7372 + }
  7373 +
  7374 + function validate(tableData) {
  7375 + let validateFlag = true
  7376 + for (let i = 0; i < tableData.length; i++) {
  7377 + const { min, max } = tableData[i]
  7378 + if (Number(min) > Number(max)) {
  7379 + validateFlag = false
  7380 + layer.tips('输入值大于最大值', $(`#${enumActionEl.TABLE_BODY_EL} tr`).eq(i).find(`input[name="${enumConst.MIN}"]`), { tips: 1 })
  7381 + break
  7382 + } else if (Number(max) < Number(min)) {
  7383 + validateFlag = false
  7384 + layer.tips('输入值小于最小值', $(`#${enumActionEl.TABLE_BODY_EL} tr`).eq(i).find(`input[name="${enumConst.MAX}"]`), { tips: 1 })
  7385 + break
  7386 + }
  7387 + }
  7388 + return validateFlag
  7389 + }
  7390 +
  7391 + async function submit(callback) {
  7392 + if (!hasPermission()) return
  7393 + const tableData = Array.from({ length: addRowNumber }).map((_, index) => form.val(getRowFilter(index))).filter(obj => Object.keys(obj).length)
  7394 + if (!validate(tableData)) return
  7395 + const formVal = form.val(enumActionEl.FORM_FILTER)
  7396 + const formModel = {
  7397 + configurationId,
  7398 + contentId: currentPageId.id,
  7399 + ...formVal,
  7400 + id: graphId,
  7401 + condition: tableData,
  7402 + type: event.data.type,
  7403 + ...recordData
  7404 + }
  7405 + await to(autoSaveGraphInfo())
  7406 + const [err, res] = await to(ConfigurationNodeApi.updateNodeAct(formModel))
  7407 + if (err) return
  7408 + UseLayUi.successMsg()
  7409 + callback()
  7410 + }
  7411 +
  7412 + function isExistInArea(min, max, value) {
  7413 + return Number(value) >= Number(min) && Number(value) <= Number(max)
  7414 + }
  7415 +
  7416 + function createLayerForm(type) {
  7417 + const content = `
  7418 + <form class="layui-form" lay-filter="${enumActionEl.FORM_FILTER}">
  7419 + <div style="width:400px">
  7420 + <div class="layui-form-item">
  7421 + <label class="layui-form-label">类型</label>
  7422 + <div class="layui-input-block">
  7423 + <input type="radio" name="type" value="1" title="联网设备" checked>
  7424 + <input type="radio" name="type" value="2" title="产品/场景" disabled>
  7425 + </div>
  7426 + </div>
  7427 +
  7428 + <div id="${enumActionEl.DATA_SOURCE_COMP_EL}"></div>
  7429 +
  7430 + </div>
  7431 + </form>
  7432 + <div class="override__table">
  7433 + <table class="layui-table" >
  7434 + <thead>
  7435 + <tr>
  7436 + ${IS_DISPLAY ? '<th style="text-align:center">类型</th>' : ''}
  7437 + ${IS_RUNNING ? '<th style="text-align:center">类型</th>' : ''}
  7438 + <th style="text-align:center">最小值(>=)</th>
  7439 + <th style="text-align:center">最大值(<=)</th>
  7440 + <th style="text-align:center">操作</th>
  7441 + </tr>
  7442 + </thead>
  7443 + <tbody id="${enumActionEl.TABLE_BODY_EL}"></tbody>
  7444 + </table>
  7445 + <div style="display:flex;justify-content:center;">
  7446 + <button type="button" class="layui-btn layui-btn-primary layui-border-blue" id="${enumActionEl.ADD_BTN_EL}">添加一条</button>
  7447 + </div>
  7448 + </div>
  7449 + `
  7450 +
  7451 + layer.open({
  7452 + title: enumEventType[type],
  7453 + content,
  7454 + skin: 'event-layer__override',
  7455 + btn: ['保存', '取消'],
  7456 + offset: '100px',
  7457 + area: (IS_DISPLAY || IS_RUNNING) ? '1000px' : '800PX',
  7458 + success(layero) {
  7459 + $(layero).addClass('layui-form').find('.layui-layer-btn0').attr({
  7460 + 'lay-submit': '',
  7461 + 'lay-filter': enumActionEl.LAYER_SUBMIT_FILTER,
  7462 + })
  7463 +
  7464 + // render data source tree
  7465 + const {
  7466 + component,
  7467 + echoDataSource,
  7468 + } = generateDataSourceComponent({ overrideClass: 'dynamic-effect__data-source-comp--override' })
  7469 + $(`#${enumActionEl.DATA_SOURCE_COMP_EL}`).append(component)
  7470 + form.render()
  7471 +
  7472 + const info = getLayerBindInfo('act', type)
  7473 + const { condition = [], enabled } = info
  7474 + Object.assign(recordData, { enabled })
  7475 + if (info && condition.length) {
  7476 + echoDataSource(info)
  7477 + echoData(info)
  7478 + } else {
  7479 + addRecord()
  7480 + }
  7481 + createEventListen()
  7482 + form.render()
  7483 + },
  7484 + yes(index) {
  7485 + form.on(`submit(${enumActionEl.LAYER_SUBMIT_FILTER})`, data => {
  7486 + submit(() => {
  7487 + layer.close(index)
  7488 + getNodeBindInfo()
  7489 + })
  7490 + })
  7491 + },
  7492 + btn2() {
  7493 +
  7494 + },
  7495 + })
  7496 + }
  7497 +
  7498 + createLayerForm(event.data.type)
  7499 +
  7500 + }
  7501 +
  7502 +
  7503 + /**
  7504 + * @description
  7505 + * @param {} event
  7506 + */
  7507 + function handleStateSetting(event) {
  7508 +
  7509 + const enumActionEl = {
  7510 + TABLE_BODY_EL: 'stateTableBodyEl',
  7511 + LAYER_SUBMIT_FILTER: 'stateSettingSubmitFilter',
  7512 + ENABLE_FILTER: 'enableFilter',
  7513 + CLOSE_FILTER: 'closeFilter',
  7514 + SET_IMG_EL: 'variableImageTableSetImgEl',
  7515 + PREVIEW_IMG_CONTAINER: 'img__container',
  7516 + DEL_PREVIEW_IMG: 'img__delete',
  7517 + PREVIEW_CONTAINER: 'previewContainer'
  7518 + }
  7519 +
  7520 + const enumConst = {
  7521 +
  7522 + TYPE: 'type',
  7523 +
  7524 + /**
  7525 + * @description 开启类型
  7526 + */
  7527 + ENABLE_TYPE: 'enable',
  7528 +
  7529 + /**
  7530 + * @description 关闭类型
  7531 + */
  7532 + CLOSE_TYPE: 'close',
  7533 +
  7534 + /**
  7535 + * @description 图片来源
  7536 + */
  7537 + IMAGE_ORIGIN: 'imageOrigin',
  7538 +
  7539 + /**
  7540 + * @description 图库图形类别
  7541 + */
  7542 + IMAGE_GALLERY_CATEGORY: 'category',
  7543 +
  7544 + /**
  7545 + * @description 图表图形路径
  7546 + */
  7547 + IMAGE_GALLERY_IMAGE_PATH: 'imagePath',
  7548 +
  7549 + /**
  7550 + * @description 状态设置 值
  7551 + */
  7552 + VALUE: 'value'
  7553 + }
  7554 +
  7555 + const recordData = {
  7556 + enabled: false
  7557 + }
  7558 +
  7559 + const StateDefaultValue = {
  7560 + enable: {
  7561 + [enumConst.TYPE]: enumConst.ENABLE_TYPE,
  7562 + [enumConst.IMAGE_ORIGIN]: 'gallery',
  7563 + [enumConst.IMAGE_GALLERY_CATEGORY]: 'switch',
  7564 + [enumConst.IMAGE_GALLERY_IMAGE_PATH]: `${Proxy_Prefix}/images/thingskit/switch-on.svg`,
  7565 + },
  7566 + close: {
  7567 + [enumConst.TYPE]: enumConst.CLOSE_TYPE,
  7568 + [enumConst.IMAGE_ORIGIN]: 'gallery',
  7569 + [enumConst.IMAGE_GALLERY_CATEGORY]: 'switch',
  7570 + [enumConst.IMAGE_GALLERY_IMAGE_PATH]: `${Proxy_Prefix}/images/thingskit/switch-off.svg`,
  7571 + }
  7572 + }
  7573 +
  7574 + function createUploadImgContainer(info) {
  7575 + const enableEl = $(`#${enumActionEl.TABLE_BODY_EL} tr[lay-filter="${enumActionEl.ENABLE_FILTER}"] .${enumActionEl.PREVIEW_CONTAINER}`)
  7576 + const closeEl = $(`#${enumActionEl.TABLE_BODY_EL} tr[lay-filter="${enumActionEl.CLOSE_FILTER}"] .${enumActionEl.PREVIEW_CONTAINER}`)
  7577 + generateUploadImgContainer({ el: enableEl })
  7578 + generateUploadImgContainer({ el: closeEl })
  7579 + }
  7580 +
  7581 + function addRecord(filter, state, defaultValue) {
  7582 + return `
  7583 + <tr class="layui-form" lay-filter="${filter}">
  7584 + <td>
  7585 + ${state}
  7586 + </td>
  7587 + <td>
  7588 + <div class="layui-form-item">
  7589 + <input class="layui-input" autocomplete="off" type="number" name="${enumConst.VALUE}" lay-verType="tips" lay-verify="required" />
  7590 + <input class="layui-input" autocomplete="off" style="display: none;" name="${enumConst.TYPE}" lay-verType="tips" lay-verify="required" />
  7591 + </div>
  7592 + </td>
  7593 + <td class=${enumActionEl.PREVIEW_CONTAINER}>
  7594 + </td>
  7595 + </tr>`
  7596 + }
  7597 +
  7598 + /**
  7599 + * @descrition 回显数据
  7600 + */
  7601 + function echoData(info = {}) {
  7602 + const { condition = [] } = info
  7603 +
  7604 + condition.forEach(item => {
  7605 + if (item[enumConst.TYPE] === enumConst.ENABLE_TYPE) {
  7606 + setRowFormValue(enumActionEl.ENABLE_FILTER, item)
  7607 + } else if (item[enumConst.TYPE] === enumConst.CLOSE_TYPE) {
  7608 + setRowFormValue(enumActionEl.CLOSE_FILTER, item)
  7609 + }
  7610 + })
  7611 +
  7612 + form.render()
  7613 + }
  7614 +
  7615 + /**
  7616 + * @description 初始化数据
  7617 + */
  7618 + function initialData() {
  7619 + setRowFormValue(enumActionEl.ENABLE_FILTER, StateDefaultValue.enable)
  7620 + setRowFormValue(enumActionEl.CLOSE_FILTER, StateDefaultValue.close)
  7621 + }
  7622 +
  7623 + function setRowFormValue(formFilter, value = {}) {
  7624 + form.val(formFilter, value)
  7625 + $(`#${enumActionEl.TABLE_BODY_EL} tr[lay-filter="${formFilter}"] .${enumActionEl.SET_IMG_EL} img`).attr('src', value[enumConst.IMAGE_GALLERY_IMAGE_PATH])
  7626 + form.render()
  7627 + }
  7628 +
  7629 + async function submit(callback) {
  7630 + if (!hasPermission()) return
  7631 +
  7632 + const enableValue = form.val(enumActionEl.ENABLE_FILTER)
  7633 + const closeValue = form.val(enumActionEl.CLOSE_FILTER)
  7634 +
  7635 + const formVal = getDataSourceBindValue()
  7636 +
  7637 + if (![formVal.deviceId && formVal.orgId].every(Boolean)) {
  7638 + UseLayUi.topErrorMsg('请先选择数据源')
  7639 + return
  7640 + }
  7641 +
  7642 + const formModel = {
  7643 + configurationId,
  7644 + contentId: currentPageId.id,
  7645 + ...formVal,
  7646 + id: graphId,
  7647 + condition: [enableValue, closeValue],
  7648 + type: event.data.type,
  7649 + ...recordData
  7650 + }
  7651 + await to(autoSaveGraphInfo())
  7652 + const [err, res] = await to(ConfigurationNodeApi.updateNodeAct(formModel))
  7653 + if (err) return
  7654 + overrideCurrentData('act', 'type', res)
  7655 + echoActionType()
  7656 + UseLayUi.successMsg()
  7657 + callback()
  7658 + }
  7659 +
  7660 + function createLayerForm(type) {
  7661 + const content = `
  7662 + <table class="layui-table">
  7663 + <colgroup>
  7664 + <col width="150">
  7665 + <col width="150">
  7666 + <col>
  7667 + </colgroup>
  7668 + <thead>
  7669 + <tr>
  7670 + <th>状态</th>
  7671 + <th>值</th>
  7672 + <th>展示图片</th>
  7673 + </tr>
  7674 + </thead>
  7675 + <tbody id="${enumActionEl.TABLE_BODY_EL}">
  7676 + ${addRecord(enumActionEl.ENABLE_FILTER, '开启')}
  7677 + ${addRecord(enumActionEl.CLOSE_FILTER, '关闭')}
  7678 + </tbody>
  7679 + </table>`
  7680 +
  7681 + layer.open({
  7682 + title: '状态设置',
  7683 + content,
  7684 + skin: 'event-layer__override',
  7685 + btn: ['保存', '取消'],
  7686 + offset: '100px',
  7687 + area: '500px',
  7688 + success(layero) {
  7689 + $(layero).addClass('layui-form').find('.layui-layer-btn0').attr({
  7690 + 'lay-submit': '',
  7691 + 'lay-filter': enumActionEl.LAYER_SUBMIT_FILTER,
  7692 + })
  7693 + createUploadImgContainer()
  7694 +
  7695 + const info = getLayerBindInfo('act', type)
  7696 + const { condition = [], enabled } = info
  7697 + Object.assign(recordData, { enabled })
  7698 +
  7699 + if (info && condition.length) {
  7700 + echoData(info)
  7701 + } else {
  7702 + initialData()
  7703 + }
  7704 + },
  7705 + yes(index) {
  7706 + form.on(`submit(${enumActionEl.LAYER_SUBMIT_FILTER})`, data => {
  7707 + submit(() => {
  7708 + layer.close(index)
  7709 + // getNodeBindInfo()
  7710 + })
  7711 + })
  7712 + },
  7713 + btn2() {
  7714 +
  7715 + },
  7716 + })
  7717 + }
  7718 +
  7719 + createLayerForm(event.data.type)
  7720 + }
  7721 +
  7722 + /**
  7723 + * @description 生成数据源组件
  7724 + * @param {object} options
  7725 + * @param {string} [options.overrideClass = ''] options.overrideClass
  7726 + * @param {boolean} [options.validate = true ] options.validate
  7727 + */
  7728 + function generateDataSourceComponent(options = {}) {
  7729 + const { overrideClass = '', validate = true } = options
  7730 + const componentFilter = `data-source__component-filter-${Date.now()}`
  7731 + const componentId = `data-source__component-${Date.now()}`
  7732 +
  7733 + const validateRule = validate ? { layVerify: 'required', layVerType: 'tips' } : {}
  7734 +
  7735 + const enumDataSourceConst = {
  7736 + ORG_ID: 'orgId',
  7737 + DEVICE_ID: 'deviceId',
  7738 + SLAVE_DEVICE_ID: 'slaveDeviceId',
  7739 + ATTR: 'attr',
  7740 + GATEWAY: 'GATEWAY',
  7741 + }
  7742 + /**
  7743 + * @description
  7744 + * @type {{id: string, deviceType: string, name: string}[]}
  7745 + */
  7746 + let deviceList = []
  7747 +
  7748 + const component = document.createElement('div')
  7749 +
  7750 + $(component)
  7751 + .addClass(`layui-form data-source__component-style ${overrideClass}`)
  7752 + .attr({
  7753 + id: componentId,
  7754 + 'lay-filter': componentFilter,
  7755 + })
  7756 +
  7757 + init()
  7758 +
  7759 + function init() {
  7760 + generatorOrgTres()
  7761 + generatorDeviceSelect()
  7762 + generatorSlaveDevice()
  7763 + generatorAttrSelect()
  7764 + }
  7765 +
  7766 +
  7767 + /**
  7768 + * @description 根据组织ID获取设备
  7769 + */
  7770 + async function getDevicesByOrgId(organizationId) {
  7771 + if (organizationId) {
  7772 + const items = deviceList = await ConfigurationNodeApi.getMasterDevice(organizationId);
  7773 + $(`#${componentId} `).find(`select[name="${enumDataSourceConst.DEVICE_ID}"]`).html(UseLayUi.generateOptionTemplate({ dataSource: items }))
  7774 + form.render('select', componentFilter)
  7775 + }
  7776 + }
  7777 +
  7778 + /**
  7779 + * @description 通过主设备ID获取从设备
  7780 + */
  7781 + async function getSlaveDeviceByMasterDeviceId(orgId, deviceId) {
  7782 + if (deviceId && currentCheckedOrgNode) {
  7783 + const items = await ConfigurationNodeApi.getSlaveDevice(orgId, deviceId);
  7784 + $(`#${componentId} `).find(`select[name="${enumDataSourceConst.SLAVE_DEVICE_ID}"]`).html(UseLayUi.generateOptionTemplate({ dataSource: items }))
  7785 + form.render('select', componentFilter)
  7786 + }
  7787 + }
  7788 +
  7789 + /**
  7790 + * @description 根据设备ID获取属性
  7791 + */
  7792 + async function getAttrByDeviceId(tbDeviceId) {
  7793 + if (tbDeviceId) {
  7794 + const [err, res] = await to(ConfigurationNodeApi.getDeviceAttr(tbDeviceId))
  7795 + $(`#${componentId} `).find(`select[name="${enumDataSourceConst.ATTR}"]`).html(UseLayUi.generateOptionTemplate({ dataSource: res }))
  7796 + form.render('select', componentFilter)
  7797 + }
  7798 + }
  7799 +
  7800 +
  7801 + /**
  7802 + * @description 生成组织选择
  7803 + */
  7804 + async function generatorOrgTres() {
  7805 + const orgContainerId = `data-source__component--org-${Date.now()}`
  7806 + const orgContainer = `<div id="${orgContainerId}"></div>`
  7807 + $(component).append(orgContainer)
  7808 +
  7809 + const [err, res] = await to(ConfigurationNodeApi.getOrgTree())
  7810 + if (err) return
  7811 + treeList = res
  7812 +
  7813 + UseLayUi.createTreeSelect({
  7814 + elem: `#${orgContainerId}`,
  7815 + layFilter: enumDataSourceConst.ORG_ID,
  7816 + label: '组织',
  7817 + singleUsage: false,
  7818 + className: 'data-source__component-select',
  7819 + ...validateRule,
  7820 + treeProps: {
  7821 + data: treeList,
  7822 + onlyIconControl: true,
  7823 + click(node) {
  7824 + currentCheckedOrgNode = node.data.id
  7825 + form.val(componentFilter, {
  7826 + [enumDataSourceConst.DEVICE_ID]: null,
  7827 + [enumDataSourceConst.SLAVE_DEVICE_ID]: null,
  7828 + [enumDataSourceConst.ATTR]: null,
  7829 + })
  7830 + getDevicesByOrgId(node.data.id)
  7831 + },
  7832 + },
  7833 + })
  7834 + }
  7835 +
  7836 + /**
  7837 + * @description 生成设备选择器
  7838 + */
  7839 + function generatorDeviceSelect() {
  7840 + const deviceSelect = UseLayUi.createSelect({
  7841 + label: '设备',
  7842 + bindValueFiled: enumDataSourceConst.DEVICE_ID,
  7843 + layFilter: `${componentFilter}--${enumDataSourceConst.DEVICE_ID}`,
  7844 + className: 'data-source__component-select',
  7845 + ...validateRule,
  7846 + onClick(data) {
  7847 + const { value } = data
  7848 + const selected = deviceList.find(item => item.id === value)
  7849 + form.val(componentFilter, {
  7850 + [enumDataSourceConst.SLAVE_DEVICE_ID]: null,
  7851 + [enumDataSourceConst.ATTR]: null,
  7852 + })
  7853 + if (!selected) return
  7854 + if (selected.deviceType === enumDataSourceConst.GATEWAY) {
  7855 + $(`#${componentId}`).find(`select[name="${enumDataSourceConst.SLAVE_DEVICE_ID}"]`)
  7856 + .attr('lay-verify', 'required').attr('lay-verType', 'tips')
  7857 + .parentsUntil(`#${componentId}`).show()
  7858 + getSlaveDeviceByMasterDeviceId(currentCheckedOrgNode, selected.id)
  7859 + } else {
  7860 + $(`#${componentId}`).find(`select[name="${enumDataSourceConst.SLAVE_DEVICE_ID}"]`)
  7861 + .attr('lay-verify', '').attr('lay-verType', 'tips')
  7862 + .parentsUntil(`#${componentId}`).hide()
  7863 + getAttrByDeviceId(selected.id)
  7864 + }
  7865 + },
  7866 + })
  7867 + $(component).append(deviceSelect)
  7868 + }
  7869 +
  7870 + /**
  7871 + * @description 生成从设备选择器
  7872 + */
  7873 + function generatorSlaveDevice() {
  7874 + const slaveDeviceSelect = UseLayUi.createSelect({
  7875 + label: '子设备',
  7876 + bindValueFiled: enumDataSourceConst.SLAVE_DEVICE_ID,
  7877 + layFilter: `${componentFilter}--${enumDataSourceConst.SLAVE_DEVICE_ID}`,
  7878 + className: 'data-source__component-select',
  7879 + onClick(data) {
  7880 + const { value } = data
  7881 + form.val(componentFilter, {
  7882 + [enumDataSourceConst.ATTR]: null,
  7883 + })
  7884 + getAttrByDeviceId(value)
  7885 + },
  7886 + })
  7887 + $(component).append(slaveDeviceSelect)
  7888 + }
  7889 +
  7890 + /**
  7891 + * @description 生成属性选择器
  7892 + */
  7893 + function generatorAttrSelect() {
  7894 + const attrsSelect = UseLayUi.createSelect({
  7895 + label: '属性',
  7896 + bindValueFiled: enumDataSourceConst.ATTR,
  7897 + layFilter: `${componentFilter}--${enumDataSourceConst.ATTR}`,
  7898 + className: 'data-source__component-select',
  7899 + ...validateRule,
  7900 + })
  7901 + $(component).append(attrsSelect)
  7902 + }
  7903 +
  7904 + /**
  7905 + * @description 回显数据
  7906 + * @param {{orgId: string, deviceId: string, slaveDeviceId?: string, attr: string}} dataSource
  7907 + */
  7908 + function echoDataSource(dataSource = {}) {
  7909 + const { orgId, deviceId, slaveDeviceId, attr } = dataSource
  7910 + const queue = []
  7911 + if (orgId) {
  7912 + currentCheckedOrgNode = orgId
  7913 + queue.push(getDevicesByOrgId(orgId))
  7914 + }
  7915 + if (slaveDeviceId) {
  7916 + queue.push(getSlaveDeviceByMasterDeviceId(orgId, deviceId))
  7917 + $(`#${componentId}`).find(`select[name="${enumDataSourceConst.SLAVE_DEVICE_ID}"]`).parentsUntil(`#${componentId}`).show()
  7918 + queue.push(getAttrByDeviceId(slaveDeviceId))
  7919 + } else {
  7920 + $(`#${componentId}`).find(`select[name="${enumDataSourceConst.SLAVE_DEVICE_ID}"]`).parentsUntil(`#${componentId}`).hide()
  7921 + queue.push(getAttrByDeviceId(deviceId))
  7922 + }
  7923 + Promise.all(queue)
  7924 + .finally(() => {
  7925 + const orgNode = UseLayUi.findTreeObjectByField(treeList, currentCheckedOrgNode)
  7926 + $(`#${componentId} input[name="${enumDataSourceConst.ORG_ID}"]`).parent().find('span').html(orgNode?.name)
  7927 + form.val(componentFilter, {
  7928 + orgId,
  7929 + deviceId,
  7930 + slaveDeviceId,
  7931 + attr,
  7932 + })
  7933 + })
  7934 + }
  7935 +
  7936 + /**
  7937 + *
  7938 + * @returns {{orgId: string, attr: string, deviceId: string, slaveDeviceId: string}}
  7939 + */
  7940 + function getValue() {
  7941 + return form.val(componentFilter)
  7942 + }
  7943 +
  7944 + return { component, getValue, componentId, componentFilter, echoDataSource }
  7945 + }
  7946 +
  7947 + /**
  7948 + * @description 生成上传弹出层组件
  7949 + */
  7950 + function generateUploadLayerComponent(callback) {
  7951 +
  7952 + const enumActionEl = {
  7953 + /**
  7954 + * @description 图片选中区域节点
  7955 + */
  7956 + IMG_SELECT_CONTAINER_EL: 'imgSelectContainerEl',
  7957 +
  7958 + /**
  7959 + * @description 切换图片来源控制
  7960 + */
  7961 + SWITCH_IMG_ORIGIN_FILTER: 'switchImgOriginFilter',
  7962 +
  7963 + /**
  7964 + * @description 图片选中侧边栏节点
  7965 + */
  7966 + VAR_IMG_CONTAINER_SIDEBAR: 'var-image__container-sidebar',
  7967 +
  7968 + /**
  7969 + * @description 图库图形选择区域
  7970 + */
  7971 + VAR_IMG_CONTAINER_CONTENT: 'var-image__container-content',
  7972 +
  7973 + /**
  7974 + * @description 图库图形项
  7975 + */
  7976 + VAR_IMG_ITEM: 'var-image__img-item',
  7977 +
  7978 + /**
  7979 + * @description 图库图形选中控制
  7980 + */
  7981 + VAR_IMG_ITEM_CHECKED: 'var-image__img-item--checked',
  7982 +
  7983 + /**
  7984 + * @description 图库图形类别节点
  7985 + */
  7986 + VAR_IMG_CATEGORY: 'var-image__category',
  7987 +
  7988 + /**
  7989 + * @description 图库图形类别选择状态
  7990 + */
  7991 + VAR_IMG_CATEGORY_CHECKED: 'var-image__category--checked',
  7992 +
  7993 + /**
  7994 + * @description 本地上传图片节点
  7995 + */
  7996 + UPLOAD_LOCAL_FILE_EL: 'var-image__container--local',
  7997 +
  7998 + /**
  7999 + * @description 本地上传图片状态
  8000 + */
  8001 + IMAGE_UPLOAD_STATE_EL: 'var-image__upload-state',
  8002 +
  8003 + /**
  8004 + * @description 预览图片状态
  8005 + */
  8006 + IMAGE_PREVIEW_EL: 'preview__img--preview',
  8007 +
  8008 + /**
  8009 + * @description 本地图片上传删除按钮
  8010 + */
  8011 + IMAGE_DEL_PREVIEW_EL: 'var-image__del-icon',
  8012 + }
  8013 +
  8014 + const enumConst = {
  8015 + /**
  8016 + * @description 图片来源
  8017 + */
  8018 + IMAGE_ORIGIN: 'imageOrigin',
  8019 +
  8020 + /**
  8021 + * @description 图库图形类别
  8022 + */
  8023 + IMAGE_GALLERY_CATEGORY: 'category',
  8024 +
  8025 + /**
  8026 + * @description 图表图形路径
  8027 + */
  8028 + IMAGE_GALLERY_IMAGE_PATH: 'imagePath',
  8029 + }
  8030 +
  8031 + /**
  8032 + * @description 图片来源枚举类型
  8033 + */
  8034 + const enumImageOriginType = {
  8035 + LOCAL: 'local',
  8036 + GALLERY: 'gallery',
  8037 + }
  8038 +
  8039 + let imageState = {}
  8040 +
  8041 + /**
  8042 + * @description 设置选中样式
  8043 + * @param event
  8044 + * @param nodeClass
  8045 + * @param styleClass
  8046 + */
  8047 + function setImageSelectedStyle(event, nodeClass, styleClass) {
  8048 + if (!$(event.target).hasClass(nodeClass)) return
  8049 + $(`.${nodeClass}`).each(function () {
  8050 + $(this).removeClass(styleClass)
  8051 + })
  8052 + $(event.target).addClass(styleClass)
  8053 + }
  8054 +
  8055 + /**
  8056 + * @description 切换图库图形
  8057 + * @param event
  8058 + */
  8059 + function switchGalleryLib(event) {
  8060 + const key = $(event.target).attr(enumConst.IMAGE_GALLERY_CATEGORY)
  8061 + const category = sidebarInstance.getVariableImageLib(key)
  8062 + if (key && category) {
  8063 + $(`#${enumActionEl.VAR_IMG_CONTAINER_CONTENT}`).html(
  8064 + category.lib.reduce((prev, next) => prev + `<div class="${enumActionEl.VAR_IMG_ITEM}" title="${next.name}"><img src="${next.staticPath}" alt=""></div>`, ''),
  8065 + )
  8066 + }
  8067 + }
  8068 +
  8069 + /**
  8070 + * @description 图片来源事件监听
  8071 + */
  8072 + function generatorSelectImgEventListener() {
  8073 + $(`#${enumActionEl.VAR_IMG_CONTAINER_CONTENT}`).on('click', event => {
  8074 + setImageSelectedStyle(event, enumActionEl.VAR_IMG_ITEM, enumActionEl.VAR_IMG_ITEM_CHECKED)
  8075 + imageState[enumConst.IMAGE_GALLERY_IMAGE_PATH] = $(event.target).find('img').attr('src')
  8076 + })
  8077 + $(`#${enumActionEl.VAR_IMG_CONTAINER_SIDEBAR}`).on('click', event => {
  8078 + setImageSelectedStyle(event, enumActionEl.VAR_IMG_CATEGORY, enumActionEl.VAR_IMG_CATEGORY_CHECKED)
  8079 + imageState[enumConst.IMAGE_GALLERY_CATEGORY] = $(event.target).attr(enumConst.IMAGE_GALLERY_CATEGORY)
  8080 + imageState[enumConst.IMAGE_GALLERY_IMAGE_PATH] = null
  8081 + switchGalleryLib(event)
  8082 + })
  8083 + }
  8084 +
  8085 +
  8086 + /**
  8087 + * @description 创建本地图片上传区域
  8088 + */
  8089 + function createLocalFileContainer() {
  8090 + return `
  8091 + <div id="${enumActionEl.UPLOAD_LOCAL_FILE_EL}" class="layui-upload">
  8092 + <div class="preview__img">
  8093 + <img src="" class="layui-upload-img" id="${enumActionEl.IMAGE_PREVIEW_EL}">
  8094 + <div class="var_image__add-icon">+</div>
  8095 + <div class="var-image__del-icon">x</div>
  8096 + </div>
  8097 + <div class="layui-upload-list"></div>
  8098 + </div>
  8099 + <div style="margin-top: 10px;" id="${enumActionEl.IMAGE_UPLOAD_STATE_EL}"></div>
  8100 + <div style="margin-top: 10px;">图片格式支持png、jpg(jpeg)、gif, 大小不能超过5M</div>`
  8101 + }
  8102 +
  8103 + /**
  8104 + * @description 创建图库图形上传区域
  8105 + */
  8106 + function createGalleryFileContainer() {
  8107 + const category = sidebarInstance.getAllVariableImageLib()
  8108 + const defaultShow = category[0] || { lib: [] }
  8109 + return `
  8110 + <div class="var-image__container--gallery">
  8111 + <div id="${enumActionEl.VAR_IMG_CONTAINER_SIDEBAR}">
  8112 + ${category.reduce((prev, next) => prev + `<div class="${enumActionEl.VAR_IMG_CATEGORY}" ${enumConst.IMAGE_GALLERY_CATEGORY}="${next.key}">${next.label}</div>`, '')}
  8113 + </div>
  8114 + <div id="${enumActionEl.VAR_IMG_CONTAINER_CONTENT}">
  8115 + ${defaultShow.lib.reduce((prev, next) => prev + `<div class="${enumActionEl.VAR_IMG_ITEM}" title="${next.name}"><img src="${next.staticPath}" alt=""></div>`, '')}
  8116 + </div>
  8117 + </div>`
  8118 + }
  8119 +
  8120 + /**
  8121 + * @description 上传
  8122 + */
  8123 + function uploadFileEvent() {
  8124 + $(`.${enumActionEl.IMAGE_DEL_PREVIEW_EL}`).on('click', (event) => {
  8125 + event.stopPropagation()
  8126 + $(`#${enumActionEl.IMAGE_PREVIEW_EL}`).attr('src', '')
  8127 + imageState[enumConst.IMAGE_GALLERY_IMAGE_PATH] = null
  8128 + })
  8129 + upload.render({
  8130 + elem: `#${enumActionEl.UPLOAD_LOCAL_FILE_EL}`,
  8131 + auto: false,
  8132 + size: 1024 * 5,
  8133 + url: '/yt/oss/upload',
  8134 + method: 'post',
  8135 + choose(obj) {
  8136 + obj.preview(async function (index, file, result) {
  8137 + const formData = new FormData()
  8138 + formData.set('file', file)
  8139 + $(`#${enumActionEl.IMAGE_UPLOAD_STATE_EL}`).html('上传中...')
  8140 + const [err, res] = await to(ConfigurationNodeApi.uploadImg(formData))
  8141 + if (!err) {
  8142 + $(`#${enumActionEl.IMAGE_PREVIEW_EL}`).attr('src', result)
  8143 + const { fileStaticUri = '' } = res
  8144 + imageState[enumConst.IMAGE_GALLERY_IMAGE_PATH] = fileStaticUri
  8145 + $(`#${enumActionEl.IMAGE_UPLOAD_STATE_EL}`).html('上传成功').css({ color: '#5fb878' })
  8146 + } else {
  8147 + $(`#${enumActionEl.IMAGE_UPLOAD_STATE_EL}`).html('上传失败').css({ color: 'red' })
  8148 + }
  8149 + });
  8150 + },
  8151 + });
  8152 + }
  8153 +
  8154 + /**
  8155 + * @description 切换
  8156 + */
  8157 + function switchUploadImgTypeEventListener() {
  8158 + form.on(`radio(${enumActionEl.SWITCH_IMG_ORIGIN_FILTER})`, event => {
  8159 + const { value } = event
  8160 + imageState = {}
  8161 + if (value === enumImageOriginType.LOCAL) {
  8162 + $(`#${enumActionEl.IMG_SELECT_CONTAINER_EL}`).html(createLocalFileContainer())
  8163 + imageState[enumConst.IMAGE_ORIGIN] = enumImageOriginType.LOCAL
  8164 + uploadFileEvent()
  8165 + } else if (value === enumImageOriginType.GALLERY) {
  8166 + $(`#${enumActionEl.IMG_SELECT_CONTAINER_EL}`).html(createGalleryFileContainer())
  8167 + const defaultChecked = $(`.${enumActionEl.VAR_IMG_CATEGORY}`).eq(0)
  8168 + defaultChecked.addClass(enumActionEl.VAR_IMG_CATEGORY_CHECKED)
  8169 + imageState[enumConst.IMAGE_GALLERY_CATEGORY] = defaultChecked.attr(enumConst.IMAGE_GALLERY_CATEGORY)
  8170 + imageState[enumConst.IMAGE_ORIGIN] = enumImageOriginType.GALLERY
  8171 + generatorSelectImgEventListener()
  8172 + }
  8173 + })
  8174 + }
  8175 +
  8176 +
  8177 + /**
  8178 + * @description 初始化并设置默认值
  8179 + */
  8180 + function initSelectImg() {
  8181 + switchUploadImgTypeEventListener()
  8182 + uploadFileEvent()
  8183 + imageState[enumConst.IMAGE_ORIGIN] = enumImageOriginType.LOCAL
  8184 + form.render()
  8185 + }
  8186 +
  8187 + /**
  8188 + * @description 创建上传图片layer
  8189 + */
  8190 + function createUploadImgLayer() {
  8191 + const content = `
  8192 + <div class="layui-form">
  8193 + <div class="layui-form-item">
  8194 + <div class="var-image__radio">
  8195 + <input type="radio" lay-filter="${enumActionEl.SWITCH_IMG_ORIGIN_FILTER}" name="${enumConst.IMAGE}" value="${enumImageOriginType.LOCAL}" title="本地图片" checked="">
  8196 + <input type="radio" lay-filter="${enumActionEl.SWITCH_IMG_ORIGIN_FILTER}" name="${enumConst.IMAGE}" value="${enumImageOriginType.GALLERY}" title="图库图形">
  8197 + </div>
  8198 + </div>
  8199 + </div>
  8200 + <div id="${enumActionEl.IMG_SELECT_CONTAINER_EL}">
  8201 + ${createLocalFileContainer()}
  8202 + </div>
  8203 + `
  8204 +
  8205 + layer.open({
  8206 + title: '图片',
  8207 + type: 1,
  8208 + content,
  8209 + skin: 'event-layer__override',
  8210 + area: '600px',
  8211 + btn: ["应用", "取消"],
  8212 + shade: ["0.7", "#fafafa"],
  8213 + yes(index) {
  8214 + layer.close(index)
  8215 + if (callback && typeof callback === 'function') callback(imageState)
  8216 + },
  8217 + but2(index, layero) {
  8218 + imageState = {}
  8219 + layer.close(index)
  8220 + },
  8221 + // zIndex: layer.zIndex,
  8222 + async success(layero, index) {
  8223 + // layer.setTop(layero);
  8224 + $(layero).addClass('layui-form').find('.layui-layer-btn0').attr({
  8225 + 'lay-submit': '',
  8226 + 'lay-filter': enumActionEl.IMAGE_LAYER_FILTER,
  8227 + })
  8228 + initSelectImg()
  8229 + },
  8230 + })
  8231 + }
  8232 +
  8233 + createUploadImgLayer()
  8234 +
  8235 + return { record: imageState }
  8236 + }
  8237 +
  8238 +
  8239 + /**
  8240 + * @description TODO 图片上传容器
  8241 + * @param {} option
  8242 + * @param {} option.el
  8243 + */
  8244 + function generateUploadImgContainer({ el }) {
  8245 +
  8246 +
  8247 + const enumConst = {
  8248 + /**
  8249 + * @description 图片来源
  8250 + */
  8251 + IMAGE_ORIGIN: 'imageOrigin',
  8252 +
  8253 + /**
  8254 + * @description 图库图形类别
  8255 + */
  8256 + IMAGE_GALLERY_CATEGORY: 'category',
  8257 +
  8258 + /**
  8259 + * @description 图表图形路径
  8260 + */
  8261 + IMAGE_GALLERY_IMAGE_PATH: 'imagePath',
  8262 + }
  8263 +
  8264 + const enumActionEl = {
  8265 +
  8266 + CONTAINER_FILTER: 'imgContainerFilter',
  8267 +
  8268 + /**
  8269 + * @description
  8270 + */
  8271 + SET_IMG_EL: 'variableImageTableSetImgEl',
  8272 +
  8273 + /**
  8274 + * @description 预览
  8275 + */
  8276 + PREVIEW_IMG_CONTAINER: 'img__container',
  8277 +
  8278 + /**
  8279 + * @description 删除
  8280 + */
  8281 + DEL_PREVIEW_IMG: 'img__delete',
  8282 + }
  8283 +
  8284 + const getFormFilter = `${enumActionEl.CONTAINER_FILTER}-${Date.now() - Math.random()}`
  8285 +
  8286 + function createTemplate() {
  8287 + return `
  8288 + <div class="layui-form ${enumActionEl.SET_IMG_EL}" lay-filter="${getFormFilter}">
  8289 + <input name="${enumConst.IMAGE_GALLERY_CATEGORY}" type="text" style="display: none">
  8290 + <input name="${enumConst.IMAGE_GALLERY_IMAGE_PATH}" type="text" style="display: none">
  8291 + <input name="${enumConst.IMAGE_ORIGIN}" type="text" style="display: none">
  8292 + <div class="${enumActionEl.PREVIEW_IMG_CONTAINER}">
  8293 + <img src="" alt="">
  8294 + <div class="${enumActionEl.DEL_PREVIEW_IMG}">x</div>
  8295 + <div class="add__button">+</div>
  8296 + </div>
  8297 + </div>
  8298 + `
  8299 + }
  8300 +
  8301 + /**
  8302 + * @description 设置回显
  8303 + * @param {} value
  8304 + */
  8305 + function setValue(value = {}) {
  8306 + form.val(getFormFilter, value)
  8307 + }
  8308 +
  8309 + /**
  8310 + * @description 获取值
  8311 + * @returns
  8312 + */
  8313 + function getValue() {
  8314 + return form.val(getFormFilter) || {}
  8315 + }
  8316 +
  8317 + function mount() {
  8318 + if (!el) throw Error('mount element is required')
  8319 + $(el).append(createTemplate())
  8320 + generateEventLinstenner()
  8321 + }
  8322 +
  8323 + // TODO 生成容器监听事件
  8324 + function generateEventLinstenner() {
  8325 + $(el).on('click', `.${enumActionEl.SET_IMG_EL}`, (event) => {
  8326 + if ($(event.target).hasClass(enumActionEl.DEL_PREVIEW_IMG)) {
  8327 + $(el).find(`div[lay-filter="${getFormFilter}"] img`).attr('src', '')
  8328 + form.val(getFormFilter, {
  8329 + [enumConst.IMAGE_ORIGIN]: null,
  8330 + [enumConst.IMAGE_GALLERY_CATEGORY]: null,
  8331 + [enumConst.IMAGE_GALLERY_IMAGE_PATH]: null,
  8332 + })
  8333 + return
  8334 + }
  8335 + generateUploadLayerComponent((imageState) => {
  8336 + $(el).find('img').attr('src', imageState[enumConst.IMAGE_GALLERY_IMAGE_PATH])
  8337 + form.val(getFormFilter, imageState)
  8338 + })
  8339 + })
  8340 + }
  8341 +
  8342 + mount()
  8343 + return { getValue, setValue }
  8344 + }
  8345 +
  8346 + function createHelpMessage(message, className) {
  8347 + return `
  8348 + <div class="thingskit-help-message ${className}">
  8349 + <img src="${Proxy_Prefix}/images/thingskit/question.svg"/>
  8350 + <div class="thingskit-help-container">
  8351 + <div class="thingskit-help-content">${message}</div>
  8352 + <div class="thingskit-help-arrow"></div>
  8353 + </div>
  8354 + </div>
  8355 + `
  8356 + }
  8357 +
  8358 + function hasPermission() {
  8359 + let flag = hasSavePermission()
  8360 + if (!flag) UseLayUi.topErrorMsg('没有权限')
  8361 + return flag
  8362 + }
  8363 +
  8364 + // 异步设置此处才能生效 -- 设置默认select和样式和初始化侧边栏生成组件和事件绑定
  8365 + setTimeout(() => {
  8366 +
  8367 + function proxyFn(fn) {
  8368 + return (...args) => {
  8369 + const currentDataSource = getDataSourceBindValue() || {}
  8370 +
  8371 + to(ConfigurationNodeApi.updateNodeInfo({
  8372 + configurationId,
  8373 + contentId: currentPageId.id,
  8374 + nodeId: graphId,
  8375 + [enumCategory.DATA_SOURCE]: currentDataSource
  8376 + }))
  8377 + fn.apply(null, args)
  8378 + }
  8379 + }
  8380 +
  8381 + // TODO 数据交互事件
  8382 + $(`#${enumDynamicEffectType.IMAGE}`).click({ type: enumDynamicEffectType.IMAGE }, proxyFn(handleSettingVarImage));
  8383 +
  8384 + $(`#${enumInteractionType.DOWN}`).click({ type: enumInteractionType.DOWN }, proxyFn(handleDownOrUpEvent));
  8385 + $(`#${enumInteractionType.UP}`).click({ type: enumInteractionType.UP }, proxyFn(handleDownOrUpEvent));
  8386 + $(`#${enumInteractionType.SINGLE}`).click({ type: enumInteractionType.SINGLE }, proxyFn(handleClickOrDbClick));
  8387 + $(`#${enumInteractionType.DOUBLE}`).click({ type: enumInteractionType.DOUBLE }, proxyFn(handleClickOrDbClick));
  8388 + // 数据动效事件
  8389 + $(`#${enumDynamicEffectType.FLASH}`).click({ type: enumDynamicEffectType.FLASH }, proxyFn(handleDataDynamicEffect));
  8390 + $(`#${enumDynamicEffectType.DISPLAY}`).click({ type: enumDynamicEffectType.DISPLAY }, proxyFn(handleDataDynamicEffect));
  8391 + $(`#${enumDynamicEffectType.ROTATE}`).click({ type: enumDynamicEffectType.ROTATE }, proxyFn(handleDataDynamicEffect));
  8392 + $(`#${enumDynamicEffectType.RUNNING}`).click({ type: enumDynamicEffectType.RUNNING }, proxyFn(handleDataDynamicEffect))
  8393 +
  8394 + $(`#${enumDynamicEffectType.SWITCH}`).click({ type: enumDynamicEffectType.SWITCH }, proxyFn(handleStateSetting))
  8395 +
  8396 + });
  8397 +};
  8398 +
  8399 +/**
  8400 + * Adds the label menu items to the given menu and parent.
  8401 + */
  8402 +StyleFormatPanel = function (format, editorUi, container) {
  8403 + BaseFormatPanel.call(this, format, editorUi, container);
  8404 + this.init();
  8405 +};
  8406 +
  8407 +mxUtils.extend(StyleFormatPanel, BaseFormatPanel);
  8408 +
  8409 +/**
  8410 + *
  8411 + */
  8412 +StyleFormatPanel.prototype.defaultStrokeColor = "black";
  8413 +
  8414 +/**
  8415 + * Adds the label menu items to the given menu and parent.
  8416 + */
  8417 +StyleFormatPanel.prototype.init = function () {
  8418 + var ui = this.editorUi;
  8419 + var editor = ui.editor;
  8420 + var graph = editor.graph;
  8421 + var ss = ui.getSelectionState();
  8422 +
  8423 + if (!ss.containsLabel && ss.cells.length > 0) {
  8424 + if (
  8425 + ss.containsImage &&
  8426 + ss.vertices.length == 1 &&
  8427 + ss.style.shape == "image" &&
  8428 + ss.style.image != null &&
  8429 + ss.style.image.substring(0, 19) == "data:image/svg+xml;"
  8430 + ) {
  8431 + this.container.appendChild(this.addSvgStyles(this.createPanel()));
  8432 + }
  8433 +
  8434 + if (ss.fill) {
  8435 + this.container.appendChild(this.addFill(this.createPanel()));
  8436 + }
  8437 +
  8438 + this.container.appendChild(this.addStroke(this.createPanel()));
  8439 + this.container.appendChild(this.addLineJumps(this.createPanel()));
  8440 + var opacityPanel = this.createRelativeOption(
  8441 + mxResources.get("opacity"),
  8442 + mxConstants.STYLE_OPACITY,
  8443 + );
  8444 + opacityPanel.style.paddingTop = "8px";
  8445 + opacityPanel.style.paddingBottom = "8px";
  8446 + this.container.appendChild(opacityPanel);
  8447 + this.container.appendChild(this.addEffects(this.createPanel()));
  8448 + }
  8449 +
  8450 + // TODO thingsKit 编辑样式 隐藏
  8451 + // var opsPanel = this.addEditOps(this.createPanel());
  8452 +
  8453 + // if (opsPanel.firstChild != null) {
  8454 + // mxUtils.br(opsPanel);
  8455 + // }
  8456 +
  8457 + // this.container.appendChild(this.addStyleOps(opsPanel));
  8458 +};
  8459 +
  8460 +/**
  8461 + * Use browser for parsing CSS.
  8462 + */
  8463 +StyleFormatPanel.prototype.getCssRules = function (css) {
  8464 + var doc = document.implementation.createHTMLDocument("");
  8465 + var styleElement = document.createElement("style");
  8466 +
  8467 + mxUtils.setTextContent(styleElement, css);
  8468 + doc.body.appendChild(styleElement);
  8469 +
  8470 + return styleElement.sheet.cssRules;
  8471 +};
  8472 +
  8473 +/**
  8474 + * Adds the label menu items to the given menu and parent.
  8475 + */
  8476 +StyleFormatPanel.prototype.addSvgStyles = function (container) {
  8477 + var ui = this.editorUi;
  8478 + var graph = ui.editor.graph;
  8479 + var ss = ui.getSelectionState();
  8480 + container.style.paddingTop = "6px";
  8481 + container.style.paddingBottom = "6px";
  8482 + container.style.fontWeight = "bold";
  8483 + container.style.display = "none";
  8484 +
  8485 + try {
  8486 + var exp = ss.style.editableCssRules;
  8487 +
  8488 + if (exp != null) {
  8489 + var regex = new RegExp(exp);
  8490 +
  8491 + var data = ss.style.image.substring(ss.style.image.indexOf(",") + 1);
  8492 + var xml = window.atob ? atob(data) : Base64.decode(data, true);
  8493 + var svg = mxUtils.parseXml(xml);
  8494 +
  8495 + if (svg != null) {
  8496 + var styles = svg.getElementsByTagName("style");
  8497 +
  8498 + for (var i = 0; i < styles.length; i++) {
  8499 + var rules = this.getCssRules(mxUtils.getTextContent(styles[i]));
  8500 +
  8501 + for (var j = 0; j < rules.length; j++) {
  8502 + this.addSvgRule(
  8503 + container,
  8504 + rules[j],
  8505 + svg,
  8506 + styles[i],
  8507 + rules,
  8508 + j,
  8509 + regex,
  8510 + );
  8511 + }
  8512 + }
  8513 + }
  8514 + }
  8515 + } catch (e) {
  8516 + // ignore
  8517 + }
  8518 +
  8519 + return container;
  8520 +};
  8521 +
  8522 +/**
  8523 + * Adds the label menu items to the given menu and parent.
  8524 + */
  8525 +StyleFormatPanel.prototype.addSvgRule = function (
  8526 + container,
  8527 + rule,
  8528 + svg,
  8529 + styleElem,
  8530 + rules,
  8531 + ruleIndex,
  8532 + regex,
  8533 +) {
  8534 + var ui = this.editorUi;
  8535 + var graph = ui.editor.graph;
  8536 +
  8537 + if (regex.test(rule.selectorText)) {
  8538 + function rgb2hex(rgb) {
  8539 + rgb = rgb.match(
  8540 + /^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i,
  8541 + );
  8542 +
  8543 + return rgb && rgb.length === 4
  8544 + ? "#" +
  8545 + ("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) +
  8546 + ("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) +
  8547 + ("0" + parseInt(rgb[3], 10).toString(16)).slice(-2)
  8548 + : "";
  8549 + }
  8550 +
  8551 + var addStyleRule = mxUtils.bind(this, function (rule, key, label) {
  8552 + var value = mxUtils.trim(rule.style[key]);
  8553 +
  8554 + if (value != "" && value.substring(0, 4) != "url(") {
  8555 + var option = this.createColorOption(
  8556 + label + " " + rule.selectorText,
  8557 + function () {
  8558 + return rgb2hex(value);
  8559 + },
  8560 + mxUtils.bind(this, function (color) {
  8561 + rules[ruleIndex].style[key] = color;
  8562 + var cssTxt = "";
  8563 +
  8564 + for (var i = 0; i < rules.length; i++) {
  8565 + cssTxt += rules[i].cssText + " ";
  8566 + }
  8567 +
  8568 + styleElem.textContent = cssTxt;
  8569 + var xml = mxUtils.getXml(svg.documentElement);
  8570 +
  8571 + graph.setCellStyles(
  8572 + mxConstants.STYLE_IMAGE,
  8573 + "data:image/svg+xml," +
  8574 + (window.btoa ? btoa(xml) : Base64.encode(xml, true)),
  8575 + ui.getSelectionState().cells,
  8576 + );
  8577 + }),
  8578 + "#ffffff",
  8579 + {
  8580 + install: function (apply) {
  8581 + // ignore
  8582 + },
  8583 + destroy: function () {
  8584 + // ignore
  8585 + },
  8586 + },
  8587 + );
  8588 +
  8589 + container.appendChild(option);
  8590 +
  8591 + // Shows container if rules are added
  8592 + container.style.display = "";
  8593 + }
  8594 + });
  8595 +
  8596 + addStyleRule(rule, "fill", mxResources.get("fill"));
  8597 + addStyleRule(rule, "stroke", mxResources.get("line"));
  8598 + addStyleRule(rule, "stop-color", mxResources.get("gradient"));
  8599 + }
  8600 +};
  8601 +
  8602 +/**
  8603 + * Adds the label menu items to the given menu and parent.
  8604 + */
  8605 +StyleFormatPanel.prototype.addEditOps = function (div) {
  8606 + var ss = this.editorUi.getSelectionState();
  8607 + var btn = null;
  8608 +
  8609 + if (ss.cells.length == 1) {
  8610 + btn = mxUtils.button(
  8611 + mxResources.get("editStyle"),
  8612 + mxUtils.bind(this, function (evt) {
  8613 + this.editorUi.actions.get("editStyle").funct();
  8614 + }),
  8615 + );
  8616 +
  8617 + btn.setAttribute(
  8618 + "title",
  8619 + mxResources.get("editStyle") +
  8620 + " (" +
  8621 + this.editorUi.actions.get("editStyle").shortcut +
  8622 + ")",
  8623 + );
  8624 + btn.style.width = "210px";
  8625 + btn.style.marginBottom = "2px";
  8626 +
  8627 + div.appendChild(btn);
  8628 + }
  8629 +
  8630 + if (ss.image && ss.cells.length > 0) {
  8631 + var btn2 = mxUtils.button(
  8632 + mxResources.get("editImage"),
  8633 + mxUtils.bind(this, function (evt) {
  8634 + this.editorUi.actions.get("image").funct();
  8635 + }),
  8636 + );
  8637 +
  8638 + btn2.setAttribute("title", mxResources.get("editImage"));
  8639 + btn2.style.marginBottom = "2px";
  8640 +
  8641 + if (btn == null) {
  8642 + btn2.style.width = "210px";
  8643 + } else {
  8644 + btn.style.width = "104px";
  8645 + btn2.style.width = "104px";
  8646 + btn2.style.marginLeft = "2px";
  8647 + }
  8648 +
  8649 + div.appendChild(btn2);
  8650 + }
  8651 +
  8652 + return div;
  8653 +};
  8654 +
  8655 +/**
  8656 + * Adds the label menu items to the given menu and parent.
  8657 + */
  8658 +StyleFormatPanel.prototype.addFill = function (container) {
  8659 + var ui = this.editorUi;
  8660 + var graph = ui.editor.graph;
  8661 + var ss = ui.getSelectionState();
  8662 + container.style.paddingTop = "6px";
  8663 + container.style.paddingBottom = "6px";
  8664 +
  8665 + // Adds gradient direction option
  8666 + var gradientSelect = document.createElement("select");
  8667 + gradientSelect.style.position = "absolute";
  8668 + gradientSelect.style.left = "104px";
  8669 + gradientSelect.style.width = "70px";
  8670 + gradientSelect.style.height = "22px";
  8671 + gradientSelect.style.padding = "0px";
  8672 + gradientSelect.style.marginTop = "-3px";
  8673 + gradientSelect.style.borderRadius = "4px";
  8674 + gradientSelect.style.border = "1px solid rgb(160, 160, 160)";
  8675 + gradientSelect.style.boxSizing = "border-box";
  8676 +
  8677 + var fillStyleSelect = gradientSelect.cloneNode(false);
  8678 +
  8679 + // Stops events from bubbling to color option event handler
  8680 + mxEvent.addListener(gradientSelect, "click", function (evt) {
  8681 + mxEvent.consume(evt);
  8682 + });
  8683 + mxEvent.addListener(fillStyleSelect, "click", function (evt) {
  8684 + mxEvent.consume(evt);
  8685 + });
  8686 +
  8687 + var defs =
  8688 + ss.vertices.length >= 1
  8689 + ? graph.stylesheet.getDefaultVertexStyle()
  8690 + : graph.stylesheet.getDefaultEdgeStyle();
  8691 +
  8692 + var gradientPanel = this.createCellColorOption(
  8693 + mxResources.get("gradient"),
  8694 + mxConstants.STYLE_GRADIENTCOLOR,
  8695 + defs[mxConstants.STYLE_GRADIENTCOLOR] != null
  8696 + ? defs[mxConstants.STYLE_GRADIENTCOLOR]
  8697 + : "#ffffff",
  8698 + function (color) {
  8699 + if (color == null || color == mxConstants.NONE) {
  8700 + gradientSelect.style.display = "none";
  8701 + } else {
  8702 + gradientSelect.style.display = "";
  8703 + }
  8704 + },
  8705 + function (color) {
  8706 + graph.updateCellStyles(
  8707 + { gradientColor: color },
  8708 + graph.getSelectionCells(),
  8709 + );
  8710 + },
  8711 + );
  8712 +
  8713 + var fillKey =
  8714 + ss.style.shape == "image"
  8715 + ? mxConstants.STYLE_IMAGE_BACKGROUND
  8716 + : mxConstants.STYLE_FILLCOLOR;
  8717 +
  8718 + var fillPanel = this.createCellColorOption(
  8719 + mxResources.get("fill"),
  8720 + fillKey,
  8721 + "default",
  8722 + null,
  8723 + mxUtils.bind(this, function (color) {
  8724 + graph.setCellStyles(fillKey, color, ss.cells);
  8725 + }),
  8726 + graph.shapeBackgroundColor,
  8727 + );
  8728 +
  8729 + fillPanel.style.fontWeight = "bold";
  8730 + var tmpColor = mxUtils.getValue(ss.style, fillKey, null);
  8731 + gradientPanel.style.display =
  8732 + tmpColor != null &&
  8733 + tmpColor != mxConstants.NONE &&
  8734 + ss.fill &&
  8735 + ss.style.shape != "image"
  8736 + ? ""
  8737 + : "none";
  8738 +
  8739 + var directions = [
  8740 + mxConstants.DIRECTION_NORTH,
  8741 + mxConstants.DIRECTION_EAST,
  8742 + mxConstants.DIRECTION_SOUTH,
  8743 + mxConstants.DIRECTION_WEST,
  8744 + mxConstants.DIRECTION_RADIAL,
  8745 + ];
  8746 +
  8747 + for (var i = 0; i < directions.length; i++) {
  8748 + var gradientOption = document.createElement("option");
  8749 + gradientOption.setAttribute("value", directions[i]);
  8750 + mxUtils.write(gradientOption, mxResources.get(directions[i]));
  8751 + gradientSelect.appendChild(gradientOption);
  8752 + }
  8753 +
  8754 + gradientPanel.appendChild(gradientSelect);
  8755 +
  8756 + for (var i = 0; i < Editor.roughFillStyles.length; i++) {
  8757 + var fillStyleOption = document.createElement("option");
  8758 + fillStyleOption.setAttribute("value", Editor.roughFillStyles[i].val);
  8759 + mxUtils.write(fillStyleOption, Editor.roughFillStyles[i].dispName);
  8760 + fillStyleSelect.appendChild(fillStyleOption);
  8761 + }
  8762 +
  8763 + fillPanel.appendChild(fillStyleSelect);
  8764 +
  8765 + var listener = mxUtils.bind(this, function () {
  8766 + ss = ui.getSelectionState();
  8767 + var value = mxUtils.getValue(
  8768 + ss.style,
  8769 + mxConstants.STYLE_GRADIENT_DIRECTION,
  8770 + mxConstants.DIRECTION_SOUTH,
  8771 + );
  8772 + var fillStyle = mxUtils.getValue(ss.style, "fillStyle", "auto");
  8773 +
  8774 + // Handles empty string which is not allowed as a value
  8775 + if (value == "") {
  8776 + value = mxConstants.DIRECTION_SOUTH;
  8777 + }
  8778 +
  8779 + gradientSelect.value = value;
  8780 + fillStyleSelect.value = fillStyle;
  8781 + container.style.display = ss.fill ? "" : "none";
  8782 +
  8783 + var fillColor = mxUtils.getValue(ss.style, fillKey, null);
  8784 +
  8785 + if (
  8786 + !ss.fill ||
  8787 + fillColor == null ||
  8788 + fillColor == mxConstants.NONE ||
  8789 + ss.style.shape == "filledEdge"
  8790 + ) {
  8791 + fillStyleSelect.style.display = "none";
  8792 + gradientPanel.style.display = "none";
  8793 + } else {
  8794 + fillStyleSelect.style.display = ss.style.sketch == "1" ? "" : "none";
  8795 + gradientPanel.style.display =
  8796 + !ss.containsImage &&
  8797 + (ss.style.sketch != "1" || fillStyle == "solid" || fillStyle == "auto")
  8798 + ? ""
  8799 + : "none";
  8800 + }
  8801 + });
  8802 +
  8803 + graph.getModel().addListener(mxEvent.CHANGE, listener);
  8804 + this.listeners.push({
  8805 + destroy: function () {
  8806 + graph.getModel().removeListener(listener);
  8807 + },
  8808 + });
  8809 + listener();
  8810 +
  8811 + mxEvent.addListener(gradientSelect, "change", function (evt) {
  8812 + graph.setCellStyles(
  8813 + mxConstants.STYLE_GRADIENT_DIRECTION,
  8814 + gradientSelect.value,
  8815 + ss.cells,
  8816 + );
  8817 + ui.fireEvent(
  8818 + new mxEventObject(
  8819 + "styleChanged",
  8820 + "keys",
  8821 + [mxConstants.STYLE_GRADIENT_DIRECTION],
  8822 + "values",
  8823 + [gradientSelect.value],
  8824 + "cells",
  8825 + ss.cells,
  8826 + ),
  8827 + );
  8828 + mxEvent.consume(evt);
  8829 + });
  8830 +
  8831 + mxEvent.addListener(fillStyleSelect, "change", function (evt) {
  8832 + graph.setCellStyles("fillStyle", fillStyleSelect.value, ss.cells);
  8833 + ui.fireEvent(
  8834 + new mxEventObject(
  8835 + "styleChanged",
  8836 + "keys",
  8837 + ["fillStyle"],
  8838 + "values",
  8839 + [fillStyleSelect.value],
  8840 + "cells",
  8841 + ss.cells,
  8842 + ),
  8843 + );
  8844 + mxEvent.consume(evt);
  8845 + });
  8846 +
  8847 + container.appendChild(fillPanel);
  8848 + container.appendChild(gradientPanel);
  8849 +
  8850 + // Adds custom colors
  8851 + var custom = this.getCustomColors();
  8852 +
  8853 + for (var i = 0; i < custom.length; i++) {
  8854 + container.appendChild(
  8855 + this.createCellColorOption(
  8856 + custom[i].title,
  8857 + custom[i].key,
  8858 + custom[i].defaultValue,
  8859 + ),
  8860 + );
  8861 + }
  8862 +
  8863 + return container;
  8864 +};
  8865 +
  8866 +/**
  8867 + * Adds the label menu items to the given menu and parent.
  8868 + */
  8869 +StyleFormatPanel.prototype.getCustomColors = function () {
  8870 + var ss = this.editorUi.getSelectionState();
  8871 + var result = [];
  8872 +
  8873 + if (ss.swimlane) {
  8874 + result.push({
  8875 + title: mxResources.get("laneColor"),
  8876 + key: "swimlaneFillColor",
  8877 + defaultValue: "default",
  8878 + });
  8879 + }
  8880 +
  8881 + return result;
  8882 +};
  8883 +
  8884 +/**
  8885 + * Adds the label menu items to the given menu and parent.
  8886 + */
  8887 +StyleFormatPanel.prototype.addStroke = function (container) {
  8888 + var ui = this.editorUi;
  8889 + var graph = ui.editor.graph;
  8890 + var ss = ui.getSelectionState();
  8891 +
  8892 + container.style.paddingTop = "6px";
  8893 + container.style.paddingBottom = "4px";
  8894 + container.style.whiteSpace = "normal";
  8895 +
  8896 + var colorPanel = document.createElement("div");
  8897 + colorPanel.style.fontWeight = "bold";
  8898 +
  8899 + if (!ss.stroke) {
  8900 + colorPanel.style.display = "none";
  8901 + }
  8902 +
  8903 + // Adds gradient direction option
  8904 + var styleSelect = document.createElement("select");
  8905 + styleSelect.style.position = "absolute";
  8906 + styleSelect.style.height = "22px";
  8907 + styleSelect.style.padding = "0px";
  8908 + styleSelect.style.marginTop = "-3px";
  8909 + styleSelect.style.boxSizing = "border-box";
  8910 + styleSelect.style.left = "94px";
  8911 + styleSelect.style.width = "80px";
  8912 + styleSelect.style.border = "1px solid rgb(160, 160, 160)";
  8913 + styleSelect.style.borderRadius = "4px";
  8914 +
  8915 + var styles = ["sharp", "rounded", "curved"];
  8916 +
  8917 + for (var i = 0; i < styles.length; i++) {
  8918 + var styleOption = document.createElement("option");
  8919 + styleOption.setAttribute("value", styles[i]);
  8920 + mxUtils.write(styleOption, mxResources.get(styles[i]));
  8921 + styleSelect.appendChild(styleOption);
  8922 + }
  8923 +
  8924 + mxEvent.addListener(styleSelect, "change", function (evt) {
  8925 + graph.getModel().beginUpdate();
  8926 + try {
  8927 + var keys = [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED];
  8928 + // Default for rounded is 1
  8929 + var values = ["0", null];
  8930 +
  8931 + if (styleSelect.value == "rounded") {
  8932 + values = ["1", null];
  8933 + } else if (styleSelect.value == "curved") {
  8934 + values = [null, "1"];
  8935 + }
  8936 +
  8937 + for (var i = 0; i < keys.length; i++) {
  8938 + graph.setCellStyles(keys[i], values[i], ss.cells);
  8939 + }
  8940 +
  8941 + ui.fireEvent(
  8942 + new mxEventObject(
  8943 + "styleChanged",
  8944 + "keys",
  8945 + keys,
  8946 + "values",
  8947 + values,
  8948 + "cells",
  8949 + ss.cells,
  8950 + ),
  8951 + );
  8952 + } finally {
  8953 + graph.getModel().endUpdate();
  8954 + }
  8955 +
  8956 + mxEvent.consume(evt);
  8957 + });
  8958 +
  8959 + // Stops events from bubbling to color option event handler
  8960 + mxEvent.addListener(styleSelect, "click", function (evt) {
  8961 + mxEvent.consume(evt);
  8962 + });
  8963 +
  8964 + var strokeKey =
  8965 + ss.style.shape == "image"
  8966 + ? mxConstants.STYLE_IMAGE_BORDER
  8967 + : mxConstants.STYLE_STROKECOLOR;
  8968 + var label =
  8969 + ss.style.shape == "image"
  8970 + ? mxResources.get("border")
  8971 + : mxResources.get("line");
  8972 +
  8973 + var lineColor = this.createCellColorOption(
  8974 + label,
  8975 + strokeKey,
  8976 + "default",
  8977 + null,
  8978 + mxUtils.bind(this, function (color) {
  8979 + graph.setCellStyles(strokeKey, color, ss.cells);
  8980 + }),
  8981 + graph.shapeForegroundColor,
  8982 + );
  8983 +
  8984 + lineColor.appendChild(styleSelect);
  8985 + colorPanel.appendChild(lineColor);
  8986 +
  8987 + // Used if only edges selected
  8988 + var stylePanel = colorPanel.cloneNode(false);
  8989 + stylePanel.style.fontWeight = "normal";
  8990 + stylePanel.style.whiteSpace = "nowrap";
  8991 + stylePanel.style.position = "relative";
  8992 + stylePanel.style.paddingLeft = "0px";
  8993 + stylePanel.style.marginBottom = "2px";
  8994 + stylePanel.style.overflow = "hidden";
  8995 + stylePanel.style.marginTop = "2px";
  8996 + stylePanel.style.width = "220px";
  8997 + stylePanel.className = "geToolbarContainer";
  8998 +
  8999 + var addItem = mxUtils.bind(
  9000 + this,
  9001 + function (menu, width, cssName, keys, values) {
  9002 + var item = this.editorUi.menus.styleChange(
  9003 + menu,
  9004 + "",
  9005 + keys,
  9006 + values,
  9007 + "geIcon",
  9008 + null,
  9009 + );
  9010 +
  9011 + var pat = document.createElement("div");
  9012 + pat.style.width = width + "px";
  9013 + pat.style.height = "1px";
  9014 + pat.style.borderBottom = "1px " + cssName + " " + this.defaultStrokeColor;
  9015 + pat.style.paddingTop = "6px";
  9016 +
  9017 + item.firstChild.firstChild.style.padding = "0px 4px 0px 4px";
  9018 + item.firstChild.firstChild.style.width = width + "px";
  9019 + item.firstChild.firstChild.appendChild(pat);
  9020 +
  9021 + return item;
  9022 + },
  9023 + );
  9024 +
  9025 + var pattern = this.editorUi.toolbar.addMenuFunctionInContainer(
  9026 + stylePanel,
  9027 + "geSprite-orthogonal",
  9028 + mxResources.get("pattern"),
  9029 + false,
  9030 + mxUtils.bind(this, function (menu) {
  9031 + addItem(
  9032 + menu,
  9033 + 75,
  9034 + "solid",
  9035 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9036 + [null, null],
  9037 + ).setAttribute("title", mxResources.get("solid"));
  9038 + addItem(
  9039 + menu,
  9040 + 75,
  9041 + "dashed",
  9042 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9043 + ["1", null],
  9044 + ).setAttribute("title", mxResources.get("dashed"));
  9045 + addItem(
  9046 + menu,
  9047 + 75,
  9048 + "dotted",
  9049 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9050 + ["1", "1 1"],
  9051 + ).setAttribute("title", mxResources.get("dotted") + " (1)");
  9052 + addItem(
  9053 + menu,
  9054 + 75,
  9055 + "dotted",
  9056 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9057 + ["1", "1 2"],
  9058 + ).setAttribute("title", mxResources.get("dotted") + " (2)");
  9059 + addItem(
  9060 + menu,
  9061 + 75,
  9062 + "dotted",
  9063 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9064 + ["1", "1 4"],
  9065 + ).setAttribute("title", mxResources.get("dotted") + " (3)");
  9066 + }),
  9067 + );
  9068 +
  9069 + // Used for mixed selection (vertices and edges)
  9070 + var altStylePanel = stylePanel.cloneNode(false);
  9071 +
  9072 + var edgeShape = this.editorUi.toolbar.addMenuFunctionInContainer(
  9073 + altStylePanel,
  9074 + "geSprite-connection",
  9075 + mxResources.get("connection"),
  9076 + false,
  9077 + mxUtils.bind(this, function (menu) {
  9078 + this.editorUi.menus
  9079 + .styleChange(
  9080 + menu,
  9081 + "",
  9082 + [
  9083 + mxConstants.STYLE_SHAPE,
  9084 + mxConstants.STYLE_STARTSIZE,
  9085 + mxConstants.STYLE_ENDSIZE,
  9086 + "width",
  9087 + ],
  9088 + [null, null, null, null],
  9089 + "geIcon geSprite geSprite-connection",
  9090 + null,
  9091 + true,
  9092 + )
  9093 + .setAttribute("title", mxResources.get("line"));
  9094 + this.editorUi.menus
  9095 + .styleChange(
  9096 + menu,
  9097 + "",
  9098 + [
  9099 + mxConstants.STYLE_SHAPE,
  9100 + mxConstants.STYLE_STARTSIZE,
  9101 + mxConstants.STYLE_ENDSIZE,
  9102 + "width",
  9103 + ],
  9104 + ["link", null, null, null],
  9105 + "geIcon geSprite geSprite-linkedge",
  9106 + null,
  9107 + true,
  9108 + )
  9109 + .setAttribute("title", mxResources.get("link"));
  9110 + this.editorUi.menus
  9111 + .styleChange(
  9112 + menu,
  9113 + "",
  9114 + [
  9115 + mxConstants.STYLE_SHAPE,
  9116 + mxConstants.STYLE_STARTSIZE,
  9117 + mxConstants.STYLE_ENDSIZE,
  9118 + "width",
  9119 + ],
  9120 + ["flexArrow", null, null, null],
  9121 + "geIcon geSprite geSprite-arrow",
  9122 + null,
  9123 + true,
  9124 + )
  9125 + .setAttribute("title", mxResources.get("arrow"));
  9126 + this.editorUi.menus
  9127 + .styleChange(
  9128 + menu,
  9129 + "",
  9130 + [
  9131 + mxConstants.STYLE_SHAPE,
  9132 + mxConstants.STYLE_STARTSIZE,
  9133 + mxConstants.STYLE_ENDSIZE,
  9134 + "width",
  9135 + ],
  9136 + ["arrow", null, null, null],
  9137 + "geIcon geSprite geSprite-simplearrow",
  9138 + null,
  9139 + true,
  9140 + )
  9141 + .setAttribute("title", mxResources.get("simpleArrow"));
  9142 + }),
  9143 + );
  9144 +
  9145 + var altPattern = this.editorUi.toolbar.addMenuFunctionInContainer(
  9146 + altStylePanel,
  9147 + "geSprite-orthogonal",
  9148 + mxResources.get("pattern"),
  9149 + false,
  9150 + mxUtils.bind(this, function (menu) {
  9151 + addItem(
  9152 + menu,
  9153 + 33,
  9154 + "solid",
  9155 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9156 + [null, null],
  9157 + ).setAttribute("title", mxResources.get("solid"));
  9158 + addItem(
  9159 + menu,
  9160 + 33,
  9161 + "dashed",
  9162 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9163 + ["1", null],
  9164 + ).setAttribute("title", mxResources.get("dashed"));
  9165 + addItem(
  9166 + menu,
  9167 + 33,
  9168 + "dotted",
  9169 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9170 + ["1", "1 1"],
  9171 + ).setAttribute("title", mxResources.get("dotted") + " (1)");
  9172 + addItem(
  9173 + menu,
  9174 + 33,
  9175 + "dotted",
  9176 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9177 + ["1", "1 2"],
  9178 + ).setAttribute("title", mxResources.get("dotted") + " (2)");
  9179 + addItem(
  9180 + menu,
  9181 + 33,
  9182 + "dotted",
  9183 + [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  9184 + ["1", "1 4"],
  9185 + ).setAttribute("title", mxResources.get("dotted") + " (3)");
  9186 + }),
  9187 + );
  9188 +
  9189 + var stylePanel2 = stylePanel.cloneNode(false);
  9190 +
  9191 + // Stroke width
  9192 + var input = document.createElement("input");
  9193 + input.style.position = "absolute";
  9194 + input.style.textAlign = "right";
  9195 + input.style.marginTop = "2px";
  9196 + input.style.width = "52px";
  9197 + input.style.height = "21px";
  9198 + input.style.left = "146px";
  9199 + input.style.border = "1px solid rgb(160, 160, 160)";
  9200 + input.style.borderRadius = "4px";
  9201 + input.style.boxSizing = "border-box";
  9202 + input.setAttribute("title", mxResources.get("linewidth"));
  9203 +
  9204 + stylePanel.appendChild(input);
  9205 +
  9206 + var altInput = input.cloneNode(true);
  9207 + altStylePanel.appendChild(altInput);
  9208 +
  9209 + function update(evt) {
  9210 + // Maximum stroke width is 999
  9211 + var value = parseFloat(input.value);
  9212 + value = Math.min(999, Math.max(0, isNaN(value) ? 1 : value));
  9213 +
  9214 + if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) {
  9215 + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, ss.cells);
  9216 + ui.fireEvent(
  9217 + new mxEventObject(
  9218 + "styleChanged",
  9219 + "keys",
  9220 + [mxConstants.STYLE_STROKEWIDTH],
  9221 + "values",
  9222 + [value],
  9223 + "cells",
  9224 + ss.cells,
  9225 + ),
  9226 + );
  9227 + }
  9228 +
  9229 + input.value = value + " pt";
  9230 + mxEvent.consume(evt);
  9231 + }
  9232 +
  9233 + function altUpdate(evt) {
  9234 + // Maximum stroke width is 999
  9235 + var value = parseFloat(altInput.value);
  9236 + value = Math.min(999, Math.max(0, isNaN(value) ? 1 : value));
  9237 +
  9238 + if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) {
  9239 + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, ss.cells);
  9240 + ui.fireEvent(
  9241 + new mxEventObject(
  9242 + "styleChanged",
  9243 + "keys",
  9244 + [mxConstants.STYLE_STROKEWIDTH],
  9245 + "values",
  9246 + [value],
  9247 + "cells",
  9248 + ss.cells,
  9249 + ),
  9250 + );
  9251 + }
  9252 +
  9253 + altInput.value = value + " pt";
  9254 + mxEvent.consume(evt);
  9255 + }
  9256 +
  9257 + var stepper = this.createStepper(input, update, 1, 9);
  9258 + stepper.style.display = input.style.display;
  9259 + stepper.style.marginTop = "2px";
  9260 + stepper.style.left = "198px";
  9261 + stylePanel.appendChild(stepper);
  9262 +
  9263 + var altStepper = this.createStepper(altInput, altUpdate, 1, 9);
  9264 + altStepper.style.display = altInput.style.display;
  9265 + altStepper.style.marginTop = "2px";
  9266 + altInput.style.position = "absolute";
  9267 + altStepper.style.left = "198px";
  9268 + altStylePanel.appendChild(altStepper);
  9269 +
  9270 + mxEvent.addListener(input, "blur", update);
  9271 + mxEvent.addListener(input, "change", update);
  9272 +
  9273 + mxEvent.addListener(altInput, "blur", altUpdate);
  9274 + mxEvent.addListener(altInput, "change", altUpdate);
  9275 +
  9276 + var edgeStyle = this.editorUi.toolbar.addMenuFunctionInContainer(
  9277 + stylePanel2,
  9278 + "geSprite-orthogonal",
  9279 + mxResources.get("waypoints"),
  9280 + false,
  9281 + mxUtils.bind(this, function (menu) {
  9282 + if (ss.style.shape != "arrow") {
  9283 + this.editorUi.menus
  9284 + .edgeStyleChange(
  9285 + menu,
  9286 + "",
  9287 + [
  9288 + mxConstants.STYLE_EDGE,
  9289 + mxConstants.STYLE_CURVED,
  9290 + mxConstants.STYLE_NOEDGESTYLE,
  9291 + ],
  9292 + [null, null, null],
  9293 + "geIcon geSprite geSprite-straight",
  9294 + null,
  9295 + true,
  9296 + )
  9297 + .setAttribute("title", mxResources.get("straight"));
  9298 + this.editorUi.menus
  9299 + .edgeStyleChange(
  9300 + menu,
  9301 + "",
  9302 + [
  9303 + mxConstants.STYLE_EDGE,
  9304 + mxConstants.STYLE_CURVED,
  9305 + mxConstants.STYLE_NOEDGESTYLE,
  9306 + ],
  9307 + ["orthogonalEdgeStyle", null, null],
  9308 + "geIcon geSprite geSprite-orthogonal",
  9309 + null,
  9310 + true,
  9311 + )
  9312 + .setAttribute("title", mxResources.get("orthogonal"));
  9313 + this.editorUi.menus
  9314 + .edgeStyleChange(
  9315 + menu,
  9316 + "",
  9317 + [
  9318 + mxConstants.STYLE_EDGE,
  9319 + mxConstants.STYLE_ELBOW,
  9320 + mxConstants.STYLE_CURVED,
  9321 + mxConstants.STYLE_NOEDGESTYLE,
  9322 + ],
  9323 + ["elbowEdgeStyle", null, null, null],
  9324 + "geIcon geSprite geSprite-horizontalelbow",
  9325 + null,
  9326 + true,
  9327 + )
  9328 + .setAttribute("title", mxResources.get("simple"));
  9329 + this.editorUi.menus
  9330 + .edgeStyleChange(
  9331 + menu,
  9332 + "",
  9333 + [
  9334 + mxConstants.STYLE_EDGE,
  9335 + mxConstants.STYLE_ELBOW,
  9336 + mxConstants.STYLE_CURVED,
  9337 + mxConstants.STYLE_NOEDGESTYLE,
  9338 + ],
  9339 + ["elbowEdgeStyle", "vertical", null, null],
  9340 + "geIcon geSprite geSprite-verticalelbow",
  9341 + null,
  9342 + true,
  9343 + )
  9344 + .setAttribute("title", mxResources.get("simple"));
  9345 + this.editorUi.menus
  9346 + .edgeStyleChange(
  9347 + menu,
  9348 + "",
  9349 + [
  9350 + mxConstants.STYLE_EDGE,
  9351 + mxConstants.STYLE_ELBOW,
  9352 + mxConstants.STYLE_CURVED,
  9353 + mxConstants.STYLE_NOEDGESTYLE,
  9354 + ],
  9355 + ["isometricEdgeStyle", null, null, null],
  9356 + "geIcon geSprite geSprite-horizontalisometric",
  9357 + null,
  9358 + true,
  9359 + )
  9360 + .setAttribute("title", mxResources.get("isometric"));
  9361 + this.editorUi.menus
  9362 + .edgeStyleChange(
  9363 + menu,
  9364 + "",
  9365 + [
  9366 + mxConstants.STYLE_EDGE,
  9367 + mxConstants.STYLE_ELBOW,
  9368 + mxConstants.STYLE_CURVED,
  9369 + mxConstants.STYLE_NOEDGESTYLE,
  9370 + ],
  9371 + ["isometricEdgeStyle", "vertical", null, null],
  9372 + "geIcon geSprite geSprite-verticalisometric",
  9373 + null,
  9374 + true,
  9375 + )
  9376 + .setAttribute("title", mxResources.get("isometric"));
  9377 +
  9378 + if (ss.style.shape == "connector") {
  9379 + this.editorUi.menus
  9380 + .edgeStyleChange(
  9381 + menu,
  9382 + "",
  9383 + [
  9384 + mxConstants.STYLE_EDGE,
  9385 + mxConstants.STYLE_CURVED,
  9386 + mxConstants.STYLE_NOEDGESTYLE,
  9387 + ],
  9388 + ["orthogonalEdgeStyle", "1", null],
  9389 + "geIcon geSprite geSprite-curved",
  9390 + null,
  9391 + true,
  9392 + )
  9393 + .setAttribute("title", mxResources.get("curved"));
  9394 + }
  9395 +
  9396 + this.editorUi.menus
  9397 + .edgeStyleChange(
  9398 + menu,
  9399 + "",
  9400 + [
  9401 + mxConstants.STYLE_EDGE,
  9402 + mxConstants.STYLE_CURVED,
  9403 + mxConstants.STYLE_NOEDGESTYLE,
  9404 + ],
  9405 + ["entityRelationEdgeStyle", null, null],
  9406 + "geIcon geSprite geSprite-entity",
  9407 + null,
  9408 + true,
  9409 + )
  9410 + .setAttribute("title", mxResources.get("entityRelation"));
  9411 + }
  9412 + }),
  9413 + );
  9414 +
  9415 + var lineStart = this.editorUi.toolbar.addMenuFunctionInContainer(
  9416 + stylePanel2,
  9417 + "geSprite-startclassic",
  9418 + mxResources.get("linestart"),
  9419 + false,
  9420 + mxUtils.bind(this, function (menu) {
  9421 + if (
  9422 + ss.style.shape == "connector" ||
  9423 + ss.style.shape == "flexArrow" ||
  9424 + ss.style.shape == "filledEdge"
  9425 + ) {
  9426 + var item = this.editorUi.menus.edgeStyleChange(
  9427 + menu,
  9428 + "",
  9429 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9430 + [mxConstants.NONE, 0],
  9431 + "geIcon",
  9432 + null,
  9433 + false,
  9434 + );
  9435 + item.setAttribute("title", mxResources.get("none"));
  9436 +
  9437 + var font = document.createElement("font");
  9438 + font.style.fontSize = "10px";
  9439 + mxUtils.write(font, mxResources.get("none"));
  9440 + item.firstChild.firstChild.appendChild(font);
  9441 +
  9442 + if (ss.style.shape == "connector" || ss.style.shape == "filledEdge") {
  9443 + Format.processMenuIcon(
  9444 + this.editorUi.menus.edgeStyleChange(
  9445 + menu,
  9446 + "",
  9447 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9448 + [mxConstants.ARROW_CLASSIC, 1],
  9449 + null,
  9450 + null,
  9451 + false,
  9452 + Format.classicFilledMarkerImage.src,
  9453 + ),
  9454 + );
  9455 + Format.processMenuIcon(
  9456 + this.editorUi.menus.edgeStyleChange(
  9457 + menu,
  9458 + "",
  9459 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9460 + [mxConstants.ARROW_CLASSIC_THIN, 1],
  9461 + null,
  9462 + null,
  9463 + false,
  9464 + Format.classicThinFilledMarkerImage.src,
  9465 + ),
  9466 + );
  9467 + Format.processMenuIcon(
  9468 + this.editorUi.menus.edgeStyleChange(
  9469 + menu,
  9470 + "",
  9471 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9472 + [mxConstants.ARROW_OPEN, 0],
  9473 + null,
  9474 + null,
  9475 + false,
  9476 + Format.openFilledMarkerImage.src,
  9477 + ),
  9478 + );
  9479 + Format.processMenuIcon(
  9480 + this.editorUi.menus.edgeStyleChange(
  9481 + menu,
  9482 + "",
  9483 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9484 + [mxConstants.ARROW_OPEN_THIN, 0],
  9485 + null,
  9486 + null,
  9487 + false,
  9488 + Format.openThinFilledMarkerImage.src,
  9489 + ),
  9490 + );
  9491 + Format.processMenuIcon(
  9492 + this.editorUi.menus.edgeStyleChange(
  9493 + menu,
  9494 + "",
  9495 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9496 + ["openAsync", 0],
  9497 + null,
  9498 + null,
  9499 + false,
  9500 + Format.openAsyncFilledMarkerImage.src,
  9501 + ),
  9502 + );
  9503 + Format.processMenuIcon(
  9504 + this.editorUi.menus.edgeStyleChange(
  9505 + menu,
  9506 + "",
  9507 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9508 + [mxConstants.ARROW_BLOCK, 1],
  9509 + null,
  9510 + null,
  9511 + false,
  9512 + Format.blockFilledMarkerImage.src,
  9513 + ),
  9514 + );
  9515 + Format.processMenuIcon(
  9516 + this.editorUi.menus.edgeStyleChange(
  9517 + menu,
  9518 + "",
  9519 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9520 + [mxConstants.ARROW_BLOCK_THIN, 1],
  9521 + null,
  9522 + null,
  9523 + false,
  9524 + Format.blockThinFilledMarkerImage.src,
  9525 + ),
  9526 + );
  9527 + Format.processMenuIcon(
  9528 + this.editorUi.menus.edgeStyleChange(
  9529 + menu,
  9530 + "",
  9531 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9532 + ["async", 1],
  9533 + null,
  9534 + null,
  9535 + false,
  9536 + Format.asyncFilledMarkerImage.src,
  9537 + ),
  9538 + );
  9539 + Format.processMenuIcon(
  9540 + this.editorUi.menus.edgeStyleChange(
  9541 + menu,
  9542 + "",
  9543 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9544 + [mxConstants.ARROW_OVAL, 1],
  9545 + null,
  9546 + null,
  9547 + false,
  9548 + Format.ovalFilledMarkerImage.src,
  9549 + ),
  9550 + );
  9551 + Format.processMenuIcon(
  9552 + this.editorUi.menus.edgeStyleChange(
  9553 + menu,
  9554 + "",
  9555 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9556 + [mxConstants.ARROW_DIAMOND, 1],
  9557 + null,
  9558 + null,
  9559 + false,
  9560 + Format.diamondFilledMarkerImage.src,
  9561 + ),
  9562 + );
  9563 + Format.processMenuIcon(
  9564 + this.editorUi.menus.edgeStyleChange(
  9565 + menu,
  9566 + "",
  9567 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9568 + [mxConstants.ARROW_DIAMOND_THIN, 1],
  9569 + null,
  9570 + null,
  9571 + false,
  9572 + Format.diamondThinFilledMarkerImage.src,
  9573 + ),
  9574 + );
  9575 + Format.processMenuIcon(
  9576 + this.editorUi.menus.edgeStyleChange(
  9577 + menu,
  9578 + "",
  9579 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9580 + [mxConstants.ARROW_CLASSIC, 0],
  9581 + null,
  9582 + null,
  9583 + false,
  9584 + Format.classicMarkerImage.src,
  9585 + ),
  9586 + );
  9587 + Format.processMenuIcon(
  9588 + this.editorUi.menus.edgeStyleChange(
  9589 + menu,
  9590 + "",
  9591 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9592 + [mxConstants.ARROW_CLASSIC_THIN, 0],
  9593 + null,
  9594 + null,
  9595 + false,
  9596 + Format.classicThinMarkerImage.src,
  9597 + ),
  9598 + );
  9599 + Format.processMenuIcon(
  9600 + this.editorUi.menus.edgeStyleChange(
  9601 + menu,
  9602 + "",
  9603 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9604 + [mxConstants.ARROW_BLOCK, 0],
  9605 + null,
  9606 + null,
  9607 + false,
  9608 + Format.blockMarkerImage.src,
  9609 + ),
  9610 + );
  9611 + Format.processMenuIcon(
  9612 + this.editorUi.menus.edgeStyleChange(
  9613 + menu,
  9614 + "",
  9615 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9616 + [mxConstants.ARROW_BLOCK_THIN, 0],
  9617 + null,
  9618 + null,
  9619 + false,
  9620 + Format.blockThinMarkerImage.src,
  9621 + ),
  9622 + );
  9623 + Format.processMenuIcon(
  9624 + this.editorUi.menus.edgeStyleChange(
  9625 + menu,
  9626 + "",
  9627 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9628 + ["async", 0],
  9629 + null,
  9630 + null,
  9631 + false,
  9632 + Format.asyncMarkerImage.src,
  9633 + ),
  9634 + );
  9635 + Format.processMenuIcon(
  9636 + this.editorUi.menus.edgeStyleChange(
  9637 + menu,
  9638 + "",
  9639 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9640 + [mxConstants.ARROW_OVAL, 0],
  9641 + null,
  9642 + null,
  9643 + false,
  9644 + Format.ovalMarkerImage.src,
  9645 + ),
  9646 + );
  9647 + Format.processMenuIcon(
  9648 + this.editorUi.menus.edgeStyleChange(
  9649 + menu,
  9650 + "",
  9651 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9652 + [mxConstants.ARROW_DIAMOND, 0],
  9653 + null,
  9654 + null,
  9655 + false,
  9656 + Format.diamondMarkerImage.src,
  9657 + ),
  9658 + );
  9659 + Format.processMenuIcon(
  9660 + this.editorUi.menus.edgeStyleChange(
  9661 + menu,
  9662 + "",
  9663 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9664 + [mxConstants.ARROW_DIAMOND_THIN, 0],
  9665 + null,
  9666 + null,
  9667 + false,
  9668 + Format.diamondThinMarkerImage.src,
  9669 + ),
  9670 + );
  9671 + Format.processMenuIcon(
  9672 + this.editorUi.menus.edgeStyleChange(
  9673 + menu,
  9674 + "",
  9675 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9676 + ["box", 0],
  9677 + null,
  9678 + null,
  9679 + false,
  9680 + Format.boxMarkerImage.src,
  9681 + ),
  9682 + );
  9683 + Format.processMenuIcon(
  9684 + this.editorUi.menus.edgeStyleChange(
  9685 + menu,
  9686 + "",
  9687 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9688 + ["halfCircle", 0],
  9689 + null,
  9690 + null,
  9691 + false,
  9692 + Format.halfCircleMarkerImage.src,
  9693 + ),
  9694 + );
  9695 + Format.processMenuIcon(
  9696 + this.editorUi.menus.edgeStyleChange(
  9697 + menu,
  9698 + "",
  9699 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9700 + ["dash", 0],
  9701 + null,
  9702 + null,
  9703 + false,
  9704 + Format.dashMarkerImage.src,
  9705 + ),
  9706 + );
  9707 + Format.processMenuIcon(
  9708 + this.editorUi.menus.edgeStyleChange(
  9709 + menu,
  9710 + "",
  9711 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9712 + ["cross", 0],
  9713 + null,
  9714 + null,
  9715 + false,
  9716 + Format.crossMarkerImage.src,
  9717 + ),
  9718 + );
  9719 + Format.processMenuIcon(
  9720 + this.editorUi.menus.edgeStyleChange(
  9721 + menu,
  9722 + "",
  9723 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9724 + ["circlePlus", 0],
  9725 + null,
  9726 + null,
  9727 + false,
  9728 + Format.circlePlusMarkerImage.src,
  9729 + ),
  9730 + );
  9731 + Format.processMenuIcon(
  9732 + this.editorUi.menus.edgeStyleChange(
  9733 + menu,
  9734 + "",
  9735 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9736 + ["circle", 1],
  9737 + null,
  9738 + null,
  9739 + false,
  9740 + Format.circleMarkerImage.src,
  9741 + ),
  9742 + );
  9743 + Format.processMenuIcon(
  9744 + this.editorUi.menus.edgeStyleChange(
  9745 + menu,
  9746 + "",
  9747 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9748 + ["baseDash", 0],
  9749 + null,
  9750 + null,
  9751 + false,
  9752 + Format.baseDashMarkerImage.src,
  9753 + ),
  9754 + );
  9755 + Format.processMenuIcon(
  9756 + this.editorUi.menus.edgeStyleChange(
  9757 + menu,
  9758 + "",
  9759 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9760 + ["ERone", 0],
  9761 + null,
  9762 + null,
  9763 + false,
  9764 + Format.EROneMarkerImage.src,
  9765 + ),
  9766 + );
  9767 + Format.processMenuIcon(
  9768 + this.editorUi.menus.edgeStyleChange(
  9769 + menu,
  9770 + "",
  9771 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9772 + ["ERmandOne", 0],
  9773 + null,
  9774 + null,
  9775 + false,
  9776 + Format.ERmandOneMarkerImage.src,
  9777 + ),
  9778 + );
  9779 + Format.processMenuIcon(
  9780 + this.editorUi.menus.edgeStyleChange(
  9781 + menu,
  9782 + "",
  9783 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9784 + ["ERmany", 0],
  9785 + null,
  9786 + null,
  9787 + false,
  9788 + Format.ERmanyMarkerImage.src,
  9789 + ),
  9790 + );
  9791 + Format.processMenuIcon(
  9792 + this.editorUi.menus.edgeStyleChange(
  9793 + menu,
  9794 + "",
  9795 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9796 + ["ERoneToMany", 0],
  9797 + null,
  9798 + null,
  9799 + false,
  9800 + Format.ERoneToManyMarkerImage.src,
  9801 + ),
  9802 + );
  9803 + Format.processMenuIcon(
  9804 + this.editorUi.menus.edgeStyleChange(
  9805 + menu,
  9806 + "",
  9807 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9808 + ["ERzeroToOne", 0],
  9809 + null,
  9810 + null,
  9811 + false,
  9812 + Format.ERzeroToOneMarkerImage.src,
  9813 + ),
  9814 + );
  9815 + Format.processMenuIcon(
  9816 + this.editorUi.menus.edgeStyleChange(
  9817 + menu,
  9818 + "",
  9819 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9820 + ["ERzeroToMany", 0],
  9821 + null,
  9822 + null,
  9823 + false,
  9824 + Format.ERzeroToManyMarkerImage.src,
  9825 + ),
  9826 + );
  9827 + Format.processMenuIcon(
  9828 + this.editorUi.menus.edgeStyleChange(
  9829 + menu,
  9830 + "",
  9831 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9832 + ["doubleBlock", 0],
  9833 + null,
  9834 + null,
  9835 + false,
  9836 + Format.doubleBlockMarkerImage.src,
  9837 + ),
  9838 + );
  9839 + Format.processMenuIcon(
  9840 + this.editorUi.menus.edgeStyleChange(
  9841 + menu,
  9842 + "",
  9843 + [mxConstants.STYLE_STARTARROW, "startFill"],
  9844 + ["doubleBlock", 1],
  9845 + null,
  9846 + null,
  9847 + false,
  9848 + Format.doubleBlockFilledMarkerImage.src,
  9849 + ),
  9850 + );
  9851 + } else {
  9852 + this.editorUi.menus
  9853 + .edgeStyleChange(
  9854 + menu,
  9855 + "",
  9856 + [mxConstants.STYLE_STARTARROW],
  9857 + [mxConstants.ARROW_BLOCK],
  9858 + "geIcon geSprite geSprite-startblocktrans",
  9859 + null,
  9860 + false,
  9861 + )
  9862 + .setAttribute("title", mxResources.get("block"));
  9863 + }
  9864 +
  9865 + menu.div.style.width = "40px";
  9866 +
  9867 + window.setTimeout(
  9868 + mxUtils.bind(this, function () {
  9869 + if (menu.div != null) {
  9870 + mxUtils.fit(menu.div);
  9871 + }
  9872 + }),
  9873 + 0,
  9874 + );
  9875 + }
  9876 + }),
  9877 + );
  9878 +
  9879 + var lineEnd = this.editorUi.toolbar.addMenuFunctionInContainer(
  9880 + stylePanel2,
  9881 + "geSprite-endclassic",
  9882 + mxResources.get("lineend"),
  9883 + false,
  9884 + mxUtils.bind(this, function (menu) {
  9885 + if (
  9886 + ss.style.shape == "connector" ||
  9887 + ss.style.shape == "flexArrow" ||
  9888 + ss.style.shape == "filledEdge"
  9889 + ) {
  9890 + var item = this.editorUi.menus.edgeStyleChange(
  9891 + menu,
  9892 + "",
  9893 + [mxConstants.STYLE_ENDARROW, "endFill"],
  9894 + [mxConstants.NONE, 0],
  9895 + "geIcon",
  9896 + null,
  9897 + false,
  9898 + );
  9899 + item.setAttribute("title", mxResources.get("none"));
  9900 +
  9901 + var font = document.createElement("font");
  9902 + font.style.fontSize = "10px";
  9903 + mxUtils.write(font, mxResources.get("none"));
  9904 + item.firstChild.firstChild.appendChild(font);
  9905 +
  9906 + if (ss.style.shape == "connector" || ss.style.shape == "filledEdge") {
  9907 + Format.processMenuIcon(
  9908 + this.editorUi.menus.edgeStyleChange(
  9909 + menu,
  9910 + "",
  9911 + [mxConstants.STYLE_ENDARROW, "endFill"],
  9912 + [mxConstants.ARROW_CLASSIC, 1],
  9913 + null,
  9914 + null,
  9915 + false,
  9916 + Format.classicFilledMarkerImage.src,
  9917 + ),
  9918 + "scaleX(-1)",
  9919 + );
  9920 + Format.processMenuIcon(
  9921 + this.editorUi.menus.edgeStyleChange(
  9922 + menu,
  9923 + "",
  9924 + [mxConstants.STYLE_ENDARROW, "endFill"],
  9925 + [mxConstants.ARROW_CLASSIC_THIN, 1],
  9926 + null,
  9927 + null,
  9928 + false,
  9929 + Format.classicThinFilledMarkerImage.src,
  9930 + ),
  9931 + "scaleX(-1)",
  9932 + );
  9933 + Format.processMenuIcon(
  9934 + this.editorUi.menus.edgeStyleChange(
  9935 + menu,
  9936 + "",
  9937 + [mxConstants.STYLE_ENDARROW, "endFill"],
  9938 + [mxConstants.ARROW_OPEN, 0],
  9939 + null,
  9940 + null,
  9941 + false,
  9942 + Format.openFilledMarkerImage.src,
  9943 + ),
  9944 + "scaleX(-1)",
  9945 + );
  9946 + Format.processMenuIcon(
  9947 + this.editorUi.menus.edgeStyleChange(
  9948 + menu,
  9949 + "",
  9950 + [mxConstants.STYLE_ENDARROW, "endFill"],
  9951 + [mxConstants.ARROW_OPEN_THIN, 0],
  9952 + null,
  9953 + null,
  9954 + false,
  9955 + Format.openThinFilledMarkerImage.src,
  9956 + ),
  9957 + "scaleX(-1)",
  9958 + );
  9959 + Format.processMenuIcon(
  9960 + this.editorUi.menus.edgeStyleChange(
  9961 + menu,
  9962 + "",
  9963 + [mxConstants.STYLE_ENDARROW, "endFill"],
  9964 + ["openAsync", 0],
  9965 + null,
  9966 + null,
  9967 + false,
  9968 + Format.openAsyncFilledMarkerImage.src,
  9969 + ),
  9970 + "scaleX(-1)",
  9971 + );
  9972 + Format.processMenuIcon(
  9973 + this.editorUi.menus.edgeStyleChange(
  9974 + menu,
  9975 + "",
  9976 + [mxConstants.STYLE_ENDARROW, "endFill"],
  9977 + [mxConstants.ARROW_BLOCK, 1],
  9978 + null,
  9979 + null,
  9980 + false,
  9981 + Format.blockFilledMarkerImage.src,
  9982 + ),
  9983 + "scaleX(-1)",
  9984 + );
  9985 + Format.processMenuIcon(
  9986 + this.editorUi.menus.edgeStyleChange(
  9987 + menu,
  9988 + "",
  9989 + [mxConstants.STYLE_ENDARROW, "endFill"],
  9990 + [mxConstants.ARROW_BLOCK_THIN, 1],
  9991 + null,
  9992 + null,
  9993 + false,
  9994 + Format.blockThinFilledMarkerImage.src,
  9995 + ),
  9996 + "scaleX(-1)",
  9997 + );
  9998 + Format.processMenuIcon(
  9999 + this.editorUi.menus.edgeStyleChange(
  10000 + menu,
  10001 + "",
  10002 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10003 + ["async", 1],
  10004 + null,
  10005 + null,
  10006 + false,
  10007 + Format.asyncFilledMarkerImage.src,
  10008 + ),
  10009 + "scaleX(-1)",
  10010 + );
  10011 + Format.processMenuIcon(
  10012 + this.editorUi.menus.edgeStyleChange(
  10013 + menu,
  10014 + "",
  10015 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10016 + [mxConstants.ARROW_OVAL, 1],
  10017 + null,
  10018 + null,
  10019 + false,
  10020 + Format.ovalFilledMarkerImage.src,
  10021 + ),
  10022 + "scaleX(-1)",
  10023 + );
  10024 + Format.processMenuIcon(
  10025 + this.editorUi.menus.edgeStyleChange(
  10026 + menu,
  10027 + "",
  10028 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10029 + [mxConstants.ARROW_DIAMOND, 1],
  10030 + null,
  10031 + null,
  10032 + false,
  10033 + Format.diamondFilledMarkerImage.src,
  10034 + ),
  10035 + "scaleX(-1)",
  10036 + );
  10037 + Format.processMenuIcon(
  10038 + this.editorUi.menus.edgeStyleChange(
  10039 + menu,
  10040 + "",
  10041 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10042 + [mxConstants.ARROW_DIAMOND_THIN, 1],
  10043 + null,
  10044 + null,
  10045 + false,
  10046 + Format.diamondThinFilledMarkerImage.src,
  10047 + ),
  10048 + "scaleX(-1)",
  10049 + );
  10050 + Format.processMenuIcon(
  10051 + this.editorUi.menus.edgeStyleChange(
  10052 + menu,
  10053 + "",
  10054 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10055 + [mxConstants.ARROW_CLASSIC, 0],
  10056 + null,
  10057 + null,
  10058 + false,
  10059 + Format.classicMarkerImage.src,
  10060 + ),
  10061 + "scaleX(-1)",
  10062 + );
  10063 + Format.processMenuIcon(
  10064 + this.editorUi.menus.edgeStyleChange(
  10065 + menu,
  10066 + "",
  10067 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10068 + [mxConstants.ARROW_CLASSIC_THIN, 0],
  10069 + null,
  10070 + null,
  10071 + false,
  10072 + Format.classicThinMarkerImage.src,
  10073 + ),
  10074 + "scaleX(-1)",
  10075 + );
  10076 + Format.processMenuIcon(
  10077 + this.editorUi.menus.edgeStyleChange(
  10078 + menu,
  10079 + "",
  10080 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10081 + [mxConstants.ARROW_BLOCK, 0],
  10082 + null,
  10083 + null,
  10084 + false,
  10085 + Format.blockMarkerImage.src,
  10086 + ),
  10087 + "scaleX(-1)",
  10088 + );
  10089 + Format.processMenuIcon(
  10090 + this.editorUi.menus.edgeStyleChange(
  10091 + menu,
  10092 + "",
  10093 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10094 + [mxConstants.ARROW_BLOCK_THIN, 0],
  10095 + null,
  10096 + null,
  10097 + false,
  10098 + Format.blockThinMarkerImage.src,
  10099 + ),
  10100 + "scaleX(-1)",
  10101 + );
  10102 + Format.processMenuIcon(
  10103 + this.editorUi.menus.edgeStyleChange(
  10104 + menu,
  10105 + "",
  10106 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10107 + ["async", 0],
  10108 + null,
  10109 + null,
  10110 + false,
  10111 + Format.asyncMarkerImage.src,
  10112 + ),
  10113 + "scaleX(-1)",
  10114 + );
  10115 + Format.processMenuIcon(
  10116 + this.editorUi.menus.edgeStyleChange(
  10117 + menu,
  10118 + "",
  10119 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10120 + [mxConstants.ARROW_OVAL, 0],
  10121 + null,
  10122 + null,
  10123 + false,
  10124 + Format.ovalMarkerImage.src,
  10125 + ),
  10126 + "scaleX(-1)",
  10127 + );
  10128 + Format.processMenuIcon(
  10129 + this.editorUi.menus.edgeStyleChange(
  10130 + menu,
  10131 + "",
  10132 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10133 + [mxConstants.ARROW_DIAMOND, 0],
  10134 + null,
  10135 + null,
  10136 + false,
  10137 + Format.diamondMarkerImage.src,
  10138 + ),
  10139 + "scaleX(-1)",
  10140 + );
  10141 + Format.processMenuIcon(
  10142 + this.editorUi.menus.edgeStyleChange(
  10143 + menu,
  10144 + "",
  10145 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10146 + [mxConstants.ARROW_DIAMOND_THIN, 0],
  10147 + null,
  10148 + null,
  10149 + false,
  10150 + Format.diamondThinMarkerImage.src,
  10151 + ),
  10152 + "scaleX(-1)",
  10153 + );
  10154 + Format.processMenuIcon(
  10155 + this.editorUi.menus.edgeStyleChange(
  10156 + menu,
  10157 + "",
  10158 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10159 + ["box", 0],
  10160 + null,
  10161 + null,
  10162 + false,
  10163 + Format.boxMarkerImage.src,
  10164 + ),
  10165 + "scaleX(-1)",
  10166 + );
  10167 + Format.processMenuIcon(
  10168 + this.editorUi.menus.edgeStyleChange(
  10169 + menu,
  10170 + "",
  10171 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10172 + ["halfCircle", 0],
  10173 + null,
  10174 + null,
  10175 + false,
  10176 + Format.halfCircleMarkerImage.src,
  10177 + ),
  10178 + "scaleX(-1)",
  10179 + );
  10180 + Format.processMenuIcon(
  10181 + this.editorUi.menus.edgeStyleChange(
  10182 + menu,
  10183 + "",
  10184 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10185 + ["dash", 0],
  10186 + null,
  10187 + null,
  10188 + false,
  10189 + Format.dashMarkerImage.src,
  10190 + ),
  10191 + "scaleX(-1)",
  10192 + );
  10193 + Format.processMenuIcon(
  10194 + this.editorUi.menus.edgeStyleChange(
  10195 + menu,
  10196 + "",
  10197 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10198 + ["cross", 0],
  10199 + null,
  10200 + null,
  10201 + false,
  10202 + Format.crossMarkerImage.src,
  10203 + ),
  10204 + "scaleX(-1)",
  10205 + );
  10206 + Format.processMenuIcon(
  10207 + this.editorUi.menus.edgeStyleChange(
  10208 + menu,
  10209 + "",
  10210 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10211 + ["circlePlus", 0],
  10212 + null,
  10213 + null,
  10214 + false,
  10215 + Format.circlePlusMarkerImage.src,
  10216 + ),
  10217 + "scaleX(-1)",
  10218 + );
  10219 + Format.processMenuIcon(
  10220 + this.editorUi.menus.edgeStyleChange(
  10221 + menu,
  10222 + "",
  10223 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10224 + ["circle", 0],
  10225 + null,
  10226 + null,
  10227 + false,
  10228 + Format.circleMarkerImage.src,
  10229 + ),
  10230 + "scaleX(-1)",
  10231 + );
  10232 + Format.processMenuIcon(
  10233 + this.editorUi.menus.edgeStyleChange(
  10234 + menu,
  10235 + "",
  10236 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10237 + ["baseDash", 0],
  10238 + null,
  10239 + null,
  10240 + false,
  10241 + Format.baseDashMarkerImage.src,
  10242 + ),
  10243 + "scaleX(-1)",
  10244 + );
  10245 + Format.processMenuIcon(
  10246 + this.editorUi.menus.edgeStyleChange(
  10247 + menu,
  10248 + "",
  10249 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10250 + ["ERone", 0],
  10251 + null,
  10252 + null,
  10253 + false,
  10254 + Format.EROneMarkerImage.src,
  10255 + ),
  10256 + "scaleX(-1)",
  10257 + );
  10258 + Format.processMenuIcon(
  10259 + this.editorUi.menus.edgeStyleChange(
  10260 + menu,
  10261 + "",
  10262 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10263 + ["ERmandOne", 0],
  10264 + null,
  10265 + null,
  10266 + false,
  10267 + Format.ERmandOneMarkerImage.src,
  10268 + ),
  10269 + "scaleX(-1)",
  10270 + );
  10271 + Format.processMenuIcon(
  10272 + this.editorUi.menus.edgeStyleChange(
  10273 + menu,
  10274 + "",
  10275 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10276 + ["ERmany", 0],
  10277 + null,
  10278 + null,
  10279 + false,
  10280 + Format.ERmanyMarkerImage.src,
  10281 + ),
  10282 + "scaleX(-1)",
  10283 + );
  10284 + Format.processMenuIcon(
  10285 + this.editorUi.menus.edgeStyleChange(
  10286 + menu,
  10287 + "",
  10288 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10289 + ["ERoneToMany", 0],
  10290 + null,
  10291 + null,
  10292 + false,
  10293 + Format.ERoneToManyMarkerImage.src,
  10294 + ),
  10295 + "scaleX(-1)",
  10296 + );
  10297 + Format.processMenuIcon(
  10298 + this.editorUi.menus.edgeStyleChange(
  10299 + menu,
  10300 + "",
  10301 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10302 + ["ERzeroToOne", 0],
  10303 + null,
  10304 + null,
  10305 + false,
  10306 + Format.ERzeroToOneMarkerImage.src,
  10307 + ),
  10308 + "scaleX(-1)",
  10309 + );
  10310 + Format.processMenuIcon(
  10311 + this.editorUi.menus.edgeStyleChange(
  10312 + menu,
  10313 + "",
  10314 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10315 + ["ERzeroToMany", 0],
  10316 + null,
  10317 + null,
  10318 + false,
  10319 + Format.ERzeroToManyMarkerImage.src,
  10320 + ),
  10321 + "scaleX(-1)",
  10322 + );
  10323 + Format.processMenuIcon(
  10324 + this.editorUi.menus.edgeStyleChange(
  10325 + menu,
  10326 + "",
  10327 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10328 + ["doubleBlock", 0],
  10329 + null,
  10330 + null,
  10331 + false,
  10332 + Format.doubleBlockMarkerImage.src,
  10333 + ),
  10334 + "scaleX(-1)",
  10335 + );
  10336 + Format.processMenuIcon(
  10337 + this.editorUi.menus.edgeStyleChange(
  10338 + menu,
  10339 + "",
  10340 + [mxConstants.STYLE_ENDARROW, "endFill"],
  10341 + ["doubleBlock", 1],
  10342 + null,
  10343 + null,
  10344 + false,
  10345 + Format.doubleBlockFilledMarkerImage.src,
  10346 + ),
  10347 + "scaleX(-1)",
  10348 + );
  10349 + } else {
  10350 + this.editorUi.menus
  10351 + .edgeStyleChange(
  10352 + menu,
  10353 + "",
  10354 + [mxConstants.STYLE_ENDARROW],
  10355 + [mxConstants.ARROW_BLOCK],
  10356 + "geIcon geSprite geSprite-endblocktrans",
  10357 + null,
  10358 + false,
  10359 + )
  10360 + .setAttribute("title", mxResources.get("block"));
  10361 + }
  10362 +
  10363 + menu.div.style.width = "40px";
  10364 +
  10365 + window.setTimeout(
  10366 + mxUtils.bind(this, function () {
  10367 + if (menu.div != null) {
  10368 + mxUtils.fit(menu.div);
  10369 + }
  10370 + }),
  10371 + 0,
  10372 + );
  10373 + }
  10374 + }),
  10375 + );
  10376 +
  10377 + var elt = this.addArrow(edgeShape, 8);
  10378 + elt.nextSibling.style.position = "relative";
  10379 + elt.nextSibling.style.top = "-2px";
  10380 + elt = this.addArrow(edgeStyle, 10);
  10381 + elt.nextSibling.style.position = "relative";
  10382 + elt.nextSibling.style.top = "-3px";
  10383 + edgeStyle.getElementsByTagName("img")[0].style.top = "-1px";
  10384 + this.addArrow(lineStart);
  10385 + this.addArrow(lineEnd);
  10386 +
  10387 + var symbol = this.addArrow(pattern, 9);
  10388 + symbol.className = "geIcon";
  10389 + symbol.style.width = "auto";
  10390 +
  10391 + var altSymbol = this.addArrow(altPattern, 9);
  10392 + altSymbol.className = "geIcon";
  10393 + altSymbol.style.width = "22px";
  10394 +
  10395 + var solid = document.createElement("div");
  10396 + solid.style.width = "84px";
  10397 + solid.style.height = "1px";
  10398 + solid.style.borderBottom = "1px solid " + this.defaultStrokeColor;
  10399 + solid.style.marginBottom = "7px";
  10400 + symbol.appendChild(solid);
  10401 +
  10402 + var altSolid = document.createElement("div");
  10403 + altSolid.style.width = "23px";
  10404 + altSolid.style.height = "1px";
  10405 + altSolid.style.borderBottom = "1px solid " + this.defaultStrokeColor;
  10406 + altSolid.style.marginBottom = "7px";
  10407 + altSymbol.appendChild(altSolid);
  10408 +
  10409 + pattern.style.height = "15px";
  10410 + pattern.style.marginLeft = "16px";
  10411 + altPattern.style.height = "15px";
  10412 + altPattern.style.marginLeft = "3px";
  10413 + edgeShape.style.marginLeft = "10px";
  10414 + edgeShape.style.height = "15px";
  10415 + edgeStyle.style.marginLeft = "10px";
  10416 + edgeStyle.style.height = "17px";
  10417 + lineStart.style.marginLeft = "3px";
  10418 + lineStart.style.height = "17px";
  10419 + lineEnd.style.marginLeft = "3px";
  10420 + lineEnd.style.height = "17px";
  10421 +
  10422 + container.appendChild(colorPanel);
  10423 + container.appendChild(altStylePanel);
  10424 + container.appendChild(stylePanel);
  10425 +
  10426 + var arrowPanel = stylePanel.cloneNode(false);
  10427 + arrowPanel.style.padding = "5px 4px 6px 0px";
  10428 + arrowPanel.style.fontWeight = "normal";
  10429 +
  10430 + var span = document.createElement("div");
  10431 + span.style.position = "absolute";
  10432 + span.style.marginLeft = "0px";
  10433 + span.style.marginBottom = "12px";
  10434 + span.style.marginTop = "2px";
  10435 + span.style.fontWeight = "normal";
  10436 + span.style.width = "76px";
  10437 +
  10438 + mxUtils.write(span, mxResources.get("lineend"));
  10439 + arrowPanel.appendChild(span);
  10440 +
  10441 + var endSpacingUpdate, endSizeUpdate;
  10442 + var endSpacing = this.addUnitInput(arrowPanel, "pt", 98, 52, function () {
  10443 + endSpacingUpdate.apply(this, arguments);
  10444 + });
  10445 + var endSize = this.addUnitInput(arrowPanel, "pt", 30, 52, function () {
  10446 + endSizeUpdate.apply(this, arguments);
  10447 + });
  10448 +
  10449 + mxUtils.br(arrowPanel);
  10450 +
  10451 + var spacer = document.createElement("div");
  10452 + spacer.style.height = "8px";
  10453 + arrowPanel.appendChild(spacer);
  10454 +
  10455 + span = span.cloneNode(false);
  10456 + mxUtils.write(span, mxResources.get("linestart"));
  10457 + arrowPanel.appendChild(span);
  10458 +
  10459 + var startSpacingUpdate, startSizeUpdate;
  10460 + var startSpacing = this.addUnitInput(arrowPanel, "pt", 98, 52, function () {
  10461 + startSpacingUpdate.apply(this, arguments);
  10462 + });
  10463 + var startSize = this.addUnitInput(arrowPanel, "pt", 30, 52, function () {
  10464 + startSizeUpdate.apply(this, arguments);
  10465 + });
  10466 +
  10467 + mxUtils.br(arrowPanel);
  10468 + this.addLabel(arrowPanel, mxResources.get("spacing"), 98, 52);
  10469 + this.addLabel(arrowPanel, mxResources.get("size"), 30, 52);
  10470 + mxUtils.br(arrowPanel);
  10471 +
  10472 + var perimeterPanel = colorPanel.cloneNode(false);
  10473 + perimeterPanel.style.fontWeight = "normal";
  10474 + perimeterPanel.style.position = "relative";
  10475 + perimeterPanel.style.paddingLeft = "16px";
  10476 + perimeterPanel.style.marginBottom = "2px";
  10477 + perimeterPanel.style.marginTop = "6px";
  10478 + perimeterPanel.style.borderWidth = "0px";
  10479 + perimeterPanel.style.paddingBottom = "18px";
  10480 +
  10481 + var span = document.createElement("div");
  10482 + span.style.position = "absolute";
  10483 + span.style.marginLeft = "3px";
  10484 + span.style.marginBottom = "12px";
  10485 + span.style.marginTop = "1px";
  10486 + span.style.fontWeight = "normal";
  10487 + span.style.width = "120px";
  10488 + mxUtils.write(span, mxResources.get("perimeter"));
  10489 + perimeterPanel.appendChild(span);
  10490 +
  10491 + var perimeterUpdate;
  10492 + var perimeterSpacing = this.addUnitInput(
  10493 + perimeterPanel,
  10494 + "pt",
  10495 + 30,
  10496 + 52,
  10497 + function () {
  10498 + perimeterUpdate.apply(this, arguments);
  10499 + },
  10500 + );
  10501 +
  10502 + if (ss.edges.length == ss.cells.length) {
  10503 + container.appendChild(stylePanel2);
  10504 + container.appendChild(arrowPanel);
  10505 + } else if (ss.vertices.length == ss.cells.length) {
  10506 + container.appendChild(perimeterPanel);
  10507 + }
  10508 +
  10509 + var listener = mxUtils.bind(this, function (sender, evt, force) {
  10510 + ss = ui.getSelectionState();
  10511 +
  10512 + if (force || document.activeElement != input) {
  10513 + var tmp = parseFloat(
  10514 + mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1),
  10515 + );
  10516 + input.value = isNaN(tmp) ? "" : tmp + " pt";
  10517 + }
  10518 +
  10519 + if (force || document.activeElement != altInput) {
  10520 + var tmp = parseFloat(
  10521 + mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1),
  10522 + );
  10523 + altInput.value = isNaN(tmp) ? "" : tmp + " pt";
  10524 + }
  10525 +
  10526 + styleSelect.style.visibility =
  10527 + ss.style.shape == "connector" || ss.style.shape == "filledEdge"
  10528 + ? ""
  10529 + : "hidden";
  10530 +
  10531 + if (mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == "1") {
  10532 + styleSelect.value = "curved";
  10533 + } else if (
  10534 + mxUtils.getValue(ss.style, mxConstants.STYLE_ROUNDED, null) == "1"
  10535 + ) {
  10536 + styleSelect.value = "rounded";
  10537 + }
  10538 +
  10539 + if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASHED, null) == "1") {
  10540 + if (
  10541 + mxUtils.getValue(ss.style, mxConstants.STYLE_DASH_PATTERN, null) == null
  10542 + ) {
  10543 + solid.style.borderBottom = "1px dashed " + this.defaultStrokeColor;
  10544 + } else {
  10545 + solid.style.borderBottom = "1px dotted " + this.defaultStrokeColor;
  10546 + }
  10547 + } else {
  10548 + solid.style.borderBottom = "1px solid " + this.defaultStrokeColor;
  10549 + }
  10550 +
  10551 + altSolid.style.borderBottom = solid.style.borderBottom;
  10552 +
  10553 + // Updates toolbar icon for edge style
  10554 + var edgeStyleDiv = edgeStyle.getElementsByTagName("div")[0];
  10555 +
  10556 + if (edgeStyleDiv != null) {
  10557 + var es = mxUtils.getValue(ss.style, mxConstants.STYLE_EDGE, null);
  10558 +
  10559 + if (
  10560 + mxUtils.getValue(ss.style, mxConstants.STYLE_NOEDGESTYLE, null) == "1"
  10561 + ) {
  10562 + es = null;
  10563 + }
  10564 +
  10565 + if (
  10566 + es == "orthogonalEdgeStyle" &&
  10567 + mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == "1"
  10568 + ) {
  10569 + edgeStyleDiv.className = "geSprite geSprite-curved";
  10570 + } else if (es == "straight" || es == "none" || es == null) {
  10571 + edgeStyleDiv.className = "geSprite geSprite-straight";
  10572 + } else if (es == "entityRelationEdgeStyle") {
  10573 + edgeStyleDiv.className = "geSprite geSprite-entity";
  10574 + } else if (es == "elbowEdgeStyle") {
  10575 + edgeStyleDiv.className =
  10576 + "geSprite " +
  10577 + (mxUtils.getValue(ss.style, mxConstants.STYLE_ELBOW, null) ==
  10578 + "vertical"
  10579 + ? "geSprite-verticalelbow"
  10580 + : "geSprite-horizontalelbow");
  10581 + } else if (es == "isometricEdgeStyle") {
  10582 + edgeStyleDiv.className =
  10583 + "geSprite " +
  10584 + (mxUtils.getValue(ss.style, mxConstants.STYLE_ELBOW, null) ==
  10585 + "vertical"
  10586 + ? "geSprite-verticalisometric"
  10587 + : "geSprite-horizontalisometric");
  10588 + } else {
  10589 + edgeStyleDiv.className = "geSprite geSprite-orthogonal";
  10590 + }
  10591 + }
  10592 +
  10593 + // Updates icon for edge shape
  10594 + var edgeShapeDiv = edgeShape.getElementsByTagName("div")[0];
  10595 +
  10596 + if (edgeShapeDiv != null) {
  10597 + if (ss.style.shape == "link") {
  10598 + edgeShapeDiv.className = "geSprite geSprite-linkedge";
  10599 + } else if (ss.style.shape == "flexArrow") {
  10600 + edgeShapeDiv.className = "geSprite geSprite-arrow";
  10601 + } else if (ss.style.shape == "arrow") {
  10602 + edgeShapeDiv.className = "geSprite geSprite-simplearrow";
  10603 + } else {
  10604 + edgeShapeDiv.className = "geSprite geSprite-connection";
  10605 + }
  10606 + }
  10607 +
  10608 + if (ss.edges.length == ss.cells.length) {
  10609 + altStylePanel.style.display = "";
  10610 + stylePanel.style.display = "none";
  10611 + } else {
  10612 + altStylePanel.style.display = "none";
  10613 + stylePanel.style.display = "";
  10614 + }
  10615 +
  10616 + if (
  10617 + Graph.lineJumpsEnabled &&
  10618 + ss.edges.length > 0 &&
  10619 + ss.vertices.length == 0 &&
  10620 + ss.lineJumps
  10621 + ) {
  10622 + container.style.borderBottomStyle = "none";
  10623 + }
  10624 +
  10625 + function updateArrow(marker, fill, elt, prefix) {
  10626 + var markerDiv = elt.getElementsByTagName("div")[0];
  10627 +
  10628 + if (markerDiv != null) {
  10629 + markerDiv.className = ui.getCssClassForMarker(
  10630 + prefix,
  10631 + ss.style.shape,
  10632 + marker,
  10633 + fill,
  10634 + );
  10635 + markerDiv.nextSibling.style.marginLeft = "1px";
  10636 + markerDiv.nextSibling.style.paddingRight = "5px";
  10637 +
  10638 + if (markerDiv.className == "geSprite geSprite-noarrow") {
  10639 + markerDiv.innerHTML = mxUtils.htmlEntities(mxResources.get("none"));
  10640 + markerDiv.style.backgroundImage = "none";
  10641 + markerDiv.style.verticalAlign = "top";
  10642 + markerDiv.style.marginTop = "4px";
  10643 + markerDiv.style.fontSize = "10px";
  10644 + markerDiv.style.filter = "none";
  10645 + markerDiv.style.color = this.defaultStrokeColor;
  10646 + markerDiv.nextSibling.style.marginTop = "0px";
  10647 + } else {
  10648 + markerDiv.nextSibling.style.position = "relative";
  10649 + markerDiv.nextSibling.style.top = "-2px";
  10650 + }
  10651 + }
  10652 +
  10653 + return markerDiv;
  10654 + }
  10655 +
  10656 + var sourceDiv = updateArrow(
  10657 + mxUtils.getValue(ss.style, mxConstants.STYLE_STARTARROW, null),
  10658 + mxUtils.getValue(ss.style, "startFill", "1"),
  10659 + lineStart,
  10660 + "start",
  10661 + );
  10662 + var targetDiv = updateArrow(
  10663 + mxUtils.getValue(ss.style, mxConstants.STYLE_ENDARROW, null),
  10664 + mxUtils.getValue(ss.style, "endFill", "1"),
  10665 + lineEnd,
  10666 + "end",
  10667 + );
  10668 +
  10669 + // Special cases for markers
  10670 + if (sourceDiv != null && targetDiv != null) {
  10671 + if (ss.style.shape == "arrow") {
  10672 + sourceDiv.className = "geSprite geSprite-noarrow";
  10673 + targetDiv.className = "geSprite geSprite-endblocktrans";
  10674 + } else if (ss.style.shape == "link") {
  10675 + sourceDiv.className = "geSprite geSprite-noarrow";
  10676 + targetDiv.className = "geSprite geSprite-noarrow";
  10677 + }
  10678 + }
  10679 +
  10680 + mxUtils.setOpacity(edgeStyle, ss.style.shape == "arrow" ? 30 : 100);
  10681 +
  10682 + if (
  10683 + ss.style.shape != "connector" &&
  10684 + ss.style.shape != "flexArrow" &&
  10685 + ss.style.shape != "filledEdge"
  10686 + ) {
  10687 + mxUtils.setOpacity(lineStart, 30);
  10688 + mxUtils.setOpacity(lineEnd, 30);
  10689 + } else {
  10690 + mxUtils.setOpacity(lineStart, 100);
  10691 + mxUtils.setOpacity(lineEnd, 100);
  10692 + }
  10693 +
  10694 + if (force || document.activeElement != startSize) {
  10695 + var tmp = parseInt(
  10696 + mxUtils.getValue(
  10697 + ss.style,
  10698 + mxConstants.STYLE_STARTSIZE,
  10699 + mxConstants.DEFAULT_MARKERSIZE,
  10700 + ),
  10701 + );
  10702 + startSize.value = isNaN(tmp) ? "" : tmp + " pt";
  10703 + }
  10704 +
  10705 + if (force || document.activeElement != startSpacing) {
  10706 + var tmp = parseInt(
  10707 + mxUtils.getValue(
  10708 + ss.style,
  10709 + mxConstants.STYLE_SOURCE_PERIMETER_SPACING,
  10710 + 0,
  10711 + ),
  10712 + );
  10713 + startSpacing.value = isNaN(tmp) ? "" : tmp + " pt";
  10714 + }
  10715 +
  10716 + if (force || document.activeElement != endSize) {
  10717 + var tmp = parseInt(
  10718 + mxUtils.getValue(
  10719 + ss.style,
  10720 + mxConstants.STYLE_ENDSIZE,
  10721 + mxConstants.DEFAULT_MARKERSIZE,
  10722 + ),
  10723 + );
  10724 + endSize.value = isNaN(tmp) ? "" : tmp + " pt";
  10725 + }
  10726 +
  10727 + if (force || document.activeElement != startSpacing) {
  10728 + var tmp = parseInt(
  10729 + mxUtils.getValue(
  10730 + ss.style,
  10731 + mxConstants.STYLE_TARGET_PERIMETER_SPACING,
  10732 + 0,
  10733 + ),
  10734 + );
  10735 + endSpacing.value = isNaN(tmp) ? "" : tmp + " pt";
  10736 + }
  10737 +
  10738 + if (force || document.activeElement != perimeterSpacing) {
  10739 + var tmp = parseInt(
  10740 + mxUtils.getValue(ss.style, mxConstants.STYLE_PERIMETER_SPACING, 0),
  10741 + );
  10742 + perimeterSpacing.value = isNaN(tmp) ? "" : tmp + " pt";
  10743 + }
  10744 + });
  10745 +
  10746 + startSizeUpdate = this.installInputHandler(
  10747 + startSize,
  10748 + mxConstants.STYLE_STARTSIZE,
  10749 + mxConstants.DEFAULT_MARKERSIZE,
  10750 + 0,
  10751 + 999,
  10752 + " pt",
  10753 + );
  10754 + startSpacingUpdate = this.installInputHandler(
  10755 + startSpacing,
  10756 + mxConstants.STYLE_SOURCE_PERIMETER_SPACING,
  10757 + 0,
  10758 + -999,
  10759 + 999,
  10760 + " pt",
  10761 + );
  10762 + endSizeUpdate = this.installInputHandler(
  10763 + endSize,
  10764 + mxConstants.STYLE_ENDSIZE,
  10765 + mxConstants.DEFAULT_MARKERSIZE,
  10766 + 0,
  10767 + 999,
  10768 + " pt",
  10769 + );
  10770 + endSpacingUpdate = this.installInputHandler(
  10771 + endSpacing,
  10772 + mxConstants.STYLE_TARGET_PERIMETER_SPACING,
  10773 + 0,
  10774 + -999,
  10775 + 999,
  10776 + " pt",
  10777 + );
  10778 + perimeterUpdate = this.installInputHandler(
  10779 + perimeterSpacing,
  10780 + mxConstants.STYLE_PERIMETER_SPACING,
  10781 + 0,
  10782 + 0,
  10783 + 999,
  10784 + " pt",
  10785 + );
  10786 +
  10787 + this.addKeyHandler(input, listener);
  10788 + this.addKeyHandler(startSize, listener);
  10789 + this.addKeyHandler(startSpacing, listener);
  10790 + this.addKeyHandler(endSize, listener);
  10791 + this.addKeyHandler(endSpacing, listener);
  10792 + this.addKeyHandler(perimeterSpacing, listener);
  10793 +
  10794 + graph.getModel().addListener(mxEvent.CHANGE, listener);
  10795 + this.listeners.push({
  10796 + destroy: function () {
  10797 + graph.getModel().removeListener(listener);
  10798 + },
  10799 + });
  10800 + listener();
  10801 +
  10802 + return container;
  10803 +};
  10804 +
  10805 +/**
  10806 + * Adds UI for configuring line jumps.
  10807 + */
  10808 +StyleFormatPanel.prototype.addLineJumps = function (container) {
  10809 + var ui = this.editorUi;
  10810 + var editor = ui.editor;
  10811 + var graph = editor.graph;
  10812 + var ss = ui.getSelectionState();
  10813 +
  10814 + if (
  10815 + Graph.lineJumpsEnabled &&
  10816 + ss.edges.length > 0 &&
  10817 + ss.vertices.length == 0 &&
  10818 + ss.lineJumps
  10819 + ) {
  10820 + container.style.padding = "2px 0px 24px 14px";
  10821 +
  10822 + var span = document.createElement("div");
  10823 + span.style.position = "absolute";
  10824 + span.style.maxWidth = "82px";
  10825 + span.style.overflow = "hidden";
  10826 + span.style.textOverflow = "ellipsis";
  10827 +
  10828 + mxUtils.write(span, mxResources.get("lineJumps"));
  10829 + container.appendChild(span);
  10830 +
  10831 + var styleSelect = document.createElement("select");
  10832 + styleSelect.style.position = "absolute";
  10833 + styleSelect.style.height = "21px";
  10834 + styleSelect.style.padding = "0px";
  10835 + styleSelect.style.marginTop = "-2px";
  10836 + styleSelect.style.boxSizing = "border-box";
  10837 + styleSelect.style.right = "76px";
  10838 + styleSelect.style.width = "54px";
  10839 + styleSelect.style.border = "1px solid rgb(160, 160, 160)";
  10840 + styleSelect.style.borderRadius = "4px";
  10841 +
  10842 + var styles = ["none", "arc", "gap", "sharp", "line"];
  10843 +
  10844 + for (var i = 0; i < styles.length; i++) {
  10845 + var styleOption = document.createElement("option");
  10846 + styleOption.setAttribute("value", styles[i]);
  10847 + mxUtils.write(styleOption, mxResources.get(styles[i]));
  10848 + styleSelect.appendChild(styleOption);
  10849 + }
  10850 +
  10851 + mxEvent.addListener(styleSelect, "change", function (evt) {
  10852 + graph.getModel().beginUpdate();
  10853 + try {
  10854 + graph.setCellStyles("jumpStyle", styleSelect.value, ss.cells);
  10855 + ui.fireEvent(
  10856 + new mxEventObject(
  10857 + "styleChanged",
  10858 + "keys",
  10859 + ["jumpStyle"],
  10860 + "values",
  10861 + [styleSelect.value],
  10862 + "cells",
  10863 + ss.cells,
  10864 + ),
  10865 + );
  10866 + } finally {
  10867 + graph.getModel().endUpdate();
  10868 + }
  10869 +
  10870 + mxEvent.consume(evt);
  10871 + });
  10872 +
  10873 + // Stops events from bubbling to color option event handler
  10874 + mxEvent.addListener(styleSelect, "click", function (evt) {
  10875 + mxEvent.consume(evt);
  10876 + });
  10877 +
  10878 + container.appendChild(styleSelect);
  10879 +
  10880 + var jumpSizeUpdate;
  10881 +
  10882 + var jumpSize = this.addUnitInput(container, "pt", 16, 42, function () {
  10883 + jumpSizeUpdate.apply(this, arguments);
  10884 + });
  10885 +
  10886 + jumpSizeUpdate = this.installInputHandler(
  10887 + jumpSize,
  10888 + "jumpSize",
  10889 + Graph.defaultJumpSize,
  10890 + 0,
  10891 + 999,
  10892 + " pt",
  10893 + );
  10894 +
  10895 + var listener = mxUtils.bind(this, function (sender, evt, force) {
  10896 + ss = ui.getSelectionState();
  10897 + styleSelect.value = mxUtils.getValue(ss.style, "jumpStyle", "none");
  10898 +
  10899 + if (force || document.activeElement != jumpSize) {
  10900 + var tmp = parseInt(
  10901 + mxUtils.getValue(ss.style, "jumpSize", Graph.defaultJumpSize),
  10902 + );
  10903 + jumpSize.value = isNaN(tmp) ? "" : tmp + " pt";
  10904 + }
  10905 + });
  10906 +
  10907 + this.addKeyHandler(jumpSize, listener);
  10908 +
  10909 + graph.getModel().addListener(mxEvent.CHANGE, listener);
  10910 + this.listeners.push({
  10911 + destroy: function () {
  10912 + graph.getModel().removeListener(listener);
  10913 + },
  10914 + });
  10915 + listener();
  10916 + } else {
  10917 + container.style.display = "none";
  10918 + }
  10919 +
  10920 + return container;
  10921 +};
  10922 +
  10923 +/**
  10924 + * Adds the label menu items to the given menu and parent.
  10925 + */
  10926 +StyleFormatPanel.prototype.addEffects = function (div) {
  10927 + var ui = this.editorUi;
  10928 + var editor = ui.editor;
  10929 + var graph = editor.graph;
  10930 + var ss = ui.getSelectionState();
  10931 +
  10932 + div.style.paddingTop = "4px";
  10933 + div.style.paddingBottom = "0px";
  10934 +
  10935 + var table = document.createElement("table");
  10936 +
  10937 + table.style.width = "210px";
  10938 + table.style.fontWeight = "bold";
  10939 + table.style.tableLayout = "fixed";
  10940 + var tbody = document.createElement("tbody");
  10941 + var row = document.createElement("tr");
  10942 + row.style.padding = "0px";
  10943 + var left = document.createElement("td");
  10944 + left.style.padding = "0px";
  10945 + left.style.width = "50%";
  10946 + left.setAttribute("valign", "top");
  10947 +
  10948 + var right = left.cloneNode(true);
  10949 + right.style.paddingLeft = "8px";
  10950 + row.appendChild(left);
  10951 + row.appendChild(right);
  10952 + tbody.appendChild(row);
  10953 + table.appendChild(tbody);
  10954 + div.appendChild(table);
  10955 +
  10956 + var current = left;
  10957 + var count = 0;
  10958 +
  10959 + var addOption = mxUtils.bind(this, function (label, key, defaultValue) {
  10960 + var opt = this.createCellOption(label, key, defaultValue);
  10961 + opt.style.width = "100%";
  10962 + current.appendChild(opt);
  10963 + current = current == left ? right : left;
  10964 + count++;
  10965 + });
  10966 +
  10967 + var listener = mxUtils.bind(this, function (sender, evt, force) {
  10968 + ss = ui.getSelectionState();
  10969 +
  10970 + left.innerHTML = "";
  10971 + right.innerHTML = "";
  10972 + current = left;
  10973 +
  10974 + if (ss.rounded) {
  10975 + addOption(mxResources.get("rounded"), mxConstants.STYLE_ROUNDED, 0);
  10976 + }
  10977 +
  10978 + if (ss.swimlane) {
  10979 + addOption(mxResources.get("divider"), "swimlaneLine", 1);
  10980 + }
  10981 +
  10982 + if (!ss.containsImage) {
  10983 + addOption(mxResources.get("shadow"), mxConstants.STYLE_SHADOW, 0);
  10984 + }
  10985 +
  10986 + if (ss.glass) {
  10987 + addOption(mxResources.get("glass"), mxConstants.STYLE_GLASS, 0);
  10988 + }
  10989 +
  10990 + addOption(mxResources.get("sketch"), "sketch", 0);
  10991 + });
  10992 +
  10993 + graph.getModel().addListener(mxEvent.CHANGE, listener);
  10994 + this.listeners.push({
  10995 + destroy: function () {
  10996 + graph.getModel().removeListener(listener);
  10997 + },
  10998 + });
  10999 + listener();
  11000 +
  11001 + return div;
  11002 +};
  11003 +
  11004 +/**
  11005 + * Adds the label menu items to the given menu and parent.
  11006 + */
  11007 +StyleFormatPanel.prototype.addStyleOps = function (div) {
  11008 + div.style.paddingTop = "10px";
  11009 + div.style.paddingBottom = "10px";
  11010 +
  11011 + this.addActions(div, ["setAsDefaultStyle"]);
  11012 +
  11013 + return div;
  11014 +};
  11015 +
  11016 +/**
  11017 + * Adds the label menu items to the given menu and parent.
  11018 + */
  11019 +DiagramStylePanel = function (format, editorUi, container) {
  11020 + BaseFormatPanel.call(this, format, editorUi, container);
  11021 + this.init();
  11022 +};
  11023 +
  11024 +mxUtils.extend(DiagramStylePanel, BaseFormatPanel);
  11025 +
  11026 +/**
  11027 + * Adds the label menu items to the given menu and parent.
  11028 + */
  11029 +DiagramStylePanel.prototype.init = function () {
  11030 + var ui = this.editorUi;
  11031 + var editor = ui.editor;
  11032 + var graph = editor.graph;
  11033 +
  11034 + this.darkModeChangedListener = mxUtils.bind(this, function () {
  11035 + this.format.cachedStyleEntries = [];
  11036 + });
  11037 +
  11038 + ui.addListener("darkModeChanged", this.darkModeChangedListener);
  11039 + this.container.appendChild(this.addView(this.createPanel()));
  11040 +};
  11041 +
  11042 +/**
  11043 + * Adds the label menu items to the given menu and parent.
  11044 + */
  11045 +DiagramStylePanel.prototype.addView = function (div) {
  11046 + var ui = this.editorUi;
  11047 + var editor = ui.editor;
  11048 + var graph = editor.graph;
  11049 + var model = graph.getModel();
  11050 + var gridColor = graph.view.gridColor;
  11051 +
  11052 + div.style.whiteSpace = "normal";
  11053 +
  11054 + var sketch =
  11055 + graph.currentVertexStyle["sketch"] == "1" &&
  11056 + graph.currentEdgeStyle["sketch"] == "1";
  11057 + var rounded = graph.currentVertexStyle["rounded"] == "1";
  11058 + var curved = graph.currentEdgeStyle["curved"] == "1";
  11059 +
  11060 + var opts = document.createElement("div");
  11061 + opts.style.marginRight = "16px";
  11062 + div.style.paddingTop = "8px";
  11063 +
  11064 + var table = document.createElement("table");
  11065 +
  11066 + table.style.width = "210px";
  11067 + table.style.fontWeight = "bold";
  11068 +
  11069 + var tbody = document.createElement("tbody");
  11070 + var row = document.createElement("tr");
  11071 + row.style.padding = "0px";
  11072 +
  11073 + var left = document.createElement("td");
  11074 + left.style.padding = "0px";
  11075 + left.style.width = "50%";
  11076 + left.setAttribute("valign", "middle");
  11077 +
  11078 + var right = left.cloneNode(true);
  11079 + right.style.paddingLeft = "8px";
  11080 +
  11081 + // Sketch
  11082 + if (urlParams["sketch"] != "1") {
  11083 + opts.style.paddingBottom = "12px";
  11084 + row.appendChild(left);
  11085 +
  11086 + left.appendChild(
  11087 + this.createOption(
  11088 + mxResources.get("sketch"),
  11089 + function () {
  11090 + return sketch;
  11091 + },
  11092 + function (checked) {
  11093 + sketch = checked;
  11094 +
  11095 + if (checked) {
  11096 + graph.currentEdgeStyle["sketch"] = "1";
  11097 + graph.currentVertexStyle["sketch"] = "1";
  11098 + } else {
  11099 + delete graph.currentEdgeStyle["sketch"];
  11100 + delete graph.currentVertexStyle["sketch"];
  11101 + }
  11102 +
  11103 + graph.updateCellStyles(
  11104 + { sketch: checked ? "1" : null },
  11105 + graph.getVerticesAndEdges(),
  11106 + );
  11107 + },
  11108 + null,
  11109 + function (div) {
  11110 + div.style.width = "auto";
  11111 + },
  11112 + ),
  11113 + );
  11114 + }
  11115 +
  11116 + row.appendChild(right);
  11117 + tbody.appendChild(row);
  11118 + table.appendChild(tbody);
  11119 +
  11120 + // Rounded
  11121 + right.appendChild(
  11122 + this.createOption(
  11123 + mxResources.get("rounded"),
  11124 + function () {
  11125 + return rounded;
  11126 + },
  11127 + function (checked) {
  11128 + rounded = checked;
  11129 +
  11130 + if (checked) {
  11131 + graph.currentEdgeStyle["rounded"] = "1";
  11132 + graph.currentVertexStyle["rounded"] = "1";
  11133 + } else {
  11134 + delete graph.currentEdgeStyle["rounded"];
  11135 + delete graph.currentVertexStyle["rounded"];
  11136 + }
  11137 +
  11138 + graph.updateCellStyles(
  11139 + { rounded: checked ? "1" : "0" },
  11140 + graph.getVerticesAndEdges(),
  11141 + );
  11142 + },
  11143 + null,
  11144 + function (div) {
  11145 + div.style.width = "auto";
  11146 + },
  11147 + ),
  11148 + );
  11149 +
  11150 + // Curved
  11151 + if (urlParams["sketch"] != "1") {
  11152 + left = left.cloneNode(false);
  11153 + right = right.cloneNode(false);
  11154 + row = row.cloneNode(false);
  11155 + row.appendChild(left);
  11156 + row.appendChild(right);
  11157 + tbody.appendChild(row);
  11158 +
  11159 + left.appendChild(
  11160 + this.createOption(
  11161 + mxResources.get("curved"),
  11162 + function () {
  11163 + return curved;
  11164 + },
  11165 + function (checked) {
  11166 + curved = checked;
  11167 +
  11168 + if (checked) {
  11169 + graph.currentEdgeStyle["curved"] = "1";
  11170 + } else {
  11171 + delete graph.currentEdgeStyle["curved"];
  11172 + }
  11173 +
  11174 + graph.updateCellStyles(
  11175 + { curved: checked ? "1" : null },
  11176 + graph.getVerticesAndEdges(false, true),
  11177 + );
  11178 + },
  11179 + null,
  11180 + function (div) {
  11181 + div.style.width = "auto";
  11182 + },
  11183 + ),
  11184 + );
  11185 + }
  11186 +
  11187 + opts.appendChild(table);
  11188 + div.appendChild(opts);
  11189 +
  11190 + var defaultStyles = [
  11191 + "fillColor",
  11192 + "strokeColor",
  11193 + "fontColor",
  11194 + "gradientColor",
  11195 + ];
  11196 +
  11197 + var updateCells = mxUtils.bind(this, function (styles, graphStyle) {
  11198 + var cells = graph.getVerticesAndEdges();
  11199 +
  11200 + model.beginUpdate();
  11201 + try {
  11202 + for (var i = 0; i < cells.length; i++) {
  11203 + var style = graph.getCellStyle(cells[i]);
  11204 +
  11205 + // Handles special label background color
  11206 + if (style["labelBackgroundColor"] != null) {
  11207 + graph.updateCellStyles(
  11208 + {
  11209 + labelBackgroundColor:
  11210 + graphStyle != null ? graphStyle.background : null,
  11211 + },
  11212 + [cells[i]],
  11213 + );
  11214 + }
  11215 +
  11216 + var edge = model.isEdge(cells[i]);
  11217 + var newStyle = model.getStyle(cells[i]);
  11218 + var current = edge ? graph.currentEdgeStyle : graph.currentVertexStyle;
  11219 +
  11220 + for (var j = 0; j < styles.length; j++) {
  11221 + if (
  11222 + (style[styles[j]] != null &&
  11223 + style[styles[j]] != mxConstants.NONE) ||
  11224 + (styles[j] != mxConstants.STYLE_FILLCOLOR &&
  11225 + styles[j] != mxConstants.STYLE_STROKECOLOR)
  11226 + ) {
  11227 + newStyle = mxUtils.setStyle(
  11228 + newStyle,
  11229 + styles[j],
  11230 + current[styles[j]],
  11231 + );
  11232 + }
  11233 + }
  11234 +
  11235 + model.setStyle(cells[i], newStyle);
  11236 + }
  11237 + } finally {
  11238 + model.endUpdate();
  11239 + }
  11240 + });
  11241 +
  11242 + var removeStyles = mxUtils.bind(this, function (style, styles, defaultStyle) {
  11243 + if (style != null) {
  11244 + for (var j = 0; j < styles.length; j++) {
  11245 + if (
  11246 + (style[styles[j]] != null && style[styles[j]] != mxConstants.NONE) ||
  11247 + (styles[j] != mxConstants.STYLE_FILLCOLOR &&
  11248 + styles[j] != mxConstants.STYLE_STROKECOLOR)
  11249 + ) {
  11250 + style[styles[j]] = defaultStyle[styles[j]];
  11251 + }
  11252 + }
  11253 + }
  11254 + });
  11255 +
  11256 + var applyStyle = mxUtils.bind(
  11257 + this,
  11258 + function (style, result, cell, graphStyle, theGraph) {
  11259 + if (style != null) {
  11260 + if (cell != null) {
  11261 + // Handles special label background color
  11262 + if (result["labelBackgroundColor"] != null) {
  11263 + var bg = graphStyle != null ? graphStyle.background : null;
  11264 + theGraph = theGraph != null ? theGraph : graph;
  11265 +
  11266 + if (bg == null) {
  11267 + bg = theGraph.background;
  11268 + }
  11269 +
  11270 + if (bg == null) {
  11271 + bg = theGraph.defaultPageBackgroundColor;
  11272 + }
  11273 +
  11274 + result["labelBackgroundColor"] = bg;
  11275 + }
  11276 + }
  11277 +
  11278 + for (var key in style) {
  11279 + if (
  11280 + cell == null ||
  11281 + (result[key] != null && result[key] != mxConstants.NONE) ||
  11282 + (key != mxConstants.STYLE_FILLCOLOR &&
  11283 + key != mxConstants.STYLE_STROKECOLOR)
  11284 + ) {
  11285 + result[key] = style[key];
  11286 + }
  11287 + }
  11288 + }
  11289 + },
  11290 + );
  11291 +
  11292 + if (urlParams["sketch"] != "1") {
  11293 + var btn = mxUtils.button(
  11294 + mxResources.get("reset"),
  11295 + mxUtils.bind(this, function (evt) {
  11296 + var all = graph.getVerticesAndEdges(true, true);
  11297 +
  11298 + if (all.length > 0) {
  11299 + model.beginUpdate();
  11300 + try {
  11301 + graph.updateCellStyles({ sketch: null, rounded: null }, all);
  11302 + graph.updateCellStyles(
  11303 + { curved: null },
  11304 + graph.getVerticesAndEdges(false, true),
  11305 + );
  11306 + } finally {
  11307 + model.endUpdate();
  11308 + }
  11309 + }
  11310 +
  11311 + ui.clearDefaultStyle();
  11312 + }),
  11313 + );
  11314 +
  11315 + btn.setAttribute("title", mxResources.get("reset"));
  11316 + btn.style.textOverflow = "ellipsis";
  11317 + btn.style.maxWidth = "90px";
  11318 + right.appendChild(btn);
  11319 + }
  11320 +
  11321 + var createPreview = mxUtils.bind(
  11322 + this,
  11323 + function (commonStyle, vertexStyle, edgeStyle, graphStyle, container) {
  11324 + // Wrapper needed to catch events
  11325 + var div = document.createElement("div");
  11326 + div.style.position = "absolute";
  11327 + div.style.display = "inline-block";
  11328 + div.style.overflow = "hidden";
  11329 + div.style.pointerEvents = "none";
  11330 + div.style.width = "100%";
  11331 + div.style.height = "100%";
  11332 + container.appendChild(div);
  11333 +
  11334 + var graph2 = new Graph(div, null, null, graph.getStylesheet());
  11335 +
  11336 + graph2.resetViewOnRootChange = false;
  11337 + graph2.foldingEnabled = false;
  11338 + graph2.gridEnabled = false;
  11339 + graph2.autoScroll = false;
  11340 + graph2.setTooltips(false);
  11341 + graph2.setConnectable(false);
  11342 + graph2.setPanning(false);
  11343 + graph2.setEnabled(false);
  11344 +
  11345 + graph2.getCellStyle = function (cell, resolve) {
  11346 + resolve = resolve != null ? resolve : true;
  11347 + var result = mxUtils.clone(graph.getCellStyle.apply(this, arguments));
  11348 + var defaultStyle = graph.stylesheet.getDefaultVertexStyle();
  11349 + var appliedStyle = vertexStyle;
  11350 +
  11351 + if (model.isEdge(cell)) {
  11352 + defaultStyle = graph.stylesheet.getDefaultEdgeStyle();
  11353 + appliedStyle = edgeStyle;
  11354 + }
  11355 +
  11356 + removeStyles(result, defaultStyles, defaultStyle);
  11357 + applyStyle(commonStyle, result, cell, graphStyle, graph2);
  11358 + applyStyle(appliedStyle, result, cell, graphStyle, graph2);
  11359 +
  11360 + if (resolve) {
  11361 + result = graph.postProcessCellStyle(cell, result);
  11362 + }
  11363 +
  11364 + return result;
  11365 + };
  11366 +
  11367 + // Avoid HTML labels to capture events in bubble phase
  11368 + graph2.model.beginUpdate();
  11369 + try {
  11370 + var v1 = graph2.insertVertex(
  11371 + graph2.getDefaultParent(),
  11372 + null,
  11373 + "Shape",
  11374 + 14,
  11375 + 8,
  11376 + 70,
  11377 + 40,
  11378 + "strokeWidth=2;",
  11379 + );
  11380 + var e1 = graph2.insertEdge(
  11381 + graph2.getDefaultParent(),
  11382 + null,
  11383 + "Connector",
  11384 + v1,
  11385 + v1,
  11386 + "edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;endSize=5;strokeWidth=2;",
  11387 + );
  11388 + e1.geometry.points = [new mxPoint(32, 70)];
  11389 + e1.geometry.offset = new mxPoint(0, 8);
  11390 + } finally {
  11391 + graph2.model.endUpdate();
  11392 + }
  11393 + },
  11394 + );
  11395 +
  11396 + // Entries
  11397 + var entries = document.createElement("div");
  11398 + entries.style.position = "relative";
  11399 + div.appendChild(entries);
  11400 +
  11401 + // Cached entries
  11402 + if (this.format.cachedStyleEntries == null) {
  11403 + this.format.cachedStyleEntries = [];
  11404 + }
  11405 +
  11406 + function addKeys(style, result) {
  11407 + for (var key in style) {
  11408 + result.push(key);
  11409 + }
  11410 +
  11411 + return result;
  11412 + }
  11413 +
  11414 + var addEntry = mxUtils.bind(
  11415 + this,
  11416 + function (commonStyle, vertexStyle, edgeStyle, graphStyle, index) {
  11417 + var panel = this.format.cachedStyleEntries[index];
  11418 +
  11419 + if (panel == null) {
  11420 + panel = document.createElement("div");
  11421 + panel.style.display = "inline-block";
  11422 + panel.style.position = "relative";
  11423 + panel.style.width = "96px";
  11424 + panel.style.height = "90px";
  11425 + panel.style.cursor = "pointer";
  11426 + panel.style.border = "1px solid gray";
  11427 + panel.style.borderRadius = "8px";
  11428 + panel.style.margin = "2px";
  11429 + panel.style.overflow = "hidden";
  11430 +
  11431 + if (graphStyle != null && graphStyle.background != null) {
  11432 + panel.style.backgroundColor = graphStyle.background;
  11433 + }
  11434 +
  11435 + createPreview(commonStyle, vertexStyle, edgeStyle, graphStyle, panel);
  11436 +
  11437 + mxEvent.addGestureListeners(
  11438 + panel,
  11439 + mxUtils.bind(this, function (evt) {
  11440 + panel.style.opacity = 0.5;
  11441 + }),
  11442 + null,
  11443 + mxUtils.bind(this, function (evt) {
  11444 + panel.style.opacity = 1;
  11445 + graph.currentVertexStyle = mxUtils.clone(graph.defaultVertexStyle);
  11446 + graph.currentEdgeStyle = mxUtils.clone(graph.defaultEdgeStyle);
  11447 +
  11448 + applyStyle(commonStyle, graph.currentVertexStyle);
  11449 + applyStyle(commonStyle, graph.currentEdgeStyle);
  11450 + applyStyle(vertexStyle, graph.currentVertexStyle);
  11451 + applyStyle(edgeStyle, graph.currentEdgeStyle);
  11452 +
  11453 + if (urlParams["sketch"] == "1") {
  11454 + sketch = Editor.sketchMode;
  11455 + }
  11456 +
  11457 + if (sketch) {
  11458 + graph.currentEdgeStyle["sketch"] = "1";
  11459 + graph.currentVertexStyle["sketch"] = "1";
  11460 + } else {
  11461 + graph.currentEdgeStyle["sketch"] = "0";
  11462 + graph.currentVertexStyle["sketch"] = "0";
  11463 + }
  11464 +
  11465 + if (rounded) {
  11466 + graph.currentVertexStyle["rounded"] = "1";
  11467 + graph.currentEdgeStyle["rounded"] = "1";
  11468 + } else {
  11469 + graph.currentVertexStyle["rounded"] = "0";
  11470 + graph.currentEdgeStyle["rounded"] = "1";
  11471 + }
  11472 +
  11473 + if (curved) {
  11474 + graph.currentEdgeStyle["curved"] = "1";
  11475 + } else {
  11476 + graph.currentEdgeStyle["curved"] = "0";
  11477 + }
  11478 +
  11479 + model.beginUpdate();
  11480 + try {
  11481 + updateCells(
  11482 + addKeys(commonStyle, defaultStyles.slice()),
  11483 + graphStyle,
  11484 + );
  11485 +
  11486 + var change = new ChangePageSetup(
  11487 + ui,
  11488 + graphStyle != null ? graphStyle.background : null,
  11489 + );
  11490 + change.ignoreImage = true;
  11491 + model.execute(change);
  11492 +
  11493 + model.execute(
  11494 + new ChangeGridColor(
  11495 + ui,
  11496 + graphStyle != null && graphStyle.gridColor != null
  11497 + ? graphStyle.gridColor
  11498 + : gridColor,
  11499 + ),
  11500 + );
  11501 + } finally {
  11502 + model.endUpdate();
  11503 + }
  11504 + }),
  11505 + );
  11506 +
  11507 + mxEvent.addListener(
  11508 + panel,
  11509 + "mouseenter",
  11510 + mxUtils.bind(this, function (evt) {
  11511 + var prev = graph.getCellStyle;
  11512 + var prevBg = graph.background;
  11513 + var prevGrid = graph.view.gridColor;
  11514 +
  11515 + graph.background =
  11516 + graphStyle != null ? graphStyle.background : null;
  11517 + graph.view.gridColor =
  11518 + graphStyle != null && graphStyle.gridColor != null
  11519 + ? graphStyle.gridColor
  11520 + : gridColor;
  11521 +
  11522 + graph.getCellStyle = function (cell, resolve) {
  11523 + resolve = resolve != null ? resolve : true;
  11524 + var result = mxUtils.clone(prev.apply(this, arguments));
  11525 +
  11526 + var defaultStyle = graph.stylesheet.getDefaultVertexStyle();
  11527 + var appliedStyle = vertexStyle;
  11528 +
  11529 + if (model.isEdge(cell)) {
  11530 + defaultStyle = graph.stylesheet.getDefaultEdgeStyle();
  11531 + appliedStyle = edgeStyle;
  11532 + }
  11533 +
  11534 + removeStyles(result, defaultStyles, defaultStyle);
  11535 + applyStyle(commonStyle, result, cell, graphStyle);
  11536 + applyStyle(appliedStyle, result, cell, graphStyle);
  11537 +
  11538 + if (resolve) {
  11539 + result = this.postProcessCellStyle(cell, result);
  11540 + }
  11541 +
  11542 + return result;
  11543 + };
  11544 +
  11545 + graph.refresh();
  11546 + graph.getCellStyle = prev;
  11547 + graph.background = prevBg;
  11548 + graph.view.gridColor = prevGrid;
  11549 + }),
  11550 + );
  11551 +
  11552 + mxEvent.addListener(
  11553 + panel,
  11554 + "mouseleave",
  11555 + mxUtils.bind(this, function (evt) {
  11556 + graph.refresh();
  11557 + }),
  11558 + );
  11559 +
  11560 + // Workaround for broken cache in IE11
  11561 + if (!mxClient.IS_IE && !mxClient.IS_IE11) {
  11562 + this.format.cachedStyleEntries[index] = panel;
  11563 + }
  11564 + }
  11565 +
  11566 + entries.appendChild(panel);
  11567 + },
  11568 + );
  11569 +
  11570 + // Maximum palettes to switch the switcher
  11571 + var maxEntries = 10;
  11572 + var pageCount = Math.ceil(Editor.styles.length / maxEntries);
  11573 + this.format.currentStylePage =
  11574 + this.format.currentStylePage != null ? this.format.currentStylePage : 0;
  11575 + var dots = [];
  11576 +
  11577 + var addEntries = mxUtils.bind(this, function () {
  11578 + if (dots.length > 0) {
  11579 + dots[this.format.currentStylePage].style.background = "#84d7ff";
  11580 + }
  11581 +
  11582 + for (
  11583 + var i = this.format.currentStylePage * maxEntries;
  11584 + i <
  11585 + Math.min(
  11586 + (this.format.currentStylePage + 1) * maxEntries,
  11587 + Editor.styles.length
  11588 + );
  11589 + i++
  11590 + ) {
  11591 + var s = Editor.styles[i];
  11592 + addEntry(s.commonStyle, s.vertexStyle, s.edgeStyle, s.graph, i);
  11593 + }
  11594 + });
  11595 +
  11596 + var selectPage = mxUtils.bind(this, function (index) {
  11597 + if (index >= 0 && index < pageCount) {
  11598 + dots[this.format.currentStylePage].style.background = "transparent";
  11599 + entries.innerHTML = "";
  11600 + this.format.currentStylePage = index;
  11601 + addEntries();
  11602 + }
  11603 + });
  11604 +
  11605 + if (pageCount > 1) {
  11606 + // Selector
  11607 + var switcher = document.createElement("div");
  11608 + switcher.style.whiteSpace = "nowrap";
  11609 + switcher.style.position = "relative";
  11610 + switcher.style.textAlign = "center";
  11611 + switcher.style.paddingTop = "4px";
  11612 + switcher.style.width = "210px";
  11613 +
  11614 + div.style.paddingBottom = "8px";
  11615 +
  11616 + for (var i = 0; i < pageCount; i++) {
  11617 + var dot = document.createElement("div");
  11618 + dot.style.display = "inline-block";
  11619 + dot.style.width = "6px";
  11620 + dot.style.height = "6px";
  11621 + dot.style.marginLeft = "4px";
  11622 + dot.style.marginRight = "3px";
  11623 + dot.style.borderRadius = "3px";
  11624 + dot.style.cursor = "pointer";
  11625 + dot.style.background = "transparent";
  11626 + dot.style.border = "1px solid #b5b6b7";
  11627 +
  11628 + mxUtils.bind(this, function (index, elt) {
  11629 + mxEvent.addListener(
  11630 + dot,
  11631 + "click",
  11632 + mxUtils.bind(this, function () {
  11633 + selectPage(index);
  11634 + }),
  11635 + );
  11636 + })(i, dot);
  11637 +
  11638 + switcher.appendChild(dot);
  11639 + dots.push(dot);
  11640 + }
  11641 +
  11642 + div.appendChild(switcher);
  11643 + addEntries();
  11644 +
  11645 + if (pageCount < 15) {
  11646 + var left = document.createElement("div");
  11647 + left.style.position = "absolute";
  11648 + left.style.left = "0px";
  11649 + left.style.top = "0px";
  11650 + left.style.bottom = "0px";
  11651 + left.style.width = "24px";
  11652 + left.style.height = "24px";
  11653 + left.style.margin = "0px";
  11654 + left.style.cursor = "pointer";
  11655 + left.style.opacity = "0.5";
  11656 + left.style.backgroundRepeat = "no-repeat";
  11657 + left.style.backgroundPosition = "center center";
  11658 + left.style.backgroundSize = "24px 24px";
  11659 + left.style.backgroundImage = "url(" + Editor.previousImage + ")";
  11660 +
  11661 + if (Editor.isDarkMode()) {
  11662 + left.style.filter = "invert(100%)";
  11663 + }
  11664 +
  11665 + var right = left.cloneNode(false);
  11666 + right.style.backgroundImage = "url(" + Editor.nextImage + ")";
  11667 + right.style.left = "";
  11668 + right.style.right = "2px";
  11669 +
  11670 + switcher.appendChild(left);
  11671 + switcher.appendChild(right);
  11672 +
  11673 + mxEvent.addListener(
  11674 + left,
  11675 + "click",
  11676 + mxUtils.bind(this, function () {
  11677 + selectPage(mxUtils.mod(this.format.currentStylePage - 1, pageCount));
  11678 + }),
  11679 + );
  11680 +
  11681 + mxEvent.addListener(
  11682 + right,
  11683 + "click",
  11684 + mxUtils.bind(this, function () {
  11685 + selectPage(mxUtils.mod(this.format.currentStylePage + 1, pageCount));
  11686 + }),
  11687 + );
  11688 +
  11689 + // Hover state
  11690 + function addHoverState(elt) {
  11691 + mxEvent.addListener(elt, "mouseenter", function () {
  11692 + elt.style.opacity = "1";
  11693 + });
  11694 + mxEvent.addListener(elt, "mouseleave", function () {
  11695 + elt.style.opacity = "0.5";
  11696 + });
  11697 + }
  11698 +
  11699 + addHoverState(left);
  11700 + addHoverState(right);
  11701 + }
  11702 + } else {
  11703 + addEntries();
  11704 + }
  11705 +
  11706 + return div;
  11707 +};
  11708 +
  11709 +/**
  11710 + * Adds the label menu items to the given menu and parent.
  11711 + */
  11712 +DiagramStylePanel.prototype.destroy = function () {
  11713 + BaseFormatPanel.prototype.destroy.apply(this, arguments);
  11714 +
  11715 + if (this.darkModeChangedListener) {
  11716 + this.editorUi.removeListener(this.darkModeChangedListener);
  11717 + this.darkModeChangedListener = null;
  11718 + }
  11719 +};
  11720 +
  11721 +/**
  11722 + * Adds the label menu items to the given menu and parent.
  11723 + */
  11724 +DiagramFormatPanel = function (format, editorUi, container) {
  11725 + BaseFormatPanel.call(this, format, editorUi, container);
  11726 + this.init();
  11727 +};
  11728 +
  11729 +mxUtils.extend(DiagramFormatPanel, BaseFormatPanel);
  11730 +
  11731 +/**
  11732 + * Switch to disable page view.
  11733 + */
  11734 +DiagramFormatPanel.showPageView = true;
  11735 +
  11736 +/**
  11737 + * Specifies if the background image option should be shown. Default is true.
  11738 + */
  11739 +DiagramFormatPanel.prototype.showBackgroundImageOption = true;
  11740 +
  11741 +/**
  11742 + * Adds the label menu items to the given menu and parent.
  11743 + */
  11744 +DiagramFormatPanel.prototype.init = function () {
  11745 + var ui = this.editorUi;
  11746 + var editor = ui.editor;
  11747 + var graph = editor.graph;
  11748 +
  11749 + this.container.appendChild(this.addView(this.createPanel()));
  11750 +
  11751 + if (graph.isEnabled()) {
  11752 + this.container.appendChild(this.addOptions(this.createPanel()));
  11753 + this.container.appendChild(this.addPaperSize(this.createPanel()));
  11754 + this.container.appendChild(this.addStyleOps(this.createPanel()));
  11755 + }
  11756 +};
  11757 +
  11758 +/**
  11759 + * Adds the label menu items to the given menu and parent.
  11760 + */
  11761 +DiagramFormatPanel.prototype.addView = function (div) {
  11762 + var ui = this.editorUi;
  11763 + var editor = ui.editor;
  11764 + var graph = editor.graph;
  11765 +
  11766 + div.appendChild(this.createTitle(mxResources.get("view")));
  11767 +
  11768 + // Grid
  11769 + this.addGridOption(div);
  11770 +
  11771 + // Page View
  11772 + if (DiagramFormatPanel.showPageView) {
  11773 + div.appendChild(
  11774 + this.createOption(
  11775 + mxResources.get("pageView"),
  11776 + function () {
  11777 + return graph.pageVisible;
  11778 + },
  11779 + function (checked) {
  11780 + ui.actions.get("pageView").funct();
  11781 + },
  11782 + {
  11783 + install: function (apply) {
  11784 + this.listener = function () {
  11785 + apply(graph.pageVisible);
  11786 + };
  11787 +
  11788 + ui.addListener("pageViewChanged", this.listener);
  11789 + },
  11790 + destroy: function () {
  11791 + ui.removeListener(this.listener);
  11792 + },
  11793 + },
  11794 + ),
  11795 + );
  11796 + }
  11797 +
  11798 + if (graph.isEnabled()) {
  11799 + // Background
  11800 + var bg = this.createColorOption(
  11801 + mxResources.get("background"),
  11802 + function () {
  11803 + return graph.background;
  11804 + },
  11805 + function (color) {
  11806 + var change = new ChangePageSetup(ui, color);
  11807 + change.ignoreImage = color != null && color != mxConstants.NONE;
  11808 +
  11809 + graph.model.execute(change);
  11810 + },
  11811 + "#ffffff",
  11812 + {
  11813 + install: function (apply) {
  11814 + this.listener = function () {
  11815 + apply(graph.background);
  11816 + };
  11817 +
  11818 + ui.addListener("backgroundColorChanged", this.listener);
  11819 + },
  11820 + destroy: function () {
  11821 + ui.removeListener(this.listener);
  11822 + },
  11823 + },
  11824 + );
  11825 +
  11826 + if (this.showBackgroundImageOption) {
  11827 + var label = bg.getElementsByTagName("span")[0];
  11828 + label.style.display = "inline-block";
  11829 + label.style.textOverflow = "ellipsis";
  11830 + label.style.overflow = "hidden";
  11831 + label.style.maxWidth = "68px";
  11832 +
  11833 + if (mxClient.IS_FF) {
  11834 + label.style.marginTop = "1px";
  11835 + }
  11836 +
  11837 + var btn = mxUtils.button(mxResources.get("change"), function (evt) {
  11838 + ui.showBackgroundImageDialog(null, ui.editor.graph.backgroundImage);
  11839 + mxEvent.consume(evt);
  11840 + });
  11841 +
  11842 + btn.className = "geColorBtn";
  11843 + btn.style.position = "absolute";
  11844 + btn.style.marginTop = "-3px";
  11845 + btn.style.height = "22px";
  11846 + btn.style.left = "118px";
  11847 + btn.style.width = "56px";
  11848 +
  11849 + bg.appendChild(btn);
  11850 + }
  11851 +
  11852 + div.appendChild(bg);
  11853 + }
  11854 +
  11855 + return div;
  11856 +};
  11857 +
  11858 +/**
  11859 + * Adds the label menu items to the given menu and parent.
  11860 + */
  11861 +DiagramFormatPanel.prototype.addOptions = function (div) {
  11862 + var ui = this.editorUi;
  11863 + var editor = ui.editor;
  11864 + var graph = editor.graph;
  11865 +
  11866 + div.appendChild(this.createTitle(mxResources.get("options")));
  11867 +
  11868 + if (graph.isEnabled()) {
  11869 + // Connection arrows
  11870 + div.appendChild(
  11871 + this.createOption(
  11872 + mxResources.get("connectionArrows"),
  11873 + function () {
  11874 + return graph.connectionArrowsEnabled;
  11875 + },
  11876 + function (checked) {
  11877 + ui.actions.get("connectionArrows").funct();
  11878 + },
  11879 + {
  11880 + install: function (apply) {
  11881 + this.listener = function () {
  11882 + apply(graph.connectionArrowsEnabled);
  11883 + };
  11884 +
  11885 + ui.addListener("connectionArrowsChanged", this.listener);
  11886 + },
  11887 + destroy: function () {
  11888 + ui.removeListener(this.listener);
  11889 + },
  11890 + },
  11891 + ),
  11892 + );
  11893 +
  11894 + // Connection points
  11895 + div.appendChild(
  11896 + this.createOption(
  11897 + mxResources.get("connectionPoints"),
  11898 + function () {
  11899 + return graph.connectionHandler.isEnabled();
  11900 + },
  11901 + function (checked) {
  11902 + ui.actions.get("connectionPoints").funct();
  11903 + },
  11904 + {
  11905 + install: function (apply) {
  11906 + this.listener = function () {
  11907 + apply(graph.connectionHandler.isEnabled());
  11908 + };
  11909 +
  11910 + ui.addListener("connectionPointsChanged", this.listener);
  11911 + },
  11912 + destroy: function () {
  11913 + ui.removeListener(this.listener);
  11914 + },
  11915 + },
  11916 + ),
  11917 + );
  11918 +
  11919 + // Guides
  11920 + div.appendChild(
  11921 + this.createOption(
  11922 + mxResources.get("guides"),
  11923 + function () {
  11924 + return graph.graphHandler.guidesEnabled;
  11925 + },
  11926 + function (checked) {
  11927 + ui.actions.get("guides").funct();
  11928 + },
  11929 + {
  11930 + install: function (apply) {
  11931 + this.listener = function () {
  11932 + apply(graph.graphHandler.guidesEnabled);
  11933 + };
  11934 +
  11935 + ui.addListener("guidesEnabledChanged", this.listener);
  11936 + },
  11937 + destroy: function () {
  11938 + ui.removeListener(this.listener);
  11939 + },
  11940 + },
  11941 + ),
  11942 + );
  11943 + }
  11944 +
  11945 + return div;
  11946 +};
  11947 +
  11948 +/**
  11949 + *
  11950 + */
  11951 +DiagramFormatPanel.prototype.addGridOption = function (container) {
  11952 + var fPanel = this;
  11953 + var ui = this.editorUi;
  11954 + var graph = ui.editor.graph;
  11955 +
  11956 + var input = document.createElement("input");
  11957 + input.style.position = "absolute";
  11958 + input.style.textAlign = "right";
  11959 + input.style.width = "48px";
  11960 + input.style.marginTop = "-2px";
  11961 + input.style.height = "21px";
  11962 + input.style.border = "1px solid rgb(160, 160, 160)";
  11963 + input.style.borderRadius = "4px";
  11964 + input.style.boxSizing = "border-box";
  11965 + input.value = this.inUnit(graph.getGridSize()) + " " + this.getUnit();
  11966 +
  11967 + var stepper = this.createStepper(
  11968 + input,
  11969 + update,
  11970 + this.getUnitStep(),
  11971 + null,
  11972 + null,
  11973 + null,
  11974 + this.isFloatUnit(),
  11975 + );
  11976 + input.style.display = graph.isGridEnabled() ? "" : "none";
  11977 + stepper.style.display = input.style.display;
  11978 +
  11979 + mxEvent.addListener(input, "keydown", function (e) {
  11980 + if (e.keyCode == 13) {
  11981 + graph.container.focus();
  11982 + mxEvent.consume(e);
  11983 + } else if (e.keyCode == 27) {
  11984 + input.value = graph.getGridSize();
  11985 + graph.container.focus();
  11986 + mxEvent.consume(e);
  11987 + }
  11988 + });
  11989 +
  11990 + function update(evt) {
  11991 + var value = fPanel.isFloatUnit()
  11992 + ? parseFloat(input.value)
  11993 + : parseInt(input.value);
  11994 + value = fPanel.fromUnit(
  11995 + Math.max(fPanel.inUnit(1), isNaN(value) ? fPanel.inUnit(10) : value),
  11996 + );
  11997 +
  11998 + if (value != graph.getGridSize()) {
  11999 + mxGraph.prototype.gridSize = value;
  12000 + graph.setGridSize(value);
  12001 + }
  12002 +
  12003 + input.value = fPanel.inUnit(value) + " " + fPanel.getUnit();
  12004 + mxEvent.consume(evt);
  12005 + }
  12006 +
  12007 + mxEvent.addListener(input, "blur", update);
  12008 + mxEvent.addListener(input, "change", update);
  12009 +
  12010 + input.style.right = "78px";
  12011 + stepper.style.marginTop = "-17px";
  12012 + stepper.style.right = "66px";
  12013 +
  12014 + var panel = this.createColorOption(
  12015 + mxResources.get("grid"),
  12016 + function () {
  12017 + var color = graph.view.gridColor;
  12018 +
  12019 + return graph.isGridEnabled() ? color : null;
  12020 + },
  12021 + function (color) {
  12022 + var enabled = graph.isGridEnabled();
  12023 +
  12024 + if (color == mxConstants.NONE) {
  12025 + graph.setGridEnabled(false);
  12026 + } else {
  12027 + graph.setGridEnabled(true);
  12028 + ui.setGridColor(color);
  12029 + }
  12030 +
  12031 + input.style.display = graph.isGridEnabled() ? "" : "none";
  12032 + stepper.style.display = input.style.display;
  12033 +
  12034 + if (enabled != graph.isGridEnabled()) {
  12035 + graph.defaultGridEnabled = graph.isGridEnabled();
  12036 + ui.fireEvent(new mxEventObject("gridEnabledChanged"));
  12037 + }
  12038 + },
  12039 + Editor.isDarkMode()
  12040 + ? graph.view.defaultDarkGridColor
  12041 + : graph.view.defaultGridColor,
  12042 + {
  12043 + install: function (apply) {
  12044 + this.listener = function () {
  12045 + apply(graph.isGridEnabled() ? graph.view.gridColor : null);
  12046 + };
  12047 +
  12048 + ui.addListener("gridColorChanged", this.listener);
  12049 + ui.addListener("gridEnabledChanged", this.listener);
  12050 + },
  12051 + destroy: function () {
  12052 + ui.removeListener(this.listener);
  12053 + },
  12054 + },
  12055 + );
  12056 +
  12057 + panel.appendChild(input);
  12058 + panel.appendChild(stepper);
  12059 + container.appendChild(panel);
  12060 +};
  12061 +
  12062 +/**
  12063 + * Adds the label menu items to the given menu and parent.
  12064 + */
  12065 +DiagramFormatPanel.prototype.addDocumentProperties = function (div) {
  12066 + // Hook for subclassers
  12067 + var ui = this.editorUi;
  12068 + var editor = ui.editor;
  12069 + var graph = editor.graph;
  12070 +
  12071 + div.appendChild(this.createTitle(mxResources.get("options")));
  12072 +
  12073 + return div;
  12074 +};
  12075 +
  12076 +/**
  12077 + * Adds the label menu items to the given menu and parent.
  12078 + */
  12079 +DiagramFormatPanel.prototype.addPaperSize = function (div) {
  12080 + var ui = this.editorUi;
  12081 + var editor = ui.editor;
  12082 + var graph = editor.graph;
  12083 +
  12084 + div.appendChild(this.createTitle(mxResources.get("paperSize")));
  12085 +
  12086 + var accessor = PageSetupDialog.addPageFormatPanel(
  12087 + div,
  12088 + "formatpanel",
  12089 + graph.pageFormat,
  12090 + function (pageFormat) {
  12091 + if (
  12092 + graph.pageFormat == null ||
  12093 + graph.pageFormat.width != pageFormat.width ||
  12094 + graph.pageFormat.height != pageFormat.height
  12095 + ) {
  12096 + var change = new ChangePageSetup(ui, null, null, pageFormat);
  12097 + change.ignoreColor = true;
  12098 + change.ignoreImage = true;
  12099 +
  12100 + graph.model.execute(change);
  12101 + }
  12102 + },
  12103 + );
  12104 +
  12105 + this.addKeyHandler(accessor.widthInput, function () {
  12106 + accessor.set(graph.pageFormat);
  12107 + });
  12108 + this.addKeyHandler(accessor.heightInput, function () {
  12109 + accessor.set(graph.pageFormat);
  12110 + });
  12111 +
  12112 + var listener = function () {
  12113 + accessor.set(graph.pageFormat);
  12114 + };
  12115 +
  12116 + ui.addListener("pageFormatChanged", listener);
  12117 + this.listeners.push({
  12118 + destroy: function () {
  12119 + ui.removeListener(listener);
  12120 + },
  12121 + });
  12122 +
  12123 + graph.getModel().addListener(mxEvent.CHANGE, listener);
  12124 + this.listeners.push({
  12125 + destroy: function () {
  12126 + graph.getModel().removeListener(listener);
  12127 + },
  12128 + });
  12129 +
  12130 + return div;
  12131 +};
  12132 +
  12133 +/**
  12134 + * Adds the label menu items to the given menu and parent.
  12135 + */
  12136 +DiagramFormatPanel.prototype.addStyleOps = function (div) {
  12137 + this.addActions(div, ["editData"]);
  12138 + this.addActions(div, ["clearDefaultStyle"]);
  12139 +
  12140 + return div;
  12141 +};
  12142 +
  12143 +/**
  12144 + * Adds the label menu items to the given menu and parent.
  12145 + */
  12146 +DiagramFormatPanel.prototype.destroy = function () {
  12147 + BaseFormatPanel.prototype.destroy.apply(this, arguments);
  12148 +
  12149 + if (this.gridEnabledListener) {
  12150 + this.editorUi.removeListener(this.gridEnabledListener);
  12151 + this.gridEnabledListener = null;
  12152 + }
  12153 +};
  12154 +
  12155 +// TODO UseLayUi
  12156 +class UseLayUi {
  12157 +
  12158 + constructor() {
  12159 + }
  12160 +
  12161 + static isFunction = fn => {
  12162 + const result = Object.prototype.toString.call(fn)
  12163 + return result === '[object Function]' || result === '[object AsyncFunction]'
  12164 + }
  12165 +
  12166 + static dynamicAttr = (attr, value) => value ? `${attr}="${value}"` : ''
  12167 +
  12168 + /**
  12169 + * @description format data source to tree structure
  12170 + * @param {object[]} data - data source
  12171 + * @param {Function} [customSetValue = ((record) => ({ id: record.id, title: record.name }))] customSetValue - custom set value && need return value
  12172 + * @param {string} [idField = 'id'] idField - id field
  12173 + * @param {string} [titleField = 'name'] titleField - titleField field
  12174 + * @param {string} [childrenField = 'children'] childrenField - children field
  12175 + * @returns {object[]}
  12176 + */
  12177 + static formatTreeDataSource(data, customSetValue, idField = 'id', titleField = 'name', childrenField = 'children') {
  12178 + if (!Array.isArray(data) && !data.length) return []
  12179 + customSetValue = customSetValue || ((record) => ({ id: record[idField], title: record[titleField] }))
  12180 + const fn = (data) => {
  12181 + return data.reduce((prev, next = {}) => {
  12182 + let children = []
  12183 + if (next[childrenField] && next[childrenField].length) {
  12184 + children = fn(next.children)
  12185 + }
  12186 + return [...prev, { children, ...customSetValue(next) }]
  12187 + }, [])
  12188 + }
  12189 + return fn(data)
  12190 + }
  12191 +
  12192 + /**
  12193 + * @description 通过ID 找到对应节点
  12194 + * @param dataSource
  12195 + * @param value
  12196 + * @param findField
  12197 + * @param childrenField
  12198 + * @returns {any}
  12199 + */
  12200 + static findTreeObjectByField(dataSource, value, findField = 'id', childrenField = 'children') {
  12201 + const map = new Map()
  12202 +
  12203 + function find(array) {
  12204 + for (const item of array) {
  12205 + map.set(item[findField], item)
  12206 + if (map.has(item[value])) {
  12207 + break
  12208 + }
  12209 + if (item[childrenField] && Array.isArray(item[childrenField])) {
  12210 + find(item[childrenField])
  12211 + }
  12212 + }
  12213 + }
  12214 +
  12215 + if (Array.isArray(dataSource)) {
  12216 + find(dataSource)
  12217 + return map.get(value)
  12218 + }
  12219 + }
  12220 +
  12221 + /**
  12222 + * @description generator options template 生产下拉选项模板
  12223 + * @param options
  12224 + * @param {object[]} [dataSource] options.dataSource
  12225 + * @param {boolean} [addPlaceholderOption = true] options.addPlaceholderOption
  12226 + * @param {string} [labelField = 'name'] options.labelField
  12227 + * @param {string} [valueField = 'name'] options.valueField
  12228 + * @returns {*}
  12229 + */
  12230 + static generateOptionTemplate(options) {
  12231 + const { dataSource = [], addPlaceholderOption = true, labelField = 'name', valueField = 'id' } = options
  12232 + let { renderFn } = options
  12233 + renderFn = renderFn || ((record) => {
  12234 + if (typeof record === 'object') {
  12235 + return `<option value="${record[valueField]}" ${record.disabled ? 'disabled=""' : ''}>${record[labelField]}</option>`
  12236 + } else {
  12237 + return `<option value="${record}">${record}</option>`
  12238 + }
  12239 + })
  12240 + const optionsList = dataSource.map(renderFn)
  12241 + if (addPlaceholderOption) optionsList.unshift('<option value="">请选择</option>')
  12242 + return optionsList.join('')
  12243 + }
  12244 +
  12245 + /**
  12246 + * @description create single usage controls
  12247 + * @param {string} content
  12248 + */
  12249 + static createSingleUseFormItem(content, props = '') {
  12250 + return `
  12251 + <div class="layui-form" ${props}>
  12252 + ${content}
  12253 + </div>
  12254 + `
  12255 + }
  12256 +
  12257 + /**
  12258 + * @description 判断节点是否在某个节点之内
  12259 + * @param {HTMLElement} parentNode
  12260 + * @param {HTMLElement} childrenNode
  12261 + * @param {boolean} [containsItsOwn = false] containsItsOwn - 是否包含自身
  12262 + * @returns {boolean|*}
  12263 + */
  12264 + static isInNode(parentNode, childrenNode, containsItsOwn = false) {
  12265 + return parentNode === childrenNode
  12266 + ? containsItsOwn
  12267 + : parentNode.contains(childrenNode)
  12268 + }
  12269 +
  12270 + /**
  12271 + * @description create layui single select template
  12272 + * @param {string} [options.layFilter] options.layFilter
  12273 + * @param {string} [options.bindValueFiled] options.bindValueFiled
  12274 + * @param {string} [options.layVerify] options.layVerify
  12275 + * @param {string} [options.layVerType] options.layVerType
  12276 + * @param {Function} [options.renderFn] options.renderFn
  12277 + * @param {Array} [options.dataSource = []] options.dataSource
  12278 + * @param {string} [options.valueField = id] options.valueField
  12279 + * @param {string} [options.labelField = name] options.labelField - dataSource
  12280 + * @param {string} [options.label = 选择框] options.label - select label
  12281 + * @param {string} [options.className = ''] options.className - css class name
  12282 + * @param {Function} [options.onClick] options.onClick - select on click
  12283 + * @returns {string}
  12284 + */
  12285 + static createSelect(options) {
  12286 + const { form, jquery: $ } = layui
  12287 +
  12288 + const {
  12289 + elem,
  12290 + layFilter,
  12291 + bindValueFiled,
  12292 + layVerify,
  12293 + layVerType,
  12294 + renderFn,
  12295 + dataSource = [],
  12296 + valueField = 'id',
  12297 + labelField = 'name',
  12298 + label = '',
  12299 + className = '',
  12300 + onClick,
  12301 + singleUsage = true,
  12302 + } = options
  12303 +
  12304 + const formatDataSource = (dataSource) => {
  12305 + return dataSource.map(record => {
  12306 + return {
  12307 + value: record[valueField],
  12308 + label: record[labelField],
  12309 + }
  12310 + })
  12311 + }
  12312 +
  12313 + const getOptionList = (dataSource) => {
  12314 + if (renderFn && this.isFunction(renderFn)) {
  12315 + return dataSource.map(record => renderFn(record))
  12316 + }
  12317 + return formatDataSource(dataSource).map(record => {
  12318 + return `<option value="${record[valueField]}" ${record.disable ? 'disabled=""' : ''}>${record[labelField]}</option>`
  12319 + })
  12320 + }
  12321 +
  12322 + const generateOptionTemplate = (dataSource, addPlaceholderOption = true) => {
  12323 + const optionsList = getOptionList(dataSource)
  12324 + if (addPlaceholderOption) optionsList.unshift('<option value="">请选择</option>')
  12325 + return optionsList.join('')
  12326 + }
  12327 +
  12328 + if (layFilter) {
  12329 + form.on(`select(${layFilter})`, (data) => {
  12330 + if (onClick && this.isFunction(onClick)) {
  12331 + onClick(data)
  12332 + }
  12333 + })
  12334 + }
  12335 +
  12336 + let template = `
  12337 + <div class="layui-form-item ${className}">
  12338 + <label class="layui-form-label">${label}</label>
  12339 + <div class="layui-input-block">
  12340 + <select name="${bindValueFiled}" ${this.dynamicAttr('lay-filter', layFilter)} ${this.dynamicAttr('lay-verify', layVerify)} ${this.dynamicAttr('lay-verType', layVerType)}>
  12341 + ${generateOptionTemplate(dataSource)}
  12342 + </select>
  12343 + </div>
  12344 + </div>`
  12345 +
  12346 + const updateOptions = (dataSource) => {
  12347 + if (!elem) return
  12348 + const optionList = generateOptionTemplate(dataSource)
  12349 + $(elem).find('select').html(optionList)
  12350 + form.render('select', layFilter)
  12351 + }
  12352 +
  12353 + template = singleUsage ? UseLayUi.createSingleUseFormItem(template, '') : template
  12354 +
  12355 +
  12356 + function mount() {
  12357 + if (elem) {
  12358 + UseLayUi.nextTick(() => {
  12359 + $(elem).html(template)
  12360 + })
  12361 + return { template, updateOptions }
  12362 + }
  12363 + return template
  12364 + }
  12365 +
  12366 + return mount()
  12367 + }
  12368 +
  12369 + // TODO Tree Select
  12370 + /**
  12371 + * @description create a tree select controls
  12372 + * @param {string} [options.layFilter] options.layFilter
  12373 + * @param {string} [options.label] options.label
  12374 + * @param {object} [options.treeProps] options.treeProps
  12375 + * @param {HTMLDivElement} [options.elem] options.elem
  12376 + * @param {boolean} [options.singleUsage = true] options.singleUsage
  12377 + * @param {Function} [options.customSetTree = ((record) => ({ id: record.id, title: record.name }))] options.customSetTree
  12378 + * @param {boolean} [options.autoFormatDataSource = true] options.autoFormatDataSource
  12379 + * @param {string} [options.layVerify] options.layVerify
  12380 + * @param {string} [options.layVerType] options.layVerType
  12381 + * @param {Function} [options.treeProps.onReady] options.treeProps.onReady
  12382 + */
  12383 + static createTreeSelect(options) {
  12384 + const CLASS_NAME = 'things-kit-tree-select'
  12385 + const SELECT_CLS = 'things-kit-tree-select__tree'
  12386 + const { tree, jquery: $, form } = layui
  12387 +
  12388 + const {
  12389 + layFilter,
  12390 + label,
  12391 + treeProps = {},
  12392 + elem,
  12393 + className = '',
  12394 + singleUsage = true,
  12395 + customSetTree = undefined,
  12396 + autoFormatDataSource = true,
  12397 + labelField = 'name',
  12398 + valueField = 'id',
  12399 + childrenField = 'children',
  12400 + layVerify,
  12401 + layVerType,
  12402 + } = options
  12403 +
  12404 + let { data = [], click, onReady } = treeProps
  12405 +
  12406 + let template = `
  12407 + <div class="layui-form-item ${CLASS_NAME} ${className}">
  12408 + <label class="layui-form-label">${label}</label>
  12409 + <div class="layui-input-block">
  12410 + <div class="layui-unselect layui-form-select ${SELECT_CLS}">
  12411 + <div class="layui-select-title">
  12412 + <span class="layui-input layui-unselect tree-select__label">请选择</span>
  12413 + <input ${this.dynamicAttr('lay-verify', layVerify)} ${this.dynamicAttr('lay-verType', layVerType)} type="text" style="visibility: hidden; position: absolute; top: 0" name="${layFilter}">
  12414 + <i class="layui-edge"></i>
  12415 + </div>
  12416 + <dl class="layui-anim layui-anim-upbit">
  12417 + <dd>
  12418 + <ul class="tree-select__tree-mount"></ul>
  12419 + </dd>
  12420 + </dl>
  12421 + </div>
  12422 + </div>
  12423 + </div>`
  12424 +
  12425 + template = singleUsage ? UseLayUi.createSingleUseFormItem(template) : template
  12426 +
  12427 + /**
  12428 + * @description 设置下拉树值
  12429 + * @param {string} id - layui tree 树形结构 title字段
  12430 + * @param {string} title - layui tree 树形结构 id字段
  12431 + */
  12432 + function setValue({ id, title }) {
  12433 + $(elem)
  12434 + .find('.layui-form-select').removeClass('layui-form-selected').end()
  12435 + .find(".layui-select-title span").html(title).end()
  12436 + .find(`input[name='${layFilter}']`).val(id);
  12437 + }
  12438 +
  12439 + // init mount
  12440 + function mount() {
  12441 + // mount select container
  12442 + $(elem).html(template)
  12443 +
  12444 + // mount tree
  12445 + // UseLayUi.nextTick(() => {
  12446 + tree.render({
  12447 + ...treeProps,
  12448 + ...(autoFormatDataSource ? { data: UseLayUi.formatTreeDataSource(data, customSetTree, valueField, labelField, childrenField) } : {}),
  12449 + elem: $(elem).find('.tree-select__tree-mount'),
  12450 + click(node) {
  12451 + setValue(node.data)
  12452 + if (UseLayUi.isFunction(click)) click(node)
  12453 + },
  12454 + })
  12455 +
  12456 + // focus
  12457 + $(`.${SELECT_CLS}`).off('click')
  12458 + .on("click", ".layui-select-title", function (e) {
  12459 + $(document).find('.layui-form-select').removeClass('layui-form-selected')
  12460 + $(this).parents(`.${SELECT_CLS}`).toggleClass("layui-form-selected");
  12461 + layui.stope(e);
  12462 + })
  12463 + .on('click', '.layui-anim', (e) => {
  12464 + layui.stope(e)
  12465 + })
  12466 + .on("click", "dl i", function (e) {
  12467 + layui.stope(e);
  12468 + })
  12469 +
  12470 + // blur
  12471 + // $(document)
  12472 + // .on("click", function (e) {
  12473 + // const target = e.target
  12474 + // const parentNode = $(`.${CLASS_NAME} .tree-select__tree-mount`)
  12475 + // if (!parentNode) return
  12476 + // console.log($.contains(parentNode, target))
  12477 + // // const showClose = UseLayUi.isInNode(parentNode, target, true)
  12478 + // // if (showClose) return
  12479 + // // $(`.${ SELECT_CLS }`).removeClass("layui-form-selected")
  12480 + // });
  12481 +
  12482 + if (UseLayUi.isFunction(onReady)) {
  12483 + onReady(setValue)
  12484 + }
  12485 + // })
  12486 + form.render()
  12487 + }
  12488 +
  12489 + mount()
  12490 + }
  12491 +
  12492 + static createCheckBox(options) {
  12493 + const { jquery: $, form } = layui
  12494 + const CLASS_NAME = 'things-kit__checkbox'
  12495 + let {
  12496 + elem,
  12497 + singleUsage = true,
  12498 + layFilter = '',
  12499 + layVerify = '',
  12500 + label = '',
  12501 + laySkin = 'primary',
  12502 + dataSource = [],
  12503 + customSetValue,
  12504 + labelField = 'name',
  12505 + valueField = 'id',
  12506 + checkedField = 'checked',
  12507 + onChange,
  12508 + } = options
  12509 + customSetValue = customSetValue || ((record) => ({ value: record.id, title: record.name }))
  12510 + if (!Array.isArray(dataSource)) {
  12511 + dataSource = [dataSource]
  12512 + }
  12513 +
  12514 + function createOptions(dataSource) {
  12515 + return dataSource.map((record) => {
  12516 + return `<input
  12517 + type="checkbox" lay-skin="${laySkin}"
  12518 + ${UseLayUi.dynamicAttr('name', record[valueField])}
  12519 + ${UseLayUi.dynamicAttr('title', record[labelField])}
  12520 + ${UseLayUi.dynamicAttr('lay-filter', layFilter)}
  12521 + ${UseLayUi.dynamicAttr('lay-verify', layVerify)}
  12522 + ${UseLayUi.dynamicAttr('checked', record[checkedField])}
  12523 + >`
  12524 + })
  12525 + }
  12526 +
  12527 + let template = `
  12528 + <div class="layui-form-item ${CLASS_NAME}" >
  12529 + <label class="layui-form-label">${label}</label>
  12530 + <div class="layui-input-block">
  12531 + ${createOptions(dataSource).join('')}
  12532 + </div>
  12533 + </div>`
  12534 +
  12535 + template = singleUsage ? UseLayUi.createSingleUseFormItem(template) : template
  12536 +
  12537 + function mount() {
  12538 + if (!elem) return template
  12539 + $(elem).empty()
  12540 + $(elem).append(template)
  12541 + form.on(`checkbox(${layFilter})`, (data) => {
  12542 + onChange && (onChange(data))
  12543 + })
  12544 + form.render()
  12545 + }
  12546 +
  12547 + return mount()
  12548 + }
  12549 +
  12550 + /**
  12551 + * @description 创建树形组件
  12552 + * @param {object} options
  12553 + * @param {string | object} options.elem - 挂载节点
  12554 + * @param {object[]} [data] options.data - 数据源
  12555 + * @param {string} [id] options.id - 用于基础方法传参使用
  12556 + * @param {boolean} [showCheckbox = false] options.showCheckbox - 显示复选框
  12557 + * @param {boolean|string[]} [edit] options.edit - 是否开启节点操作
  12558 + * @param {boolean} [accordion = false] options.accordion - 手风琴
  12559 + * @param {boolean} [onlyIconControl = false] options.onlyIconControl - 仅允许左侧图标展开收缩
  12560 + * @param {boolean} [isJump = false] options.isJump - 跳转
  12561 + * @param {boolean} [showLine = true] options.showLine - 显示连接线
  12562 + * @param {object} [text] options.text - 自定义默认值
  12563 + * @param {string} [valueField = 'id'] options.valueField - 值字段
  12564 + * @param {string} [labelField = 'name'] options.labelField - 标签字段
  12565 + * @param {string} [childrenField = 'children'] options.childrenField - 后代节点字段
  12566 + * @param {Function} [customSetValue] options.customSetValue - 后代节点字段
  12567 + * @param {boolean} [autoFormatValue = true] options.autoFormatValue - 自动格式化值
  12568 + */
  12569 + static createTree(options = {}) {
  12570 + const { tree } = layui
  12571 + const {
  12572 + data = [],
  12573 + valueField = 'id',
  12574 + labelField = 'name',
  12575 + childrenField = 'children',
  12576 + customSetValue,
  12577 + autoFormatValue = true,
  12578 + } = options
  12579 + tree.render({
  12580 + ...options,
  12581 + data: autoFormatValue ? UseLayUi.formatTreeDataSource(data, customSetValue, valueField, labelField, childrenField) : data,
  12582 + })
  12583 + }
  12584 +
  12585 +
  12586 + static nextTick(fn) {
  12587 + new Promise((resolve) => {
  12588 + resolve()
  12589 + }).then(() => {
  12590 + fn()
  12591 + })
  12592 + }
  12593 +
  12594 + static msg(msg, options) {
  12595 + const { layer } = layui
  12596 + layer.msg(msg, options)
  12597 + }
  12598 +
  12599 + static successMsg(msg = '操作成功', options) {
  12600 + UseLayUi.msg(msg, { ...options, icon: 6 })
  12601 + }
  12602 +
  12603 + static errorMsg(msg = '操作失败', options) {
  12604 + UseLayUi.msg(msg, { ...options, icon: 5 })
  12605 + }
  12606 +
  12607 + static topMsg(msg, options, icon) {
  12608 + const { layer } = layui
  12609 + layer.msg(`<div style="padding: 20px; display: flex; align-items: center;"><i class="layui-layer-ico layui-layer-ico${icon}" style="width: 30px;height: 30px;"></i><span style="margin-left: 5px">${msg}</span></div>`, { ...options, type: 1, icon, time: 2000, })
  12610 + }
  12611 +
  12612 + static topSuccessMsg(msg = '操作成功', options) {
  12613 + this.topMsg(msg, options, 6)
  12614 + }
  12615 +
  12616 + static topErrorMsg(msg = '操作失败', options) {
  12617 + this.topMsg(msg, options, 5)
  12618 + }
  12619 +
  12620 + static topInfoMsg(msg = '提示', options) {
  12621 + this.topMsg(msg, options, 0)
  12622 + }
  12623 +}
  12624 +
  12625 +
  12626 +/**
  12627 + * @description use to function capture await throw error
  12628 + * @param promise
  12629 + * @param errorExt - Additional Information you can pass to the err object
  12630 + */
  12631 +function to(promise, errorExt) {
  12632 + return promise
  12633 + .then((data) => [null, data])
  12634 + .catch((err) => {
  12635 + if (errorExt) {
  12636 + const parsedError = Object.assign({}, err, errorExt)
  12637 + return [parsedError, undefined]
  12638 + }
  12639 + return [err, undefined]
  12640 + })
  12641 +}
  12642 +
  12643 +
  12644 +class EventCenter {
  12645 +
  12646 + /**
  12647 + * @description 调度中心
  12648 + * @type {Map<string, Function[]>}
  12649 + */
  12650 + eventStack
  12651 +
  12652 + /**
  12653 + * @description 实例化EventCenter
  12654 + */
  12655 + constructor() {
  12656 + this.eventStack = new Map()
  12657 + }
  12658 +
  12659 + /**
  12660 + * @description 监听事件
  12661 + * @param {string | number} eventName - 事件名
  12662 + * @param {Function} callback - 事件处理方法
  12663 + */
  12664 + on(eventName, callback) {
  12665 + if (this.eventStack.has(eventName)) {
  12666 + this.eventStack.get(eventName).push(callback)
  12667 + return
  12668 + }
  12669 + this.eventStack.set(eventName, [callback])
  12670 + }
  12671 +
  12672 + /**
  12673 + * @description 触发事件
  12674 + * @param {string | number} eventName - 事件名
  12675 + * @param {any} args - 参数
  12676 + */
  12677 + emit(eventName, ...args) {
  12678 + if (this.eventStack.has(eventName))
  12679 + this.eventStack.get(eventName).forEach(fn => fn(...args))
  12680 + }
  12681 +
  12682 + /**
  12683 + * @description 移除事件
  12684 + * @param {string} eventName - 时间名
  12685 + */
  12686 + off(eventName) {
  12687 + if (this.eventStack.has(eventName))
  12688 + this.eventStack.delete(eventName)
  12689 + }
  12690 +
  12691 + /**
  12692 + * @description 清除事件
  12693 + */
  12694 + clean(eventName) {
  12695 + this.eventStack.clear()
  12696 + }
  12697 +
  12698 +
  12699 +}
  12700 +
  12701 +class Ws {
  12702 + /**
  12703 + * @description url 连接地址
  12704 + */
  12705 + url
  12706 +
  12707 + /**
  12708 + * @description 协议
  12709 + */
  12710 + protocols
  12711 +
  12712 + /**
  12713 + * @description socket open时 拓展
  12714 + * @type {Function}
  12715 + * @param {Ws} instance - 实例
  12716 + */
  12717 + onopenCallback
  12718 +
  12719 + /**
  12720 + * @description socket error时 拓展
  12721 + * @type {Function}
  12722 + * @param {Ws} instance - 实例
  12723 + */
  12724 + onerrorCallback
  12725 +
  12726 + /**
  12727 + * @description socket close时 拓展
  12728 + * @type {Function}
  12729 + * @param {Ws} instance - 实例
  12730 + */
  12731 + oncloseCallback
  12732 +
  12733 + /**
  12734 + * @description socket message时 拓展
  12735 + * @type {Function}
  12736 + * @param {object} data - event.data message数据
  12737 + * @param {MessageEvent} event - event 事件对象
  12738 + * @param {Ws} instance - Ws 实例
  12739 + */
  12740 + onmessageCallback
  12741 +
  12742 + /**
  12743 + * @description socket 实例
  12744 + * @type {WebSocket}
  12745 + */
  12746 + ws
  12747 +
  12748 + /**
  12749 + * @description 重连标志
  12750 + * @type {boolean}
  12751 + */
  12752 + isReconnectionLoading = false
  12753 +
  12754 + /**
  12755 + * @description 重连定时器
  12756 + */
  12757 + timeId = null
  12758 +
  12759 + /**
  12760 + * @description 手动关闭标志位
  12761 + * @type {boolean}
  12762 + */
  12763 + isCustomClose = false
  12764 +
  12765 + /**
  12766 + * @description 消息队列 重新连接后会将之前断开期间发送的消息重新推送
  12767 + * @type {Array}
  12768 + */
  12769 + errorStack = []
  12770 +
  12771 + /**
  12772 + * @description 实例
  12773 + */
  12774 + static instance
  12775 +
  12776 + /**
  12777 + * @description 创建websocket实例
  12778 + * @param {string} option.url - 连接路径
  12779 + * @param {string} [option.protocols] option.protocols - 协议
  12780 + * @param {Function} [option.onopenCallback] option.onopenCallback - socket open时 拓展
  12781 + * @param {Function} [option.onerrorCallback] option.onerrorCallback - socket error时 拓展
  12782 + * @param {Function} [option.oncloseCallback] option.oncloseCallback - socket close时 拓展
  12783 + * @param {Function} [option.onmessageCallback] option.onmessageCallback - socket message时 拓展
  12784 + */
  12785 + constructor(option) {
  12786 + const { url, protocols, onopenCallback, onerrorCallback, oncloseCallback, onmessageCallback } = option
  12787 + if (!url) throw new Error('param url in Ws constructor is required')
  12788 + this.url = url
  12789 + this.protocols = protocols
  12790 + this.onopenCallback = onopenCallback
  12791 + this.onerrorCallback = onerrorCallback
  12792 + this.oncloseCallback = oncloseCallback
  12793 + this.onmessageCallback = onmessageCallback
  12794 + this.createWs()
  12795 + }
  12796 +
  12797 + /**
  12798 + * @description
  12799 + */
  12800 + createWs() {
  12801 + this.ws = new WebSocket(this.url, this.protocols)
  12802 + this.onopen()
  12803 + this.onerror()
  12804 + this.onclose()
  12805 + this.onmessage()
  12806 + }
  12807 +
  12808 + /**
  12809 + * @description 建立连接时
  12810 + */
  12811 + onopen() {
  12812 + this.ws.onopen = () => {
  12813 + this.errorStack.forEach(message => {
  12814 + this.send(message)
  12815 + })
  12816 + this.errorStack = []
  12817 + this.isReconnectionLoading = false
  12818 + if (typeof this.onopenCallback === 'function') this.onopenCallback(this)
  12819 + }
  12820 + }
  12821 +
  12822 + /**
  12823 + * @description 连接发生错误
  12824 + */
  12825 + onerror() {
  12826 + this.ws.onerror = (err) => {
  12827 + this.reconnection()
  12828 + this.isReconnectionLoading = false
  12829 + if (typeof this.onerrorCallback === 'function') this.onerrorCallback(this)
  12830 + }
  12831 + }
  12832 +
  12833 + /**
  12834 + * @description 连接断开时
  12835 + */
  12836 + onclose() {
  12837 + this.ws.onclose = () => {
  12838 + if (this.isCustomClose) return
  12839 +
  12840 + this.reconnection()
  12841 + this.isReconnectionLoading = false
  12842 +
  12843 + if (typeof this.oncloseCallback === 'function') this.oncloseCallback(this)
  12844 + }
  12845 + }
  12846 +
  12847 + /**
  12848 + * @description 接受消息
  12849 + */
  12850 + onmessage() {
  12851 + this.ws.onmessage = (event) => {
  12852 + try {
  12853 + const data = JSON.parse(event.data)
  12854 + if (typeof this.onmessageCallback === 'function') this.onmessageCallback(data, event, this)
  12855 +
  12856 + } catch (error) {
  12857 + // throw new Error(error)
  12858 + console.error(error)
  12859 + }
  12860 + }
  12861 + }
  12862 +
  12863 + /**
  12864 + * @description 断开重连
  12865 + */
  12866 + reconnection() {
  12867 + if (this.isReconnectionLoading) return
  12868 +
  12869 + this.isReconnectionLoading = true
  12870 + clearTimeout(this.timeId)
  12871 + this.timeId = setTimeout(() => {
  12872 + this.createWs()
  12873 + }, 60000)
  12874 + }
  12875 +
  12876 + /**
  12877 + * @description 发送消息
  12878 + */
  12879 + send(message) {
  12880 + if (!this.ws || this.ws.readyState !== 1) {
  12881 + this.errorStack.push(message)
  12882 + return
  12883 + }
  12884 +
  12885 + this.ws.send(message)
  12886 + }
  12887 +
  12888 + /**
  12889 + * @description 手动关闭
  12890 + */
  12891 + close() {
  12892 + this.isCustomClose = true
  12893 + this.ws.close()
  12894 + }
  12895 +
  12896 + /**
  12897 + * @description 手动开启
  12898 + */
  12899 + start() {
  12900 + this.isCustomClose = false
  12901 + this.reconnection()
  12902 + }
  12903 +
  12904 + /**
  12905 + * @description 销毁
  12906 + */
  12907 + destroy() {
  12908 + this.close()
  12909 + this.ws = null
  12910 + this.errorStack = null
  12911 + Ws.instance = null
  12912 + }
  12913 +
  12914 + /**
  12915 + * @description 单例模式
  12916 + * @param {string} option.url - 连接路径
  12917 + * @param {string} [option.protocols] option.protocols - 协议
  12918 + * @param {Function} [option.onopenCallback] option.onopenCallback - socket open时 拓展
  12919 + * @param {Function} [option.onerrorCallback] option.onerrorCallback - socket error时 拓展
  12920 + * @param {Function} [option.oncloseCallback] option.oncloseCallback - socket close时 拓展
  12921 + * @param {Function} [option.onmessageCallback] option.onmessageCallback - socket message时 拓展
  12922 + * @return {Ws} instance
  12923 + */
  12924 + static getInstance(option) {
  12925 + if (!this.instance) {
  12926 + this.instance = new Ws(option)
  12927 + }
  12928 + return this.instance
  12929 + }
  12930 +}
  12931 +
  12932 +/**
  12933 + * @description 切换页面时触发 /src/main/webapp/js/diagramly/Pages.js row: 461
  12934 + * @param editorUi
  12935 + * @param currentPage
  12936 + */
  12937 +function previewAction(editorUi, currentPage) {
  12938 + if (!editorUi.editor.graph.isLightboxView()) return
  12939 + DispatchCenter.getInstance(editorUi, currentPage)
  12940 +
  12941 + /**
  12942 + * @description 编辑模式下editor对象的editable为true
  12943 + * @param editorUi
  12944 + * @return {boolean}
  12945 + */
  12946 + function isPreviewMode(editorUi = {}) {
  12947 + const { editor: { editable } = {} } = editorUi
  12948 + return !!editable
  12949 + }
  12950 +}
  12951 +
  12952 +
  12953 +/**
  12954 + * @description 调度中心
  12955 + */
  12956 +class DispatchCenter {
  12957 +
  12958 + /**
  12959 + * @description 节点映射
  12960 + * @type {Map<string, Map<string, object>>}
  12961 + */
  12962 + nodeMapping
  12963 +
  12964 + /**
  12965 + * @description cmd ID 与 node 映射关系
  12966 + * @type {Map<number, string>}
  12967 + */
  12968 + cmdIdMapping = new Map()
  12969 +
  12970 + /**
  12971 + * @description
  12972 + */
  12973 + editorUi
  12974 +
  12975 + /**
  12976 + * @description 当前页信息
  12977 + */
  12978 + currentPage
  12979 +
  12980 + /**
  12981 + * @description 当前页中的所有cell
  12982 + */
  12983 + contentAllCell
  12984 +
  12985 + /**
  12986 + * @description 图源信息
  12987 + */
  12988 + graph
  12989 +
  12990 + /**
  12991 + * @description socket instance
  12992 + * @type {Ws}
  12993 + */
  12994 + socket
  12995 +
  12996 + /**
  12997 + * @description 事件总线
  12998 + * @type {EventCenter}
  12999 + */
  13000 + eventBus
  13001 +
  13002 + /**
  13003 + * @description 页面数据
  13004 + * @type {{dataSources: [], act: [], event: []}}
  13005 + */
  13006 + contentData
  13007 +
  13008 + /**
  13009 + * @description 处理数据源实例
  13010 + * @type {HandleDataSource}
  13011 + */
  13012 + dataSourceHandlerInstance
  13013 +
  13014 + /**
  13015 + * @description 处理数据动效实例
  13016 + * @type {HandleDynamicEffect}
  13017 + */
  13018 + dynamicEffectInstance
  13019 +
  13020 + /**
  13021 + * @description 处理数据交互实例
  13022 + * @type {HandleDataInteraction}
  13023 + */
  13024 + dataInteractionInstance
  13025 +
  13026 + /**
  13027 + * @description 更新队列
  13028 + * @type {UpdateQueue}
  13029 + */
  13030 + updateQueueInstance
  13031 +
  13032 + /**
  13033 + * @description cmd id
  13034 + */
  13035 + cmdIdPrimaryKey = 0
  13036 +
  13037 + /**
  13038 + * @description 当前实例
  13039 + * @type {DispatchCenter}
  13040 + */
  13041 + static instance
  13042 +
  13043 + /**
  13044 + * @description 原页面ID 原页面content id
  13045 + */
  13046 + static rawContentId
  13047 +
  13048 + /**
  13049 + * @description 判断事件监听器时候已经绑定过
  13050 + */
  13051 + static eventListenerIsExist = false
  13052 +
  13053 + static enumEventType = {
  13054 + DOWN: 'DOWN',
  13055 + UP: 'UP',
  13056 + SINGLE: 'SINGLE',
  13057 + DOUBLE: 'DOUBLE',
  13058 + }
  13059 +
  13060 + static enumDynamicEffectType = {
  13061 + DISPLAY: 'DISPLAY',
  13062 + FLASH: 'FLASH',
  13063 + ROTATE: 'ROTATE',
  13064 + SWITCH: 'SWITCH',
  13065 + }
  13066 +
  13067 + static enumPageType = {
  13068 + PAGE: 'PAGE',
  13069 + LINK: 'LINK',
  13070 + PROPS: 'PROPS',
  13071 + PARAMS_SETTING: 'PARAMS_SETTING'
  13072 + }
  13073 +
  13074 +
  13075 + constructor(editorUi, currentPage) {
  13076 + this.nodeMapping = new Map()
  13077 + this.editorUi = editorUi
  13078 + this.init(editorUi, currentPage)
  13079 + }
  13080 +
  13081 + /**
  13082 + * @description 初始化实例
  13083 + * @param editorUi
  13084 + * @param currentPage
  13085 + */
  13086 + async init(editorUi, currentPage) {
  13087 + this.createEventBus()
  13088 + this.saveContentInfo(editorUi, currentPage)
  13089 + this.connectSocket()
  13090 + await this.getContentDataNode()
  13091 + this.dataSourceHandlerInstance = new HandleDataSource(this)
  13092 + this.dataInteractionInstance = new HandleDataInteraction(this)
  13093 + this.dynamicEffectInstance = new HandleDynamicEffect(this)
  13094 + this.updateQueueInstance = new UpdateQueue(this)
  13095 + }
  13096 +
  13097 + /**
  13098 + * @description 建立socket连接
  13099 + */
  13100 + connectSocket() {
  13101 + Ws.instance?.destroy?.()
  13102 + this.socket = Ws.getInstance({ url: GLOBAL_WS_URL(), onmessageCallback: this.socketOnmessage })
  13103 + }
  13104 +
  13105 + /**
  13106 + * @description 处理webSocket消息
  13107 + * @param message
  13108 + * @param event
  13109 + * @param ws
  13110 + */
  13111 + socketOnmessage(message, event, ws) {
  13112 + const { subscriptionId, data } = message
  13113 + DispatchCenter.instance.publishEvent(subscriptionId, data, message, event, ws)
  13114 + }
  13115 +
  13116 + /**
  13117 + * @description 创建事件中心
  13118 + */
  13119 + createEventBus() {
  13120 + this.eventBus = new EventCenter()
  13121 + }
  13122 +
  13123 + /**
  13124 + * @description 保存部分页面信息到实例上
  13125 + * @param editorUi
  13126 + * @param currentPage
  13127 + */
  13128 + saveContentInfo(editorUi, currentPage) {
  13129 + const editor = editorUi.editor
  13130 + this.graph = editor.graph
  13131 + this.currentPage = currentPage
  13132 + this.contentAllCell = this.graph.getDefaultParent().children || []
  13133 + }
  13134 +
  13135 + /**
  13136 + * @description 获取页面绑定的节点信息
  13137 + */
  13138 + async getContentDataNode() {
  13139 + const { node: { id } = {} } = this.currentPage
  13140 + if (!id) return
  13141 + const [err, res] = await to(ConfigurationNodeApi.getConfigurationInfo('CONTENT', id))
  13142 + this.contentData = res
  13143 + }
  13144 +
  13145 + /**
  13146 + * @description 发送消息去获取实时数据
  13147 + */
  13148 + sendMessageToGetRealTimeData(message) {
  13149 + if (typeof message !== 'string') message = JSON.stringify(message)
  13150 + this.socket.send(message)
  13151 + }
  13152 +
  13153 + /**
  13154 + * @description 通过传入NodeId获取cmdId
  13155 + * @param nodeId
  13156 + * @return {number}
  13157 + */
  13158 + getCmdId(nodeId) {
  13159 + const cmdId = this.cmdIdPrimaryKey++
  13160 + this.cmdIdMapping.set(cmdId, nodeId)
  13161 + return cmdId
  13162 + }
  13163 +
  13164 + /**
  13165 + * @description 生成节点映射表
  13166 + * @param dataSources
  13167 + * @return {{cmdId: number, entityType: string, keys: *, scope: string, entityId: *}[]}
  13168 + */
  13169 + generatorDataSourceMapping(dataSources = []) {
  13170 + return dataSources.map((datum) => {
  13171 + const { deviceId, attr, nodeId, slaveDeviceId } = datum
  13172 + const cmdId = this.getCmdId(nodeId)
  13173 + const sendMsgTemplate = {
  13174 + entityType: "DEVICE",
  13175 + entityId: slaveDeviceId ? slaveDeviceId : deviceId,
  13176 + scope: "LATEST_TELEMETRY",
  13177 + cmdId,
  13178 + keys: attr,
  13179 + }
  13180 + this.subscribeDataSources(datum, cmdId, attr)
  13181 + return sendMsgTemplate
  13182 + })
  13183 + }
  13184 +
  13185 + /**
  13186 + * @description 分发事件
  13187 + */
  13188 + publishEvent(eventName, data, message, event, ws) {
  13189 + // data = data ? data : {}
  13190 + this.eventBus.emit(eventName, message, event, ws)
  13191 + // console.log(arguments)
  13192 + // Object.keys(data).forEach(() => {
  13193 + // this.eventBus.emit(eventName, message, event, ws)
  13194 + // })
  13195 + }
  13196 +
  13197 + /**
  13198 + * @description 订阅数据源
  13199 + * @param datum
  13200 + * @param eventName
  13201 + * @param key
  13202 + */
  13203 + subscribeDataSources(datum, eventName, key) {
  13204 + const node = this.contentAllCell.find(item => item.id === datum.nodeId)
  13205 + this.eventBus.on(eventName, (message) => {
  13206 + node && this.updatePage(() => {
  13207 + const { data } = message
  13208 + const [[timespan, value]] = data[key]
  13209 + node.setValue(value)
  13210 + }, node)
  13211 + })
  13212 + }
  13213 +
  13214 + /**
  13215 + * @description 更新页面
  13216 + * @param callback
  13217 + * @param cell
  13218 + */
  13219 + updatePage(callback, cell) {
  13220 + this.graph.getModel().beginUpdate()
  13221 + try {
  13222 + callback()
  13223 + this.graph.refresh(cell);
  13224 + } finally {
  13225 + this.graph.getModel().endUpdate()
  13226 + }
  13227 + }
  13228 +
  13229 +
  13230 + /**
  13231 + * @description 返回 DispatchCenter 实例 当contentID变化时重新创建实例
  13232 + * @param editorUi
  13233 + * @param currentPage
  13234 + * @return {*}
  13235 + */
  13236 + static getInstance(editorUi, currentPage = {}) {
  13237 + const { node: { id } = {} } = currentPage
  13238 + if (!id) return
  13239 + if (!DispatchCenter.instance || DispatchCenter.rawContentId !== id) {
  13240 + DispatchCenter.instance?.dynamicEffectInstance?.cleanSetInterval?.()
  13241 + DispatchCenter.instance?.updateQueueInstance?.cleanAllUpdateQueue?.()
  13242 + DispatchCenter.instance = new DispatchCenter(editorUi, currentPage)
  13243 + DispatchCenter.rawContentId = id
  13244 + }
  13245 +
  13246 + return DispatchCenter.instance
  13247 + }
  13248 +
  13249 +}
  13250 +
  13251 +class HandleDataSource {
  13252 +
  13253 + static enumConst = {
  13254 +
  13255 + /**
  13256 + * @description 聚合方式
  13257 + */
  13258 + AGG: 'agg',
  13259 +
  13260 + /**
  13261 + * @description 间隔方式
  13262 + */
  13263 + INTERVAL: 'interval',
  13264 +
  13265 + /**
  13266 + * @description 单位
  13267 + */
  13268 + UNIT: 'unit',
  13269 +
  13270 + /**
  13271 + * @description 开始时间
  13272 + */
  13273 + STARTTs: 'startTs',
  13274 +
  13275 + /**
  13276 + * @description 结束时间
  13277 + */
  13278 + ENDTs: 'endTs',
  13279 +
  13280 + /**
  13281 + * @description 范围
  13282 + */
  13283 + EFFECT_SCOPE: 'effectScope',
  13284 +
  13285 + /**
  13286 + * @description 绑定数据类型
  13287 + */
  13288 + DATA_TYPE: 'dataType'
  13289 + }
  13290 +
  13291 +
  13292 + static enumDataBindType = {
  13293 +
  13294 + /**
  13295 + * @description 历史
  13296 + */
  13297 + HISTORY: 'historyCmds',
  13298 +
  13299 + /**
  13300 + * @description 事实
  13301 + */
  13302 + REAL: 'tsSubCmds'
  13303 + }
  13304 +
  13305 + /**
  13306 + * @description 数据源节点映射
  13307 + * @type {Map<string, object>}
  13308 + */
  13309 + dataSourceNodeMapping = new Map()
  13310 +
  13311 + /**
  13312 + * @description 派发队列
  13313 + * @type {DispatchCenter}
  13314 + */
  13315 + DispatchInstance
  13316 +
  13317 + constructor(DispatchInstance) {
  13318 + this.DispatchInstance = DispatchInstance
  13319 + this.generatorCommonDataSourceMapping()
  13320 + this.generatorChartDataSourceMapping()
  13321 + }
  13322 +
  13323 + get graph() {
  13324 + return this.DispatchInstance.graph
  13325 + }
  13326 +
  13327 + /**
  13328 + * @description 普通数据源绑定列表
  13329 + */
  13330 + get commonDataSourceBindList() {
  13331 + return this.DispatchInstance.contentData?.dataSources?.filter(item => !item.additional && item.attr) || []
  13332 + }
  13333 +
  13334 + /**
  13335 + * @description 图表数据源绑定列表
  13336 + */
  13337 + get chartDataSourceBindList() {
  13338 + return this.DispatchInstance.contentData?.dataSources?.filter(item => item.additional) || []
  13339 + }
  13340 +
  13341 + get cmdIdMapping() {
  13342 + return this.DispatchInstance.cmdIdMapping
  13343 + }
  13344 +
  13345 + get basicAttr() {
  13346 + return Sidebar.prototype.enumCellBasicAttribute
  13347 + }
  13348 +
  13349 + get componentType() {
  13350 + return Sidebar.prototype.enumComponentType
  13351 + }
  13352 +
  13353 + get contentAllCell() {
  13354 + return this.graph.getDefaultParent().children || []
  13355 + }
  13356 +
  13357 + /**
  13358 + * @description 生成普通数据源绑定映射关系
  13359 + * @param dataSources
  13360 + * @return {{cmdId: number, entityType: string, keys: *, scope: string, entityId: *}[]}
  13361 + */
  13362 + generatorCommonDataSourceMapping() {
  13363 + const msg = this.commonDataSourceBindList.map((datum) => {
  13364 + const { deviceId, attr, nodeId, slaveDeviceId } = datum
  13365 + const cmdId = this.getCmdId(nodeId)
  13366 + const sendMsgTemplate = {
  13367 + entityType: "DEVICE",
  13368 + entityId: slaveDeviceId ? slaveDeviceId : deviceId,
  13369 + scope: "LATEST_TELEMETRY",
  13370 + cmdId,
  13371 + keys: attr,
  13372 + }
  13373 + this.dataSourceNodeMapping.set(nodeId, datum)
  13374 + this.subscribeEvent(cmdId, this.updateCommonDataSource.bind(this))
  13375 + return sendMsgTemplate
  13376 + })
  13377 + const { REAL } = HandleDataSource.enumDataBindType
  13378 + if (msg.length) this.sendMsg({ [REAL]: msg })
  13379 + }
  13380 +
  13381 +
  13382 + /**
  13383 + * @description 图表数据源绑定关系
  13384 + * @param {any[]} dataSource
  13385 + */
  13386 + generatorChartDataSourceMapping() {
  13387 + const realList = []
  13388 + const historyList = []
  13389 + const { HISTORY, REAL } = HandleDataSource.enumDataBindType
  13390 + const { STARTTs, ENDTs } = HandleDataSource.enumConst
  13391 + for (const item of this.chartDataSourceBindList) {
  13392 + const { additional = {}, deviceId, attr, nodeId, slaveDeviceId } = item
  13393 + if (!attr) continue
  13394 + const { agg, interval = 1000, dataType, effectScope = 0 } = additional
  13395 + const cmdId = this.getCmdId(nodeId)
  13396 + const template = {
  13397 + entityType: "DEVICE",
  13398 + entityId: slaveDeviceId ? slaveDeviceId : deviceId,
  13399 + cmdId,
  13400 + interval: Number(interval),
  13401 + agg,
  13402 + keys: attr,
  13403 + }
  13404 + let scope = isNaN(effectScope) ? 0 : Number(effectScope)
  13405 + if (dataType === HISTORY) {
  13406 + template[STARTTs] = Date.now() - scope
  13407 + template[ENDTs] = Date.now()
  13408 + historyList.push(template)
  13409 + this.subscribeEvent(cmdId, (message) => {
  13410 + this.updateHistoryDataSource(message, agg)
  13411 + })
  13412 + }
  13413 + else if (dataType === REAL) {
  13414 + template[STARTTs] = Date.now() - scope
  13415 + // template['timeWindow'] = interval
  13416 + realList.push(template)
  13417 + this.subscribeEvent(cmdId, (message) => {
  13418 + this.updateRealTimeDataSource(message, agg)
  13419 + })
  13420 + }
  13421 + this.dataSourceNodeMapping.set(nodeId, item)
  13422 + }
  13423 +
  13424 + if (historyList.length || realList.length) this.sendMsg({ [HISTORY]: historyList, [REAL]: realList })
  13425 + }
  13426 +
  13427 + /**
  13428 + * @description 订阅事件 绑定回调
  13429 + * @param eventName
  13430 + * @param callback
  13431 + */
  13432 + subscribeEvent(eventName, callback) {
  13433 + this.DispatchInstance.eventBus.on(eventName, callback)
  13434 + }
  13435 +
  13436 + /**
  13437 + * @description 更新变量值
  13438 + * @param {} message
  13439 + */
  13440 + updateCommonDataSource(message) {
  13441 + const { subscriptionId } = message
  13442 + const node = this.getNodeByCmdId(subscriptionId)
  13443 + const { attr } = this.getBindData(subscriptionId)
  13444 + node && this.updatePage(() => {
  13445 + const { data } = message
  13446 + const type = this.getComponentType(node)
  13447 + if (type === this.componentType.SWITCH) {
  13448 + this.handleSwitchComponent(message)
  13449 + return
  13450 + }
  13451 +
  13452 + if (type === this.componentType.PARAMS_SETTING_BUTTON) {
  13453 + this.handleParamSettingButton(message)
  13454 + return
  13455 + }
  13456 +
  13457 + if (type === this.componentType.IMAGE) {
  13458 + this.handleImageComponent(message)
  13459 + return
  13460 + }
  13461 +
  13462 + if (!data) return
  13463 + const [[timespan, value]] = data[attr]
  13464 + node.setValue(value)
  13465 + }, node)
  13466 + }
  13467 +
  13468 + /**
  13469 + * @description 处理switch 组件
  13470 + * @param {} message
  13471 + */
  13472 + handleSwitchComponent(message) {
  13473 + const { subscriptionId, data = {} } = message
  13474 + const node = this.getNodeByCmdId(subscriptionId)
  13475 + const { nodeId, attr } = this.getBindData(subscriptionId)
  13476 + const [[timespan, receiveValue] = []] = data[attr] || []
  13477 + const switchConfig = this.DispatchInstance.contentData.act.find(item => item.id === nodeId && item.type === 'SWITCH')
  13478 + const { condition = [] } = switchConfig || {}
  13479 + let reg = /image=[^;]+/g
  13480 + let flag = false
  13481 + const { SWITCH_STATE, SWITCH_VALUE, SWITCH_SEND_VALUE } = Sidebar.prototype.enumComponentType
  13482 + const { SWITCH_STATE_NONE } = Sidebar.prototype.enumComponentTypeValue
  13483 +
  13484 + const getSendValue = (type) => {
  13485 + return (condition.find(item => item.type !== type) || {}).value
  13486 + }
  13487 + for (const item of condition) {
  13488 + const { value, imagePath, type } = item || {}
  13489 + if (Number(receiveValue) === Number(value)) {
  13490 + flag = true
  13491 + this.updatePage(() => {
  13492 + const style = node.getStyle()
  13493 + const sendValue = getSendValue(type)
  13494 + node.setStyle(style.replace(reg, `image=${imagePath}`))
  13495 + node.setAttribute('label', '')
  13496 + node.setAttribute(SWITCH_VALUE, receiveValue)
  13497 + node.setAttribute(SWITCH_SEND_VALUE, sendValue)
  13498 + node.setAttribute(SWITCH_STATE, type)
  13499 + }, node)
  13500 + break
  13501 + }
  13502 + }
  13503 + if (!flag) {
  13504 + this.updatePage(() => {
  13505 + const style = node.getStyle()
  13506 + node.setStyle(style.replace(reg, `image=images/thingskit/not-standard-value.svg`))
  13507 + node.setAttribute('label', receiveValue)
  13508 + node.setAttribute(SWITCH_VALUE, receiveValue)
  13509 + node.setAttribute(SWITCH_STATE, SWITCH_STATE_NONE)
  13510 + }, node)
  13511 + }
  13512 +
  13513 + }
  13514 +
  13515 + handleParamSettingButton(message) {
  13516 + const { subscriptionId, data = {} } = message
  13517 + if (!data) return
  13518 + const node = this.getNodeByCmdId(subscriptionId)
  13519 + const { attr } = this.getBindData(subscriptionId)
  13520 + const [[timespan, receiveValue] = []] = data[attr] || []
  13521 + this.updatePage(() => {
  13522 + node.setAttribute('label', `<button class="param-setting-button">${receiveValue}</button>`)
  13523 + }, node)
  13524 + }
  13525 +
  13526 + handleImageComponent(message) {
  13527 + const { subscriptionId, data = {} } = message
  13528 + const node = this.getNodeByCmdId(subscriptionId)
  13529 + const { attr } = this.getBindData(subscriptionId)
  13530 + const [[timespan, receiveValue] = []] = data[attr] || []
  13531 + this.updatePage(() => {
  13532 + node.setAttribute('label', `<img class="basic-component__image" alt="图片" src="${receiveValue}" />`)
  13533 + }, node)
  13534 + }
  13535 +
  13536 + /**
  13537 + * @description 更新实时数据
  13538 + * @param {} message
  13539 + * @param {} agg 聚合方式
  13540 + */
  13541 + updateRealTimeDataSource(message, agg) {
  13542 + const { data = {}, subscriptionId } = message
  13543 + const node = this.getNodeByCmdId(subscriptionId)
  13544 + if (!node) return
  13545 + const enumConst = Sidebar.prototype.enumCellBasicAttribute
  13546 + const chartInstanceMap = Sidebar.prototype.chartsInstanceMapping
  13547 + const chartInstanceId = node.getAttribute(enumConst.CHART_INSTANCE_ID)
  13548 + const chartInstanceType = node.getAttribute(enumConst.COMPONENT_TYPE)
  13549 + const instance = chartInstanceMap.get(chartInstanceId)
  13550 + const { attr = [[]] } = this.getBindData(subscriptionId)
  13551 + const realDataList = data[attr] || []
  13552 +
  13553 + const action = agg === 'NONE' ? 'unshift' : 'push'
  13554 +
  13555 + // chart insstance 是否已经接受过一次消息推送
  13556 + const isActive = instance.isActive
  13557 + if (!isActive) {
  13558 + instance.isActive = true
  13559 + const chartOption = this.getChartComponentOption(chartInstanceType, { chartType: chartInstanceType, attr, dataList: realDataList, action, nodeId: node.id })
  13560 + instance.setOption(chartOption)
  13561 +
  13562 + } else {
  13563 + const oldOptions = instance.getOption()
  13564 + const options = this.getRealTimeUpdateChartOption(chartInstanceType, { oldOptions, dataList: realDataList })
  13565 + if (!instance) clearInterval(interval)
  13566 + instance && instance.setOption(options)
  13567 + }
  13568 + }
  13569 +
  13570 + /**
  13571 + * @description 更新历史数据
  13572 + */
  13573 + updateHistoryDataSource(message, agg) {
  13574 + const { data = {}, subscriptionId } = message
  13575 + const node = this.getNodeByCmdId(subscriptionId)
  13576 + if (!node) return
  13577 + const enumConst = Sidebar.prototype.enumCellBasicAttribute
  13578 + const chartInstanceMap = Sidebar.prototype.chartsInstanceMapping
  13579 + const chartInstanceId = node.getAttribute(enumConst.CHART_INSTANCE_ID)
  13580 + const chartInstanceType = node.getAttribute(enumConst.COMPONENT_TYPE)
  13581 + const instance = chartInstanceMap.get(chartInstanceId)
  13582 + const { attr = [[]] } = this.getBindData(subscriptionId)
  13583 + const historyDataList = data[attr] || []
  13584 + const showNumberOf = 4
  13585 + const action = agg === 'NONE' ? 'unshift' : 'push'
  13586 +
  13587 + const chartOption = this.getChartComponentOption(chartInstanceType, { dataList: historyDataList, attr, chartType: chartInstanceType, action })
  13588 +
  13589 + let interval
  13590 + // TODO 清除定时器
  13591 + function autoMove() {
  13592 + if (seriesValue.length <= 5) return
  13593 + interval = setInterval(() => {
  13594 + if (Number(chartOption.dataZoom[0].endValue) === seriesValue.length - 1) {
  13595 + chartOption.dataZoom[0].endValue = showNumberOf
  13596 + chartOption.dataZoom[0].startValue = 0
  13597 + } else {
  13598 + chartOption.dataZoom[0].endValue = chartOption.dataZoom[0].endValue + 1
  13599 + chartOption.dataZoom[0].startValue = chartOption.dataZoom[0].startValue + 1
  13600 + }
  13601 + if (!chartInstanceMap.has(chartInstanceId)) {
  13602 + clearInterval(interval)
  13603 + return
  13604 + }
  13605 + instance && instance.setOption(chartOption)
  13606 +
  13607 + }, 2000);
  13608 + }
  13609 +
  13610 + function stop() {
  13611 + clearInterval(interval)
  13612 + }
  13613 +
  13614 + function goMove() {
  13615 + autoMove()
  13616 + }
  13617 +
  13618 + instance.setOption(chartOption)
  13619 + // instance.on('mouseover', stop)
  13620 + // instance.on('mouseout', goMove)
  13621 + // autoMove()
  13622 + }
  13623 +
  13624 + getChartComponentOption(chartInstanceType, params) {
  13625 + const { LINE_CHART, BAR_CHART, DASHBOARD_CHART } = Sidebar.prototype.enumComponentType
  13626 + switch (chartInstanceType) {
  13627 + case LINE_CHART:
  13628 + return this.getBasicChartOption(Object.assign(params, { chartType: 'line' }))
  13629 + case BAR_CHART:
  13630 + return this.getBasicChartOption(Object.assign(params, { chartType: 'bar' }))
  13631 + case DASHBOARD_CHART:
  13632 + return this.getDashboardChartOption(params)
  13633 + default:
  13634 + return {}
  13635 + }
  13636 + }
  13637 +
  13638 + getRealTimeUpdateChartOption(chartInstanceType, params) {
  13639 + const { LINE_CHART, BAR_CHART, DASHBOARD_CHART } = Sidebar.prototype.enumComponentType
  13640 + switch (chartInstanceType) {
  13641 + case LINE_CHART:
  13642 + return this.getRealTimeUpdateBasicChartOption(params)
  13643 + case BAR_CHART:
  13644 + return this.getRealTimeUpdateBasicChartOption(params)
  13645 + case DASHBOARD_CHART:
  13646 + return this.getRealTimeUpdateDashboardChartOption(params)
  13647 + default:
  13648 + return {}
  13649 + }
  13650 + }
  13651 +
  13652 + /**
  13653 + *
  13654 + * @param {@} params
  13655 + * @returns
  13656 + */
  13657 + getBasicChartOption(params = { dataList: [], attr: '', chartType: 'bar', action }) {
  13658 + const { dataList = [], attr = '', chartType = 'bar', action } = params
  13659 +
  13660 + const xAxisData = []
  13661 + const seriesValue = []
  13662 +
  13663 + dataList.forEach(item => {
  13664 + const [timespan, value] = item
  13665 + xAxisData[action](new Date(Number(timespan)).toLocaleTimeString())
  13666 + seriesValue[action](Number(value))
  13667 + })
  13668 +
  13669 + return {
  13670 + title: {
  13671 + subtext: dataList.length ? '' : '暂无数据',
  13672 + x: 'center',
  13673 + y: 'center',
  13674 + itemGap: -20,
  13675 + subtextStyle: {
  13676 + fontSize: 16
  13677 + }
  13678 + },
  13679 + tooltip: {
  13680 + trigger: 'axis',
  13681 + axisPointer: {
  13682 + type: 'shadow'
  13683 + }
  13684 + },
  13685 + grid: {
  13686 + left: '3%',
  13687 + right: '3%',
  13688 + bottom: '3%',
  13689 + containLabel: true,
  13690 + },
  13691 + xAxis: {
  13692 + name: '时间',
  13693 + type: 'category',
  13694 + data: xAxisData,
  13695 + boundaryGap: true,
  13696 + axisLabel: {
  13697 + rotate: 70
  13698 + },
  13699 + axisPointer: {
  13700 + label: {
  13701 + formatter() {
  13702 + return attr
  13703 + }
  13704 + }
  13705 + }
  13706 + },
  13707 + yAxis: {
  13708 + type: 'value'
  13709 + },
  13710 + series: [
  13711 + {
  13712 + data: seriesValue,
  13713 + type: chartType,
  13714 + }
  13715 + ],
  13716 + dataZoom: [
  13717 + {
  13718 + show: true,
  13719 + type: 'inside'
  13720 + }
  13721 + ]
  13722 + }
  13723 + }
  13724 +
  13725 + getRealTimeUpdateBasicChartOption(params = { oldOptions: {}, dataList: [] }) {
  13726 + const { oldOptions, dataList } = params
  13727 + const xAxisData = oldOptions.xAxis[0].data || []
  13728 + const seriesValue = oldOptions.series[0].data || []
  13729 + const oldEndValue = Number(oldOptions.dataZoom[0].endValue) || 0
  13730 + const oldStartValue = Number(oldOptions.dataZoom[0].startValue) || 0
  13731 + const showNumberOf = 4
  13732 +
  13733 + for (let i = dataList.length - 1; i >= 0; i--) {
  13734 + const [timespan, value] = dataList[i]
  13735 + xAxisData.push(new Date(Number(timespan)).toLocaleTimeString())
  13736 + seriesValue.push(Number(value))
  13737 + }
  13738 +
  13739 + if (Number(oldOptions.dataZoom[0].endValue) === seriesValue.length - 1) {
  13740 + oldOptions.dataZoom[0].endValue = showNumberOf
  13741 + oldOptions.dataZoom[0].startValue = 0
  13742 + } else {
  13743 + oldOptions.dataZoom[0].endValue = oldOptions.dataZoom[0].endValue + 1
  13744 + oldOptions.dataZoom[0].startValue = oldOptions.dataZoom[0].startValue + 1
  13745 + }
  13746 +
  13747 + return {
  13748 + title: {
  13749 + subtext: ''
  13750 + },
  13751 + xAxis: {
  13752 + data: xAxisData
  13753 + },
  13754 + series: [
  13755 + {
  13756 + data: seriesValue
  13757 + }
  13758 + ],
  13759 + dataZoom: [
  13760 + {
  13761 + show: true,
  13762 + type: 'inside',
  13763 + }
  13764 + ]
  13765 + }
  13766 + }
  13767 +
  13768 + getDashboardChartOption(params = { dataList: [], nodeId }) {
  13769 + const { dataList = [], nodeId } = params
  13770 + const dataSource = this.DispatchInstance.contentData.dataSources.find(item => item.nodeId === nodeId) || {}
  13771 + const { additional: { unit = '°C' } = {} } = dataSource
  13772 +
  13773 + const [timespan, value] = dataList[0] || []
  13774 + return {
  13775 + series: [
  13776 + {
  13777 + type: 'gauge',
  13778 + center: ['50%', '60%'],
  13779 + radius: '100%',
  13780 + startAngle: 200,
  13781 + endAngle: -20,
  13782 + min: 0,
  13783 + max: 100,
  13784 + splitNumber: 10,
  13785 + itemStyle: {
  13786 + color: '#5479c6'
  13787 + // color: '#FFAB91'
  13788 + },
  13789 + progress: {
  13790 + show: true,
  13791 + width: 30
  13792 + },
  13793 + pointer: {
  13794 + show: false
  13795 + },
  13796 + axisLine: {
  13797 + lineStyle: {
  13798 + width: 30
  13799 + }
  13800 + },
  13801 + axisTick: {
  13802 + distance: 0,
  13803 + splitNumber: 5,
  13804 + lineStyle: {
  13805 + width: 2,
  13806 + color: '#999'
  13807 + }
  13808 + },
  13809 + splitLine: {
  13810 + distance: 0,
  13811 + length: 14,
  13812 + lineStyle: {
  13813 + width: 3,
  13814 + color: '#999'
  13815 + }
  13816 + },
  13817 + axisLabel: {
  13818 + distance: 35,
  13819 + color: '#999',
  13820 + fontSize: 20
  13821 + },
  13822 + anchor: {
  13823 + show: false
  13824 + },
  13825 + title: {
  13826 + show: false
  13827 + },
  13828 + detail: {
  13829 + valueAnimation: true,
  13830 + width: '60%',
  13831 + lineHeight: 40,
  13832 + borderRadius: 8,
  13833 + offsetCenter: [0, '-15%'],
  13834 + fontSize: 25,
  13835 + fontWeight: 'bolder',
  13836 + formatter: `{value} ${unit}`,
  13837 + color: 'auto'
  13838 + },
  13839 + data: [
  13840 + {
  13841 + value
  13842 + }
  13843 + ]
  13844 + },
  13845 + ]
  13846 + }
  13847 + }
  13848 +
  13849 + /**
  13850 + * @description 获取仪表盘配置
  13851 + */
  13852 + getRealTimeUpdateDashboardChartOption(params = { dataList: [] }) {
  13853 + const { dataList = [], oldOptions } = params
  13854 + const [timespan, value] = dataList[0] || []
  13855 + console.log(value)
  13856 + return {
  13857 + series: [
  13858 + {
  13859 + data: [
  13860 + {
  13861 + value
  13862 + }
  13863 + ]
  13864 + },
  13865 + ]
  13866 + }
  13867 + }
  13868 +
  13869 + /**
  13870 + * @description 获取绑定的数据
  13871 + * @param subscriptionId
  13872 + * @param actionType
  13873 + * @return {*}
  13874 + */
  13875 + getBindData(subscriptionId) {
  13876 + const nodeId = this.getNodeIdByCmdId(subscriptionId)
  13877 + const temp = this.dataSourceNodeMapping.get(nodeId) || {}
  13878 + return temp
  13879 + }
  13880 +
  13881 + /**
  13882 + * @description 获取cmdId
  13883 + * @param {string} nodeId
  13884 + * @returns
  13885 + */
  13886 + getCmdId(nodeId) {
  13887 + return this.DispatchInstance.getCmdId(nodeId)
  13888 + }
  13889 +
  13890 + /**
  13891 + * @description 通过cmdID获取节点id
  13892 + * @param subscriptionId
  13893 + * @return {string}
  13894 + */
  13895 + getNodeIdByCmdId(subscriptionId) {
  13896 + return this.DispatchInstance.cmdIdMapping.get(subscriptionId)
  13897 + }
  13898 +
  13899 + /**
  13900 + * @description 通过cmdID 获取节点
  13901 + * @param subscriptionId
  13902 + * @return {*}
  13903 + */
  13904 + getNodeByCmdId(subscriptionId) {
  13905 + const nodeId = this.getNodeIdByCmdId(subscriptionId)
  13906 + return this.contentAllCell.find(item => item.id === nodeId)
  13907 + }
  13908 +
  13909 + getComponentType(node) {
  13910 + return node.getAttribute(this.basicAttr.COMPONENT_TYPE)
  13911 + }
  13912 +
  13913 + /**
  13914 + * @description 发送socket 消息
  13915 + * @param {any} msg
  13916 + * @returns
  13917 + */
  13918 + sendMsg(msg) {
  13919 + return this.DispatchInstance.sendMessageToGetRealTimeData(msg)
  13920 + }
  13921 +
  13922 + updatePage(callback, cell) {
  13923 + return this.DispatchInstance.updatePage(callback, cell)
  13924 + }
  13925 +}
  13926 +
  13927 +class HandleDataInteraction {
  13928 + /**
  13929 + * @description 事件分发中心实例
  13930 + * @type {DispatchCenter}
  13931 + */
  13932 + DispatchInstance
  13933 +
  13934 + /**
  13935 + * @description 事件映射
  13936 + * @type {Map<>}
  13937 + */
  13938 + static eventMapping = new Map()
  13939 +
  13940 + constructor(DispatchInstance) {
  13941 + this.DispatchInstance = DispatchInstance
  13942 + HandleDataInteraction.eventMapping = new Map()
  13943 + this.init()
  13944 + }
  13945 +
  13946 + init() {
  13947 + this.generatorEventMapping()
  13948 + this.createGraphEventListener()
  13949 + }
  13950 +
  13951 + get eventMapping() {
  13952 + return HandleDataInteraction.eventMapping
  13953 + }
  13954 +
  13955 + /**
  13956 + * @description DispatchCenter 实例中保存的 graph 对象
  13957 + * @return {*}
  13958 + */
  13959 + get graph() {
  13960 + return this.DispatchInstance.graph
  13961 + }
  13962 +
  13963 + /**
  13964 + * @description DispatchCenter 实例中保存的 editorUi 对象
  13965 + * @return {*}
  13966 + */
  13967 + get editorUi() {
  13968 + return this.DispatchInstance.editorUi
  13969 + }
  13970 +
  13971 + /**
  13972 + * @description DispatchCenter 实例中保存的 contentAllCell 对象
  13973 + * @return {*}
  13974 + */
  13975 + get eventList() {
  13976 + const { event = [] } = this.DispatchInstance.contentData
  13977 + return event
  13978 + }
  13979 +
  13980 + /**
  13981 + * @description 获取页面数据
  13982 + */
  13983 + get contentData() {
  13984 + return DispatchCenter.instance.contentData
  13985 + }
  13986 +
  13987 + get contentAllCell() {
  13988 + return this.graph.getDefaultParent().children || []
  13989 + }
  13990 +
  13991 + /**
  13992 + * @description 事件映射
  13993 + */
  13994 + generatorEventMapping() {
  13995 + const event = this.eventList
  13996 + for (const item of event) {
  13997 + const { content, id: nodeId, enabled, type } = item
  13998 + if (!enabled) continue
  13999 + if (!this.eventMapping.has(nodeId)) this.eventMapping.set(nodeId, new Map())
  14000 + const temp = this.eventMapping.get(nodeId)
  14001 + temp.set(type, content)
  14002 + }
  14003 + }
  14004 +
  14005 + createGraphEventListener() {
  14006 + if (DispatchCenter.eventListenerIsExist) return
  14007 +
  14008 + const graphDblClick = this.graph.dblClick;
  14009 + this.graph.dblClick = (...args) => {
  14010 + this.handleDoubleClickEvent(...args)
  14011 + graphDblClick.apply(this.graph, args)
  14012 + }
  14013 +
  14014 + const graphClick = this.graph.click;
  14015 + this.graph.click = (...args) => {
  14016 + this.handleClickEvent(...args)
  14017 + graphClick.apply(this.graph, args)
  14018 + }
  14019 +
  14020 + const mouseDownEvent = this.throttle(this.handleMouseDownEvent)
  14021 + const mouseUpEvent = this.throttle(this.handleMouseUpEvent)
  14022 + const graphFireMouseEvent = this.graph.fireMouseEvent;
  14023 + this.graph.fireMouseEvent = (eventName, event, sender) => {
  14024 + if (eventName === mxEvent.MOUSE_DOWN) {
  14025 + // this.handleMouseDownEvent(eventName, event, sender)
  14026 + mouseDownEvent(eventName, event, sender)
  14027 + }
  14028 + if (eventName === mxEvent.MOUSE_UP) {
  14029 + mouseUpEvent(eventName, event, sender)
  14030 + // this.handleMouseUpEvent(eventName, event, sender)
  14031 + }
  14032 + graphFireMouseEvent.apply(this.graph, [eventName, event, sender]);
  14033 + };
  14034 +
  14035 + DispatchCenter.eventListenerIsExist = true
  14036 + }
  14037 +
  14038 + /**
  14039 + * @description 处理按下事件
  14040 + * @param eventName
  14041 + * @param event
  14042 + * @param sender
  14043 + */
  14044 + handleMouseDownEvent(eventName, event, sender) {
  14045 + const { state } = event
  14046 + if (!state) return
  14047 + const { cell = {} } = state
  14048 + const { id } = cell || {}
  14049 + const temp = this.eventMapping.get(id)
  14050 + if (temp && temp.has(DispatchCenter.enumEventType.DOWN)) {
  14051 + const content = temp.get(DispatchCenter.enumEventType.DOWN)
  14052 + this.sendInstruction(content.data)
  14053 + }
  14054 + }
  14055 +
  14056 + /**
  14057 + * @description 处理抬起事件
  14058 + * @param eventName
  14059 + * @param event
  14060 + * @param sender
  14061 + */
  14062 + handleMouseUpEvent(eventName, event, sender) {
  14063 + const { state } = event
  14064 + if (!state) return
  14065 + const { cell = {} } = state
  14066 + const { id } = cell || {}
  14067 + const temp = this.eventMapping.get(id)
  14068 + if (temp && temp.has(DispatchCenter.enumEventType.UP)) {
  14069 + const content = temp.get(DispatchCenter.enumEventType.UP)
  14070 + this.sendInstruction(content.data)
  14071 + }
  14072 + }
  14073 +
  14074 + /**
  14075 + * @description 鼠标单击事件事件
  14076 + * @param event
  14077 + */
  14078 + handleClickEvent(event) {
  14079 + const { state } = event
  14080 + if (!state) return
  14081 + const { cell = {} } = state
  14082 + const { id } = cell || {}
  14083 + const temp = this.eventMapping.get(id)
  14084 + if (temp && temp.has(DispatchCenter.enumEventType.SINGLE)) {
  14085 + const content = temp.get(DispatchCenter.enumEventType.SINGLE)
  14086 + const { type, value } = content
  14087 + if (type === DispatchCenter.enumPageType.PAGE && value) {
  14088 + this.jumpPage(value)
  14089 + } else if (type === DispatchCenter.enumPageType.LINK && value) {
  14090 + window.open(value)
  14091 + } else if (type === DispatchCenter.enumPageType.PARAMS_SETTING) {
  14092 + this.paramsSetting(id, content)
  14093 + }
  14094 + }
  14095 + }
  14096 +
  14097 + /**
  14098 + * @description 鼠标双击事件
  14099 + * @param event
  14100 + * @param cell
  14101 + */
  14102 + handleDoubleClickEvent(event, cell = {}) {
  14103 + if (!cell) return
  14104 + const { id } = cell
  14105 + const temp = this.eventMapping.get(id)
  14106 + if (temp && temp.has(DispatchCenter.enumEventType.DOUBLE)) {
  14107 + const content = temp.get(DispatchCenter.enumEventType.DOUBLE)
  14108 + const { type, value } = content
  14109 + if (type === DispatchCenter.enumPageType.PAGE && value) {
  14110 + this.jumpPage(value)
  14111 + } else if (type === DispatchCenter.enumPageType.LINK && value) {
  14112 + window.open(value)
  14113 + }
  14114 + }
  14115 + }
  14116 +
  14117 +
  14118 + /**
  14119 + * @description 下发指令
  14120 + * @param list
  14121 + */
  14122 + sendInstruction(list = []) {
  14123 + const queue = []
  14124 + const fn = async (way, deviceId, data) => {
  14125 + const [err, res = []] = await to(ConfigurationNodeApi.deviceIsOnLine(deviceId))
  14126 + const { value } = res[0] || {}
  14127 + if (value) {
  14128 + await to(ConfigurationNodeApi.sendInstruction(way, deviceId, data))
  14129 + } else {
  14130 + UseLayUi.errorMsg('设备不在线!')
  14131 + }
  14132 + }
  14133 + for (const item of list) {
  14134 + const { deviceId, slaveDeviceId, value, way } = item
  14135 + if (!value || !deviceId) continue
  14136 + const data = {
  14137 + method: "methodThingskit",
  14138 + params: JSON.parse(value),
  14139 + }
  14140 + if (deviceId) {
  14141 + queue.push(() => {
  14142 + fn(way, deviceId, data)
  14143 + })
  14144 + }
  14145 + // if (slaveDeviceId) {
  14146 + // queue.push(() => {
  14147 + // fn(way, slaveDeviceId, data)
  14148 + // })
  14149 + // } else if (deviceId) {
  14150 + // queue.push(() => {
  14151 + // fn(way, deviceId, data)
  14152 + // })
  14153 + // }
  14154 + }
  14155 +
  14156 + Promise.all(queue.map(fn => fn()))
  14157 + }
  14158 +
  14159 + throttle(fn, time = 1000) {
  14160 + let now = Date.now
  14161 + let oldTime = now()
  14162 + return (...args) => {
  14163 + let newTime = now()
  14164 + if (newTime - oldTime > time) {
  14165 + oldTime = now()
  14166 + fn.apply(this, args)
  14167 + }
  14168 + }
  14169 + }
  14170 +
  14171 + /**
  14172 + * @description 跳转页面
  14173 + */
  14174 + jumpPage(page) {
  14175 + try {
  14176 + this.editorUi.handleCustomLink(`data:page/id,${page}`)
  14177 + } catch (error) {
  14178 + throw error
  14179 + }
  14180 + }
  14181 +
  14182 + /**
  14183 + * @description 参数设置
  14184 + */
  14185 + paramsSetting(nodeId, content) {
  14186 + const { layer, jquery: $, form } = layui
  14187 + const { SWITCH_STATE, SWITCH_VALUE, SWITCH_SEND_VALUE, SWITCH, PARAMS_SETTING_BUTTON } = Sidebar.prototype.enumComponentType
  14188 + const { SWITCH_STATE_NONE } = Sidebar.prototype.enumComponentTypeValue
  14189 + const { COMPONENT_TYPE } = Sidebar.prototype.enumCellBasicAttribute
  14190 + const contentData = this.contentData
  14191 + const currentNode = this.contentAllCell.find(item => item.id === nodeId)
  14192 +
  14193 + const enumConst = {
  14194 + VALUE: 'value',
  14195 + ISSUED_WAY: 'way',
  14196 + ONE_WAY: 'oneway',
  14197 + TWO_WAY: 'twoway',
  14198 + ATTR_PLACEHOLDER: 'attrPlaceholder'
  14199 + }
  14200 +
  14201 + const enumActionEl = {
  14202 + CONTAINER: 'container',
  14203 + EDITOR: 'editor',
  14204 + ISSUED_WAY_FILTER: 'wayFilter'
  14205 +
  14206 + }
  14207 +
  14208 + function createContent() {
  14209 + return `
  14210 + <div>
  14211 + <div class="layui-form" lay-filter="${enumActionEl.ISSUED_WAY_FILTER}">
  14212 + <div class="layui-form-item">
  14213 + <label class="layui-form-label">下发值</label>
  14214 + <div class="layui-input-block">
  14215 + <input type="text" name="${enumConst.VALUE}" lay-verify="required" autocomplete="off" placeholder="请输入下发值" class="layui-input">
  14216 + </div>
  14217 + </div>
  14218 + </div>
  14219 + </div>`
  14220 + }
  14221 +
  14222 + function jsonParse(value) {
  14223 + try {
  14224 + return JSON.parse(value)
  14225 + } catch (error) {
  14226 + return {}
  14227 + }
  14228 + }
  14229 +
  14230 + function handleSwitchComponent() {
  14231 + const state = currentNode.getAttribute(SWITCH_STATE)
  14232 + const value = currentNode.getAttribute(SWITCH_SEND_VALUE)
  14233 + if (state === SWITCH_STATE_NONE) {
  14234 + return
  14235 + }
  14236 + layer.confirm('是否确认下发命令?', async function (index) {
  14237 + defaultHandler(value, () => layer.close(index))
  14238 + });
  14239 +
  14240 +
  14241 + }
  14242 +
  14243 + function replaceAttrPlaceholder(oldValue = {}, replaceAttr = '', replaceValue = '', newValue = {},) {
  14244 + if (typeof oldValue !== 'object') return newValue
  14245 +
  14246 + for (const key of Object.keys(oldValue)) {
  14247 + if (key === enumConst.ATTR_PLACEHOLDER) {
  14248 + newValue[replaceAttr] = replaceValue
  14249 + continue
  14250 + }
  14251 + if (typeof oldValue[key] === 'object') {
  14252 + newValue[key] = replaceAttrPlaceholder(oldValue[key], replaceAttr, replaceValue)
  14253 + continue
  14254 + }
  14255 + newValue[key] = oldValue[key]
  14256 + }
  14257 +
  14258 + return newValue
  14259 + }
  14260 +
  14261 + const submitThrottle = this.throttle(submit)
  14262 + async function submit(callback) {
  14263 + const { value } = form.val(enumActionEl.ISSUED_WAY_FILTER)
  14264 + defaultHandler(value, callback)
  14265 + }
  14266 +
  14267 + async function defaultHandler(value, callback) {
  14268 + let { deviceId, attr } = contentData.dataSources.find(item => item.nodeId === nodeId) || {}
  14269 + let { command, way } = content
  14270 + const validate = new Validate([
  14271 + { value, required: true, message: '下发值是必填项' },
  14272 + { value: deviceId, required: true, message: '未绑定设备' },
  14273 + { value: way, required: true, message: '未绑定指令下发方式(单向/双向)' },
  14274 + { value: command, required: true, message: '未设置下发命令' },
  14275 + { value: attr, required: true, message: '未绑定设备属性' },
  14276 + ])
  14277 + if (!validate.begin()) return
  14278 + if (typeof command === 'string') command = jsonParse(command)
  14279 + const data = replaceAttrPlaceholder(command, attr, value)
  14280 + const instructionData = {
  14281 + method: "methodThingskit",
  14282 + params: data,
  14283 + }
  14284 +
  14285 + const [err, res = []] = await to(ConfigurationNodeApi.deviceIsOnLine(deviceId))
  14286 + const { value: onlineFlag } = res[0] || {}
  14287 + if (onlineFlag) {
  14288 + const [err, res] = await to(ConfigurationNodeApi.sendInstruction(way, deviceId, instructionData))
  14289 + if (!err) {
  14290 + UseLayUi.topSuccessMsg('操作成功')
  14291 + callback && typeof callback === 'function' && callback()
  14292 + }
  14293 + } else {
  14294 + UseLayUi.topErrorMsg('设备不在线!')
  14295 + }
  14296 + }
  14297 +
  14298 + function createLayer() {
  14299 +
  14300 + layer.open({
  14301 + title: '参数设置',
  14302 + content: createContent(),
  14303 + area: '400px',
  14304 + btn: ["应用", "取消"],
  14305 + yes(index) {
  14306 + submitThrottle(() => {
  14307 + layer.close(index)
  14308 + })
  14309 + },
  14310 + but2(index, layero) {
  14311 +
  14312 + },
  14313 + async success(layero, index) {
  14314 + $('.layui-layer-setwin a').removeAttr('href')
  14315 + form.render()
  14316 + },
  14317 + })
  14318 + }
  14319 +
  14320 +
  14321 + function startProcess() {
  14322 + const componentType = currentNode.getAttribute(COMPONENT_TYPE)
  14323 + const handle = {
  14324 + [SWITCH]: handleSwitchComponent,
  14325 + [PARAMS_SETTING_BUTTON]: createLayer
  14326 + }
  14327 +
  14328 + try {
  14329 + handle[componentType]()
  14330 + } catch (error) {
  14331 +
  14332 + }
  14333 + }
  14334 + startProcess()
  14335 + }
  14336 +}
  14337 +
  14338 +/**
  14339 + * @description 处理数据动效
  14340 + */
  14341 +class HandleDynamicEffect {
  14342 +
  14343 + /**
  14344 + * @description 事件分发中心实例
  14345 + * @type {DispatchCenter}
  14346 + */
  14347 + DispatchInstance
  14348 +
  14349 + /**
  14350 + * @description 开启的数据动效
  14351 + * @type {any[]}
  14352 + */
  14353 + enableActList
  14354 +
  14355 + /**
  14356 + * @description 视频记录列表
  14357 + */
  14358 + videoRecordList
  14359 +
  14360 + /**
  14361 + * @description 动效节点映射
  14362 + * @type {Map<string, {display: boolean, value: Map<string, any>}>}
  14363 + */
  14364 + actNodeMapping = new Map()
  14365 +
  14366 + /**
  14367 + * @description clear setInterval
  14368 + */
  14369 + cleanSetInterval
  14370 +
  14371 + static enumConst = {
  14372 + MAX: 'max',
  14373 + MIN: 'min',
  14374 + }
  14375 +
  14376 + static enumActType = {
  14377 + FLASH: 'FLASH',
  14378 + DISPLAY: 'DISPLAY',
  14379 + ROTATE: 'ROTATE',
  14380 + IMAGE: 'IMAGE',
  14381 + RUNNING: 'RUNNING'
  14382 + }
  14383 +
  14384 + static enumDisplayType = {
  14385 + SHOW: 'show',
  14386 + HIDDEN: 'hidden',
  14387 + }
  14388 +
  14389 + static enumRunningType = {
  14390 + RUN: 'run',
  14391 + STOP: 'stop'
  14392 + }
  14393 +
  14394 + static enumVideoConst = {
  14395 + ORG_ID: 'orgId',
  14396 + RECORD_ID: 'id',
  14397 + VIDEO_URL: 'videoUrl',
  14398 + ACCESSMODE: 'accessMode',
  14399 + VIDEO_FLAG: 'videoComponentFlag'
  14400 + }
  14401 +
  14402 + static enumVarImageConst = {
  14403 +
  14404 + /**
  14405 + * @description 最小值
  14406 + */
  14407 + MIN: 'min',
  14408 +
  14409 + /**
  14410 + * @description 最大值
  14411 + */
  14412 + MAX: 'max',
  14413 +
  14414 + /**
  14415 + * @description 图片字段 key
  14416 + */
  14417 + IMAGE: 'image',
  14418 +
  14419 + /**
  14420 + * @description 颜色
  14421 + */
  14422 + COLOR: 'color',
  14423 +
  14424 + /**
  14425 + * @description 图片来源
  14426 + */
  14427 + IMAGE_ORIGIN: 'imageOrigin',
  14428 +
  14429 + /**
  14430 + * @description 图库图形类别
  14431 + */
  14432 + IMAGE_GALLERY_CATEGORY: 'category',
  14433 +
  14434 + /**
  14435 + * @description 图表图形路径
  14436 + */
  14437 + IMAGE_GALLERY_IMAGE_PATH: 'imagePath',
  14438 +
  14439 + /**
  14440 + * @description 默认图片 flag
  14441 + */
  14442 + DEFAULT_IMAGE_FLAG: 'defaultImageFlag'
  14443 + }
  14444 +
  14445 + static enumVideoAccessMode = {
  14446 + MANUAL_ENTER: 0,
  14447 + STREAMING: 1
  14448 + }
  14449 +
  14450 + constructor(DispatchInstance) {
  14451 + this.DispatchInstance = DispatchInstance
  14452 + this.init()
  14453 + }
  14454 +
  14455 + init() {
  14456 + this.getEnableActList()
  14457 + this.getVideoRecord()
  14458 + this.generatorMappingRelation()
  14459 + }
  14460 +
  14461 + get graph() {
  14462 + return this.DispatchInstance.graph
  14463 + }
  14464 +
  14465 + /**
  14466 + * @description 只更新一次的更新事件
  14467 + * @returns {*}
  14468 + */
  14469 + get insertOnceUpdateFn() {
  14470 + return this.DispatchInstance.updateQueueInstance.insertOnceUpdateFn.bind(this.DispatchInstance.updateQueueInstance)
  14471 + }
  14472 +
  14473 + /**
  14474 + * @description 将持续更新时间放入更新队列
  14475 + * @returns {*}
  14476 + */
  14477 + get insertContinueUpdateFn() {
  14478 + return this.DispatchInstance.updateQueueInstance.insertContinueUpdateFn.bind(this.DispatchInstance.updateQueueInstance)
  14479 + }
  14480 +
  14481 + /**
  14482 + * @description 从更新队列中删除更新
  14483 + * @returns {*}
  14484 + */
  14485 + get delUpdateFn() {
  14486 + return this.DispatchInstance.updateQueueInstance.delUpdateFn.bind(this.DispatchInstance.updateQueueInstance)
  14487 + }
  14488 +
  14489 + get contentAllCell() {
  14490 + return this.graph.getDefaultParent().children || []
  14491 + }
  14492 +
  14493 + /**
  14494 + * @description 筛选出视频数据源
  14495 + */
  14496 + getVideoRecord() {
  14497 + const { dataSources = [] } = this.DispatchInstance.contentData
  14498 + const { VIDEO_FLAG } = HandleDynamicEffect.enumVideoConst
  14499 + this.videoRecordList = dataSources.filter(item => item?.additional?.[VIDEO_FLAG])
  14500 + }
  14501 +
  14502 + /**
  14503 + * @description 获取已开启的数据动效
  14504 + */
  14505 + getEnableActList() {
  14506 + const { act = [] } = this.DispatchInstance.contentData
  14507 + // 过滤页面中没有的节点及未开启动效的节点
  14508 + this.enableActList = act.filter(item => this.contentAllCell.find(each => each.id === item.id) && item.enabled)
  14509 + // this.enableActList = act.filter(item => item.enabled)
  14510 + }
  14511 +
  14512 + /**
  14513 + * @description 生成映射关系 && 初始化推送消息
  14514 + */
  14515 + generatorMappingRelation() {
  14516 + this.videoPlay()
  14517 + const tsSubCmds = []
  14518 + this.enableActList.forEach(each => {
  14519 + const { id, type, attr, deviceId, slaveDeviceId } = each
  14520 +
  14521 + if (!this.actNodeMapping.has(id)) this.actNodeMapping.set(id, { display: true, value: new Map() })
  14522 + const temp = this.actNodeMapping.get(id)
  14523 + temp.value.set(type, each)
  14524 +
  14525 + const cmdId = this.DispatchInstance.getCmdId(id)
  14526 +
  14527 + tsSubCmds.push(this.generatorMessage(slaveDeviceId ? slaveDeviceId : deviceId, cmdId, attr))
  14528 +
  14529 + this.subscribeEvent(cmdId, this.dispatch(type))
  14530 + })
  14531 + if (tsSubCmds.length) this.sendMsg({ tsSubCmds })
  14532 + }
  14533 +
  14534 + /**
  14535 + * @description 推送消息
  14536 + * @param msg
  14537 + */
  14538 + sendMsg(msg) {
  14539 + this.DispatchInstance.sendMessageToGetRealTimeData(msg)
  14540 + }
  14541 +
  14542 + /**
  14543 + * @description 订阅事件 绑定回调
  14544 + * @param eventName
  14545 + * @param callback
  14546 + */
  14547 + subscribeEvent(eventName, callback) {
  14548 + this.DispatchInstance.eventBus.on(eventName, callback)
  14549 + }
  14550 +
  14551 + /**
  14552 + * @description 创建消息
  14553 + * @param deviceId
  14554 + * @param cmdId
  14555 + * @param attr
  14556 + * @return {{cmdId, entityType: string, keys, scope: string, entityId}}
  14557 + */
  14558 + generatorMessage(deviceId, cmdId, attr) {
  14559 + return {
  14560 + entityType: "DEVICE",
  14561 + entityId: deviceId,
  14562 + scope: "LATEST_TELEMETRY",
  14563 + cmdId,
  14564 + keys: attr,
  14565 + }
  14566 + }
  14567 +
  14568 + /**
  14569 + * @description 分发事件
  14570 + * @param type
  14571 + * @return Function
  14572 + */
  14573 + dispatch(type) {
  14574 + let invoke = () => {
  14575 + }
  14576 + switch (type) {
  14577 + case HandleDynamicEffect.enumActType.ROTATE:
  14578 + invoke = this.rotate.bind(this)
  14579 + break
  14580 + case HandleDynamicEffect.enumActType.DISPLAY:
  14581 + invoke = this.display.bind(this)
  14582 + break
  14583 + case HandleDynamicEffect.enumActType.FLASH:
  14584 + invoke = this.flash.bind(this)
  14585 + break
  14586 + case HandleDynamicEffect.enumActType.IMAGE:
  14587 + invoke = this.varImage.bind(this)
  14588 + break
  14589 + case HandleDynamicEffect.enumActType.RUNNING:
  14590 + invoke = this.running.bind(this)
  14591 + break
  14592 + }
  14593 + return invoke
  14594 + }
  14595 +
  14596 + /**
  14597 + * @description 旋转
  14598 + * @param message
  14599 + * @param attr
  14600 + */
  14601 + rotate(message) {
  14602 + const { subscriptionId, data } = message
  14603 + const node = this.getNodeByCmdId(subscriptionId)
  14604 + const key = node.id + DispatchCenter.enumDynamicEffectType.ROTATE
  14605 + if (!this.validatePriority(node.id)) {
  14606 + this.delUpdateFn(key)
  14607 + return
  14608 + }
  14609 + const { flag } = this.validate(subscriptionId, DispatchCenter.enumDynamicEffectType.ROTATE, data)
  14610 + let deg = 0
  14611 + const updateFn = () => {
  14612 + if (deg === 360) deg = 0
  14613 + deg += 20
  14614 + let style = node.getStyle()
  14615 + const reg = /rotation=(-?)\w+(;?)/g
  14616 + style = style.replace(reg, '')
  14617 + style += `rotation=${deg};`
  14618 + node.setStyle(style)
  14619 + this.graph.updateCellStyles(style, node)
  14620 + }
  14621 + if (!flag) {
  14622 + this.delUpdateFn(key)
  14623 + return
  14624 + }
  14625 + this.insertContinueUpdateFn(node, updateFn, key)
  14626 + }
  14627 +
  14628 + /**
  14629 + * @description 显示与隐藏
  14630 + * @param message
  14631 + * @param attr
  14632 + */
  14633 + display(message) {
  14634 + const { subscriptionId, data = {} } = message
  14635 + const { flag, condition } = this.validate(subscriptionId, HandleDynamicEffect.enumActType.DISPLAY, data)
  14636 + if (!flag) return
  14637 + const node = this.getNodeByCmdId(subscriptionId)
  14638 + let isShow = false
  14639 + if (condition.type === HandleDynamicEffect.enumDisplayType.SHOW) {
  14640 + isShow = true
  14641 + } else if (condition.type === HandleDynamicEffect.enumDisplayType.HIDDEN) {
  14642 + isShow = false
  14643 + }
  14644 + const updateFn = () => {
  14645 + if (!isShow) {
  14646 + Object.keys(HandleDynamicEffect.enumActType).forEach(key => {
  14647 + const delKey = node.id + key
  14648 + this.delUpdateFn(delKey)
  14649 + })
  14650 + const temp = this.actNodeMapping.get(node.id)
  14651 + temp.display = false
  14652 + } else {
  14653 + const temp = this.actNodeMapping.get(node.id)
  14654 + temp.display = true
  14655 + }
  14656 +
  14657 + node.setVisible(isShow)
  14658 + }
  14659 + this.insertOnceUpdateFn(node, updateFn)
  14660 + }
  14661 +
  14662 + /**
  14663 + * @description 闪烁
  14664 + * @param message
  14665 + * @param attr
  14666 + */
  14667 + flash(message) {
  14668 + const { subscriptionId, data } = message
  14669 + const node = this.getNodeByCmdId(subscriptionId)
  14670 + const key = node.id + DispatchCenter.enumDynamicEffectType.FLASH
  14671 + if (!this.validatePriority(node.id)) {
  14672 + this.delUpdateFn(key)
  14673 + return
  14674 + }
  14675 + const { flag, condition } = this.validate(subscriptionId, HandleDynamicEffect.enumActType.FLASH, data)
  14676 + let flashFlag = false
  14677 + const updateFn = () => {
  14678 + node.setVisible(flashFlag)
  14679 + flashFlag = !flashFlag
  14680 + }
  14681 + if (!flag) {
  14682 + flashFlag = true
  14683 + this.insertOnceUpdateFn(node, updateFn)
  14684 + this.delUpdateFn(key)
  14685 + return
  14686 + }
  14687 + this.insertContinueUpdateFn(node, updateFn, key)
  14688 + }
  14689 +
  14690 + /**
  14691 + * @description 处理变量图片
  14692 + */
  14693 + varImage(message) {
  14694 + const { subscriptionId, data } = message
  14695 + const node = this.getNodeByCmdId(subscriptionId)
  14696 + const { flag, condition } = this.validate(subscriptionId, HandleDynamicEffect.enumActType.IMAGE, data)
  14697 + if (flag && node) {
  14698 + const { imagePath } = condition
  14699 + this.insertOnceUpdateFn(
  14700 + node,
  14701 + () => {
  14702 + node.setStyle(`image;image=${imagePath};imageAspect=0;`)
  14703 + })
  14704 + } else if (!flag && node) {
  14705 + const { condition = [], attr } = this.getBindData(subscriptionId, HandleDynamicEffect.enumActType.IMAGE)
  14706 + const flag = HandleDynamicEffect.enumVarImageConst.DEFAULT_IMAGE_FLAG
  14707 + const defaultBindData = condition.find(item => item[flag])
  14708 + if (defaultBindData) {
  14709 + const { imagePath } = defaultBindData
  14710 + if (!imagePath) return
  14711 + this.insertOnceUpdateFn(
  14712 + node,
  14713 + () => {
  14714 + node.setStyle(`image;image=${imagePath};imageAspect=0;`)
  14715 + })
  14716 + }
  14717 + }
  14718 + }
  14719 +
  14720 + running(message) {
  14721 + const { subscriptionId, data = {} } = message
  14722 + const { flag, condition } = this.validate(subscriptionId, HandleDynamicEffect.enumActType.RUNNING, data)
  14723 + if (!flag) return
  14724 + const node = this.getNodeByCmdId(subscriptionId)
  14725 + let isRun = false
  14726 + if (condition.type === HandleDynamicEffect.enumRunningType.RUN) {
  14727 + isRun = true
  14728 + } else if (condition.type === HandleDynamicEffect.enumRunningType.STOP) {
  14729 + isRun = false
  14730 + }
  14731 +
  14732 + const updateFn = () => {
  14733 + const reg = /flowAnimation[^;]+/
  14734 + const style = node.getStyle()
  14735 + node.setStyle(style.replace(reg, `flowAnimation=${isRun ? 1 : 0}`))
  14736 + }
  14737 + this.insertOnceUpdateFn(node, updateFn)
  14738 + }
  14739 +
  14740 + /**
  14741 + * @description 播放视频
  14742 + */
  14743 + videoPlay() {
  14744 + const enumAccessMode = HandleDynamicEffect.enumVideoAccessMode
  14745 + const reg = /(?:.*)(?<=\.)/
  14746 + const graph = this.graph
  14747 + const createVideoTemplate = this.createVideoTemplate
  14748 + const videoPlayConfig = {
  14749 + controls: true,
  14750 + autoPlay: true,
  14751 + // bigPlayButton: true,
  14752 + // textTrackDisplay: false,
  14753 + // posterImage: false,
  14754 + // errorDisplay: false,
  14755 + }
  14756 + for (const record of this.videoRecordList) {
  14757 + const { additional = {}, nodeId } = record
  14758 + const { accessMode, videoUrl, id } = additional
  14759 + const cell = this.getCellByCellId(nodeId)
  14760 + if (!cell) continue
  14761 + if (Number(accessMode) === enumAccessMode.MANUAL_ENTER) {
  14762 + handleVideoPlay(videoPlayConfig, cell, videoUrl)
  14763 + } else {
  14764 + getStreamingVideoPlayUrl(id, nodeId)
  14765 + }
  14766 + }
  14767 +
  14768 + async function getStreamingVideoPlayUrl(id, cell) {
  14769 + const [err, res = {}] = await to(ConfigurationNodeApi.getStreamingVideoPlayUrl(id))
  14770 + const { url } = res?.data || {}
  14771 + if (!url) return
  14772 + handleVideoPlay({ ...videoPlayConfig, hls: { withCredentials: true } }, cell, url)
  14773 + }
  14774 +
  14775 + function handleVideoPlay(videoPlayConfig, cell, videoUrl) {
  14776 + if (!cell) return
  14777 + const { geometry = {} } = cell
  14778 + const { width, height } = geometry
  14779 + const idEl = getIdEl()
  14780 + graph.getModel().beginUpdate()
  14781 + try {
  14782 + let type
  14783 + if (videoUrl.replace(reg, '') === 'm3u8') type = 'application/x-mpegURL'
  14784 + const template = createVideoTemplate(idEl, width, height, videoUrl, type)
  14785 + cell.setAttribute('label', template)
  14786 + graph.refresh(cell);
  14787 + } finally {
  14788 + graph.getModel().endUpdate()
  14789 + videojs(idEl, videoPlayConfig, function () {
  14790 + this.play()
  14791 + })
  14792 + }
  14793 + }
  14794 +
  14795 + function getIdEl() {
  14796 + return `video-play__${Date.now()}`
  14797 + }
  14798 + }
  14799 +
  14800 +
  14801 + createVideoTemplate(idEl, width, height, videoUrl, videoType = 'video/mp4') {
  14802 + const poster = `${Proxy_Prefix}/images/youtube.png`
  14803 + const template = `<video id="${idEl}" class="video-js" controls preload="auto" muted="muted" width="${width}" height="${height}" poster="${poster}" data-setup='{}'>
  14804 + <source src="${videoUrl}" type="${videoType}">
  14805 + <p class="vjs-no-js">
  14806 + 要查看此视频,请启用JavaScript,并考虑升级web浏览器.
  14807 + </p>
  14808 + </video>`
  14809 + return template
  14810 + }
  14811 + /**
  14812 + * @description 获取cell 通过cell id
  14813 + */
  14814 + getCellByCellId(id) {
  14815 + const allCell = this.contentAllCell || []
  14816 + return allCell.find(item => item.id === id)
  14817 + }
  14818 +
  14819 +
  14820 + /**
  14821 + * @description 验证是否满足条件列表中的任意一条
  14822 + * @param subscriptionId
  14823 + * @param type
  14824 + * @param value
  14825 + * @return {{flag: boolean, condition: {}}}
  14826 + */
  14827 + validate(subscriptionId, type, value) {
  14828 + const { condition = [], attr } = this.getBindData(subscriptionId, type)
  14829 + const result = { condition: {}, flag: false }
  14830 + for (let i = 0; i < condition.length; i++) {
  14831 + const { min, max } = condition[i]
  14832 + const [timespan, realValue] = value[attr][0]
  14833 + result.flag = this.isExistInArea(min, max, realValue)
  14834 + if (result.flag) {
  14835 + result.condition = condition[i]
  14836 + break
  14837 + }
  14838 + }
  14839 + return result
  14840 + }
  14841 +
  14842 + /**
  14843 + * @description 判断一个数是否在区间范围内
  14844 + * @param min 最小值
  14845 + * @param max 最大值
  14846 + * @param value 值
  14847 + * @return {boolean}
  14848 + */
  14849 + isExistInArea(min, max, value) {
  14850 + return Number(value) >= Number(min) && Number(value) <= Number(max)
  14851 + }
  14852 +
  14853 + /**
  14854 + * @description 获取绑定的数据
  14855 + * @param subscriptionId
  14856 + * @param actionType
  14857 + * @return {*}
  14858 + */
  14859 + getBindData(subscriptionId, actionType) {
  14860 + const nodeId = this.getNodeIdByCmdId(subscriptionId)
  14861 + const temp = this.actNodeMapping.get(nodeId)
  14862 + return temp.value.get(actionType)
  14863 + }
  14864 +
  14865 + /**
  14866 + * @description 通过cmdID获取节点id
  14867 + * @param subscriptionId
  14868 + * @return {string}
  14869 + */
  14870 + getNodeIdByCmdId(subscriptionId) {
  14871 + return this.DispatchInstance.cmdIdMapping.get(subscriptionId)
  14872 + }
  14873 +
  14874 + /**
  14875 + * @description 通过cmdID 获取节点
  14876 + * @param subscriptionId
  14877 + * @return {*}
  14878 + */
  14879 + getNodeByCmdId(subscriptionId) {
  14880 + const nodeId = this.getNodeIdByCmdId(subscriptionId)
  14881 + return this.contentAllCell.find(item => item.id === nodeId)
  14882 + }
  14883 +
  14884 + /**
  14885 + * @description 验证数据动效优先级 显示隐藏优先级最高
  14886 + * @param {string} nodeId
  14887 + * @returns
  14888 + */
  14889 + validatePriority(nodeId) {
  14890 + return this.actNodeMapping.get(nodeId).display
  14891 + }
  14892 +
  14893 +}
  14894 +
  14895 +class UpdateQueue {
  14896 +
  14897 + /**
  14898 + * @description 图实例
  14899 + */
  14900 + graph
  14901 +
  14902 + /**
  14903 + * @description 更新队列
  14904 + * @type { Map<number, {cell: any, fn: Function, key: string}[]> }
  14905 + */
  14906 + updateQueueMapping = new Map()
  14907 +
  14908 + /**
  14909 + * @description 时间队列
  14910 + * @type { Map<number, Function> }
  14911 + */
  14912 + timeQueue = new Map()
  14913 +
  14914 + constructor(DispatchCenterInstance) {
  14915 + this.graph = DispatchCenterInstance.graph
  14916 + window.timeMap = this.timeQueue
  14917 + }
  14918 +
  14919 + /**
  14920 + * @description 创建更新队列
  14921 + * @param {number} time
  14922 + */
  14923 + createUpdateQueue(time) {
  14924 + const callback = () => {
  14925 + this.graph.getModel().beginUpdate()
  14926 + try {
  14927 + const updateQueue = this.updateQueueMapping.get(time)
  14928 + updateQueue.forEach(each => {
  14929 + const { cell: updateCell, fn: updateFn } = each
  14930 + updateFn()
  14931 + this.graph.refresh(updateCell);
  14932 + })
  14933 + } finally {
  14934 + this.graph.getModel().endUpdate()
  14935 + }
  14936 + }
  14937 + const cleanFn = RAFSetInterval(callback, time)
  14938 + this.timeQueue.set(time, cleanFn)
  14939 + }
  14940 +
  14941 + createIntervalQueue(time, callback) {
  14942 + const cleanFn = RAFSetInterval(callback, time)
  14943 + this.timeQueue.set(time, cleanFn)
  14944 + }
  14945 +
  14946 + /**
  14947 + * @description 持续更新
  14948 + * @param cell 更新cell
  14949 + * @param fn 更新方法
  14950 + * @param key key
  14951 + * @param time 间隔事时间
  14952 + */
  14953 + insertContinueUpdateFn(cell, fn, key, time = 100) {
  14954 + if (!this.updateQueueMapping.has(time)) {
  14955 + this.updateQueueMapping.set(time, [])
  14956 + }
  14957 + if (!this.timeQueue.get(time)) {
  14958 + this.createUpdateQueue(time)
  14959 + }
  14960 + if (this.updateQueueMapping.get(time).find(item => item.key === key)) {
  14961 + return
  14962 + }
  14963 + this.updateQueueMapping.get(time).push({ cell, fn, key })
  14964 + }
  14965 +
  14966 + /**
  14967 + * @description 只更新一次
  14968 + * @param cell 更新cell
  14969 + * @param fn 更新方法
  14970 + */
  14971 + insertOnceUpdateFn(cell, fn) {
  14972 + this.graph.getModel().beginUpdate()
  14973 + try {
  14974 + fn()
  14975 + this.graph.refresh(cell);
  14976 + } finally {
  14977 + this.graph.getModel().endUpdate()
  14978 + }
  14979 + }
  14980 +
  14981 + /**
  14982 + * @description 从更新队列中移除不再更新的事件
  14983 + * @param key
  14984 + * @param time
  14985 + */
  14986 + delUpdateFn(key, time = 100) {
  14987 + let temp = this.updateQueueMapping.get(time)
  14988 + if (temp) {
  14989 + const index = temp.findIndex(item => item.key === key)
  14990 + if (~index) {
  14991 + temp.splice(index, 1)
  14992 + if (!temp.length) {
  14993 + const cleanFn = this.timeQueue.get(time)
  14994 + cleanFn()
  14995 + this.timeQueue.delete(time)
  14996 + }
  14997 + }
  14998 + }
  14999 + }
  15000 +
  15001 + /**
  15002 + * @description 清楚所有更新队列
  15003 + */
  15004 + cleanAllUpdateQueue() {
  15005 + this.timeQueue.forEach(cleanFn => {
  15006 + cleanFn()
  15007 + })
  15008 + }
  15009 +}
  15010 +
  15011 +class RAFSetTimeoutImp {
  15012 + constructor(callback, time = 0) {
  15013 + this.callback = callback;
  15014 + this.time = time;
  15015 + this.now = Date.now;
  15016 + this.current = this.now();
  15017 + this.old = this.now();
  15018 + const fn = () => {
  15019 + if (this.current - this.old > this.time) {
  15020 + this.callback();
  15021 + this.old = this.now();
  15022 + this.clean();
  15023 + return;
  15024 + }
  15025 + this.current = this.now();
  15026 + this.instance = window.requestAnimationFrame(fn);
  15027 + };
  15028 + this.instance = window.requestAnimationFrame(fn);
  15029 + }
  15030 +
  15031 + clean() {
  15032 + window.cancelAnimationFrame(this.instance);
  15033 + this.instance = null;
  15034 + }
  15035 +}
  15036 +
  15037 +function RAFSetTimeout(callback, time) {
  15038 + const instance = new RAFSetTimeoutImp(callback, time);
  15039 + return instance.clean.bind(instance);
  15040 +}
  15041 +
  15042 +class RAFSetIntervalImp {
  15043 + constructor(callback, time = 0) {
  15044 + this.callback = callback;
  15045 + this.time = time;
  15046 + this.now = Date.now;
  15047 + this.current = this.now();
  15048 + this.old = this.now();
  15049 + const fn = () => {
  15050 + if (this.current - this.old > this.time) {
  15051 + this.callback();
  15052 + this.old = this.now();
  15053 + }
  15054 + this.current = this.now();
  15055 + this.instance = window.requestAnimationFrame(fn);
  15056 + };
  15057 + this.instance = window.requestAnimationFrame(fn);
  15058 + }
  15059 +
  15060 + clean() {
  15061 + window.cancelAnimationFrame(this.instance);
  15062 + this.instance = null
  15063 + }
  15064 +}
  15065 +
  15066 +function RAFSetInterval(callback, time) {
  15067 + const instance = new RAFSetIntervalImp(callback, time);
  15068 + return instance.clean.bind(instance);
  15069 +}
  15070 +
  15071 +
  15072 +class Validate {
  15073 + /**
  15074 + * @description
  15075 + * @type {{value: any, message: string, required?: boolean, validator?: any}[]} list
  15076 + */
  15077 + list = []
  15078 +
  15079 + /**
  15080 + * @description
  15081 + * @param {{value: any, message: string, required?: boolean, validator?: any}[]} ruleList
  15082 + */
  15083 + constructor(ruleList = []) {
  15084 + this.list = ruleList
  15085 + }
  15086 +
  15087 + /**
  15088 + * @description 设置规则
  15089 + * @param {{value: any, message: string, required?: boolean, validator?: any}} rule
  15090 + */
  15091 + set(rule) {
  15092 + this.list.push(rule)
  15093 + }
  15094 +
  15095 + begin() {
  15096 + for (const rule of this.list) {
  15097 + const { required, value, message, validator } = rule
  15098 + if (required && !value) {
  15099 + UseLayUi.topErrorMsg(message)
  15100 + return false
  15101 + }
  15102 + }
  15103 + return true
  15104 + }
  15105 +}
... ...
... ... @@ -90,6 +90,7 @@ function copyFileUsageOssServer(cb) {
90 90 './src/main/webapp/js/shapes-14-6-5.min.js',
91 91 './src/main/webapp/js/stencils.min.js',
92 92 './src/main/webapp/js/extensions.min.js',
  93 + './src/main/webapp/js/plugin/echarts/echarts.js',
93 94 './src/main/webapp/js/plugin/crypto-js/crypto-js.js',
94 95 './src/main/webapp/js/plugin/ace/ace.js',
95 96 './src/main/webapp/js/plugin/ace/mode-json.js',
... ...