UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,119 lines • 97.3 kB
/** * DevExtreme (esm/ui/diagram/ui.diagram.js) * Version: 24.2.6 * Build date: Mon Mar 17 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import { getOuterWidth, getHeight, getOuterHeight } from "../../core/utils/size"; import $ from "../../core/renderer"; import Widget from "../widget/ui.widget"; import LoadIndicator from "../load_indicator"; import registerComponent from "../../core/component_registrator"; import { extend } from "../../core/utils/extend"; import { isFunction, isDefined } from "../../core/utils/type"; import { compileSetter, compileGetter } from "../../core/utils/data"; import positionUtils from "../../common/core/animation/position"; import { getDiagram } from "./diagram.importer"; import { getWindow, hasWindow } from "../../core/utils/window"; import { getPublicElement } from "../../core/element"; import eventsEngine from "../../common/core/events/core/events_engine"; import { addNamespace } from "../../common/core/events/utils/index"; import messageLocalization from "../../common/core/localization/message"; import numberLocalization from "../../common/core/localization/number"; import * as zIndexPool from "../../__internal/ui/overlay/m_z_index"; import Overlay from "../overlay/ui.overlay"; import DiagramToolbar from "./ui.diagram.toolbar"; import DiagramMainToolbar from "./ui.diagram.main_toolbar"; import DiagramHistoryToolbar from "./ui.diagram.history_toolbar"; import DiagramViewToolbar from "./ui.diagram.view_toolbar"; import DiagramPropertiesToolbar from "./ui.diagram.properties_toolbar"; import diagramContextMenuModule from "./ui.diagram.context_menu"; import DiagramContextToolbox from "./ui.diagram.context_toolbox"; import DiagramDialog from "./ui.diagram.dialogs"; import DiagramScrollView from "./ui.diagram.scroll_view"; import DiagramToolboxManager from "./diagram.toolbox_manager"; import DiagramToolbox from "./ui.diagram.toolbox"; import DiagramPropertiesPanel from "./ui.diagram.properties_panel"; import DiagramOptionsUpdateBar from "./diagram.options_update"; import DiagramDialogManager from "./ui.diagram.dialog_manager"; import DiagramCommandsManager from "./diagram.commands_manager"; import NodesOption from "./diagram.nodes_option"; import EdgesOption from "./diagram.edges_option"; const DIAGRAM_CLASS = "dx-diagram"; const DIAGRAM_FULLSCREEN_CLASS = "dx-diagram-fullscreen"; const DIAGRAM_TOOLBAR_WRAPPER_CLASS = "dx-diagram-toolbar-wrapper"; const DIAGRAM_CONTENT_WRAPPER_CLASS = "dx-diagram-content-wrapper"; const DIAGRAM_CONTENT_CLASS = "dx-diagram-content"; const DIAGRAM_SCROLL_VIEW_CLASS = "dx-diagram-scroll-view"; const DIAGRAM_FLOATING_TOOLBAR_CONTAINER_CLASS = "dx-diagram-floating-toolbar-container"; const DIAGRAM_PROPERTIES_PANEL_TOOLBAR_CONTAINER_CLASS = "dx-diagram-properties-panel-toolbar-container"; const DIAGRAM_LOADING_INDICATOR_CLASS = "dx-diagram-loading-indicator"; const DIAGRAM_FLOATING_PANEL_OFFSET = 12; const DIAGRAM_DEFAULT_UNIT = "in"; const DIAGRAM_DEFAULT_ZOOMLEVEL = 1; const DIAGRAM_DEFAULT_AUTOZOOM_MODE = "disabled"; const DIAGRAM_DEFAULT_PAGE_ORIENTATION = "portrait"; const DIAGRAM_DEFAULT_PAGE_COLOR = "#ffffff"; const DIAGRAM_MAX_MOBILE_WINDOW_WIDTH = 576; const DIAGRAM_TOOLBOX_SHAPE_SPACING = 12; const DIAGRAM_TOOLBOX_SHAPES_PER_ROW = 3; const DIAGRAM_CONTEXT_TOOLBOX_SHAPE_SPACING = 12; const DIAGRAM_CONTEXT_TOOLBOX_SHAPES_PER_ROW = 4; const DIAGRAM_CONTEXT_TOOLBOX_DEFAULT_WIDTH = 152; const DIAGRAM_NAMESPACE = "dxDiagramEvent"; const FULLSCREEN_CHANGE_EVENT_NAME = addNamespace("fullscreenchange", "dxDiagramEvent"); const IE_FULLSCREEN_CHANGE_EVENT_NAME = addNamespace("msfullscreenchange", "dxDiagramEvent"); const WEBKIT_FULLSCREEN_CHANGE_EVENT_NAME = addNamespace("webkitfullscreenchange", "dxDiagramEvent"); const MOZ_FULLSCREEN_CHANGE_EVENT_NAME = addNamespace("mozfullscreenchange", "dxDiagramEvent"); class Diagram extends Widget { _init() { this._updateDiagramLockCount = 0; this.toggleFullscreenLock = 0; this._toolbars = []; super._init(); this._initDiagram(); this._createCustomCommand() } _initMarkup() { super._initMarkup(); this._toolbars = []; delete this._isMobileScreenSize; const isServerSide = !hasWindow(); this.$element().addClass("dx-diagram"); delete this._mainToolbar; if (this.option("mainToolbar.visible")) { this._renderMainToolbar() } const $contentWrapper = $("<div>").addClass("dx-diagram-content-wrapper").appendTo(this.$element()); delete this._historyToolbar; delete this._historyToolbarResizeCallback; if (this._isHistoryToolbarVisible()) { this._renderHistoryToolbar($contentWrapper) } delete this._propertiesToolbar; delete this._propertiesToolbarResizeCallback; if (this._isPropertiesPanelEnabled()) { this._renderPropertiesToolbar($contentWrapper) } delete this._viewToolbar; delete this._viewToolbarResizeCallback; if (this.option("viewToolbar.visible")) { this._renderViewToolbar($contentWrapper) } delete this._toolbox; delete this._toolboxResizeCallback; if (this._isToolboxEnabled()) { this._renderToolbox($contentWrapper) } delete this._propertiesPanel; delete this._propertiesPanelResizeCallback; if (this._isPropertiesPanelEnabled()) { this._renderPropertiesPanel($contentWrapper) } this._$content = $("<div>").addClass("dx-diagram-content").appendTo($contentWrapper); delete this._contextMenu; this._diagramInstance.settings.contextMenuEnabled = this.option("contextMenu.enabled"); if (this._diagramInstance.settings.contextMenuEnabled) { this._renderContextMenu($contentWrapper) } delete this._contextToolbox; if (this.option("contextToolbox.enabled")) { this._renderContextToolbox($contentWrapper) } this._renderDialog($contentWrapper); if (!isServerSide) { const $scrollViewWrapper = $("<div>").addClass("dx-diagram-scroll-view").appendTo(this._$content); this._createComponent($scrollViewWrapper, DiagramScrollView, { useNativeScrolling: this.option("useNativeScrolling"), onCreateDiagram: e => { this._diagramInstance.createDocument(e.$parent[0], e.scrollView, $contentWrapper[0]) } }) } this._setCustomCommandChecked(DiagramCommandsManager.SHOW_PROPERTIES_PANEL_COMMAND_NAME, this._isPropertiesPanelVisible()); this._setCustomCommandChecked(DiagramCommandsManager.SHOW_TOOLBOX_COMMAND_NAME, this._isToolboxVisible()); this._createOptionsUpdateBar() } _dimensionChanged() { this._isMobileScreenSize = void 0; this._processDiagramResize() } _visibilityChanged(visible) { if (visible) { this._bindDiagramData(); this.repaint() } } _processDiagramResize() { this._diagramInstance.onDimensionChanged(); if (this._historyToolbarResizeCallback) { this._historyToolbarResizeCallback.call(this) } if (this._propertiesToolbarResizeCallback) { this._propertiesToolbarResizeCallback.call(this) } if (this._propertiesPanelResizeCallback) { this._propertiesPanelResizeCallback.call(this) } if (this._viewToolbarResizeCallback) { this._viewToolbarResizeCallback.call(this) } if (this._toolboxResizeCallback) { this._toolboxResizeCallback.call(this) } } isMobileScreenSize() { if (void 0 === this._isMobileScreenSize) { this._isMobileScreenSize = hasWindow() && getOuterWidth(this.$element()) < 576 } return this._isMobileScreenSize } _captureFocus() { if (this._diagramInstance) { this._diagramInstance.captureFocus() } } _captureFocusOnTimeout() { this._captureFocusTimeout = setTimeout((() => { this._captureFocus(); delete this._captureFocusTimeout }), 100) } _killCaptureFocusTimeout() { if (this._captureFocusTimeout) { clearTimeout(this._captureFocusTimeout); delete this._captureFocusTimeout } } notifyBarCommandExecuted() { this._captureFocusOnTimeout() } _registerToolbar(component) { this._registerBar(component); this._toolbars.push(component) } _registerBar(component) { component.bar.onChanged.add(this); this._diagramInstance.registerBar(component.bar) } _getExcludeCommands() { const excludeCommands = []; if (!this._isToolboxEnabled()) { excludeCommands.push(DiagramCommandsManager.SHOW_TOOLBOX_COMMAND_NAME) } if (!this._isPropertiesPanelEnabled()) { excludeCommands.push(DiagramCommandsManager.SHOW_PROPERTIES_PANEL_COMMAND_NAME) } return excludeCommands } _getToolbarBaseOptions() { return { onContentReady: _ref => { let { component: component } = _ref; return this._registerToolbar(component) }, onSubMenuVisibilityChanging: _ref2 => { let { component: component } = _ref2; return this._diagramInstance.updateBarItemsState(component.bar) }, onPointerUp: this._onPanelPointerUp.bind(this), export: this.option("export"), excludeCommands: this._getExcludeCommands(), onInternalCommand: this._onInternalCommand.bind(this), onCustomCommand: this._onCustomCommand.bind(this), isMobileView: this.isMobileScreenSize() } } _onInternalCommand(e) { switch (e.command) { case DiagramCommandsManager.SHOW_TOOLBOX_COMMAND_NAME: if (this._toolbox) { this._toolbox.toggle() } break; case DiagramCommandsManager.SHOW_PROPERTIES_PANEL_COMMAND_NAME: if (this._propertiesPanel) { this._propertiesPanel.toggle() } } } _onCustomCommand(e) { this._customCommandAction({ name: e.name }) } _renderMainToolbar() { const $toolbarWrapper = $("<div>").addClass("dx-diagram-toolbar-wrapper").appendTo(this.$element()); this._mainToolbar = this._createComponent($toolbarWrapper, DiagramMainToolbar, extend(this._getToolbarBaseOptions(), { commands: this.option("mainToolbar.commands"), skipAdjustSize: true })) } _isHistoryToolbarVisible() { return this.option("historyToolbar.visible") && !this.isReadOnlyMode() } _renderHistoryToolbar($parent) { const $container = $("<div>").addClass("dx-diagram-floating-toolbar-container").appendTo($parent); this._historyToolbar = this._createComponent($container, DiagramHistoryToolbar, extend(this._getToolbarBaseOptions(), { commands: this.option("historyToolbar.commands"), locateInMenu: "never" })); this._updateHistoryToolbarPosition(); this._historyToolbarResizeCallback = () => { this._historyToolbar.option("isMobileView", this.isMobileScreenSize()) } } _updateHistoryToolbarPosition() { if (!hasWindow()) { return } positionUtils.setup(this._historyToolbar.$element(), { my: "left top", at: "left top", of: this._historyToolbar.$element().parent(), offset: "12 12" }) } _isToolboxEnabled() { return "disabled" !== this.option("toolbox.visibility") && !this.isReadOnlyMode() } _isToolboxVisible() { return "visible" === this.option("toolbox.visibility") || "auto" === this.option("toolbox.visibility") && !this.isMobileScreenSize() } _renderToolbox($parent) { const isServerSide = !hasWindow(); const $toolBox = $("<div>").appendTo($parent); const bounds = this._getToolboxBounds($parent, isServerSide); this._toolbox = this._createComponent($toolBox, DiagramToolbox, { isMobileView: this.isMobileScreenSize(), isVisible: this._isToolboxVisible(), container: this.$element(), height: bounds.height, offsetParent: $parent, offsetX: bounds.offsetX, offsetY: bounds.offsetY, showSearch: this.option("toolbox.showSearch"), toolboxGroups: this._getToolboxGroups(), toolboxWidth: this.option("toolbox.width"), onShapeCategoryRendered: e => { if (isServerSide) { return } this._diagramInstance.createToolbox(e.$element[0], "texts" === e.displayMode, e.shapes || e.category, { shapeIconSpacing: 12, shapeIconCountInRow: this.option("toolbox.shapeIconsPerRow"), shapeIconAttributes: { "data-toggle": e.dataToggle } }) }, onFilterChanged: e => { if (isServerSide) { return } this._diagramInstance.applyToolboxFilter(e.text, e.filteringToolboxes) }, onVisibilityChanging: e => { if (isServerSide) { return } this._setCustomCommandChecked(DiagramCommandsManager.SHOW_TOOLBOX_COMMAND_NAME, e.visible); if (this._propertiesPanel) { if (e.visible && this.isMobileScreenSize()) { this._propertiesPanel.hide() } } if (this._historyToolbar) { if (e.visible && this.isMobileScreenSize()) { this._historyToolbarZIndex = zIndexPool.create(Overlay.baseZIndex()); this._historyToolbar.$element().css("zIndex", this._historyToolbarZIndex); this._historyToolbar.$element().css("boxShadow", "none") } } if (this._viewToolbar) { this._viewToolbar.$element().css("opacity", e.visible && this.isMobileScreenSize() ? "0" : "1"); this._viewToolbar.$element().css("pointerEvents", e.visible && this.isMobileScreenSize() ? "none" : "") } }, onVisibilityChanged: e => { if (!e.visible && !this._textInputStarted) { this._captureFocus() } if (!isServerSide) { if (this._historyToolbar) { if (!e.visible && this.isMobileScreenSize() && this._historyToolbarZIndex) { zIndexPool.remove(this._historyToolbarZIndex); this._historyToolbar.$element().css("zIndex", ""); this._historyToolbar.$element().css("boxShadow", ""); this._historyToolbarZIndex = void 0 } } } }, onPointerUp: this._onPanelPointerUp.bind(this) }); this._toolbox._popup.option("propagateOutsideClick", !this.option("fullScreen")); this._toolboxResizeCallback = () => { const bounds = this._getToolboxBounds($parent, isServerSide); this._toolbox.option("height", bounds.height); const prevIsMobileView = this._toolbox.option("isMobileView"); if (prevIsMobileView !== this.isMobileScreenSize()) { this._toolbox.option({ isMobileView: this.isMobileScreenSize(), isVisible: this._isToolboxVisible() }); this._setCustomCommandChecked(DiagramCommandsManager.SHOW_TOOLBOX_COMMAND_NAME, this._isToolboxVisible()) } this._toolbox.updateMaxHeight() } } _getToolboxBounds($parent, isServerSide) { const result = { offsetX: 12, offsetY: 12, height: !isServerSide ? getHeight($parent) - 24 : 0 }; if (this._historyToolbar && !isServerSide) { result.offsetY += getOuterHeight(this._historyToolbar.$element()) + 12; result.height -= getOuterHeight(this._historyToolbar.$element()) + 12 } if (this._viewToolbar && !isServerSide) { result.height -= getOuterHeight(this._viewToolbar.$element()) + this._getViewToolbarYOffset(isServerSide) } return result } _renderViewToolbar($parent) { const isServerSide = !hasWindow(); const $container = $("<div>").addClass("dx-diagram-floating-toolbar-container").appendTo($parent); this._viewToolbar = this._createComponent($container, DiagramViewToolbar, extend(this._getToolbarBaseOptions(), { commands: this.option("viewToolbar.commands"), locateInMenu: "never" })); this._updateViewToolbarPosition($container, $parent, isServerSide); this._viewToolbarResizeCallback = () => { this._updateViewToolbarPosition($container, $parent, isServerSide) } } _getViewToolbarYOffset(isServerSide) { if (isServerSide) { return } let result = 12; if (this._viewToolbar && this._propertiesToolbar) { result += (getOuterHeight(this._propertiesToolbar.$element()) - getOuterHeight(this._viewToolbar.$element())) / 2 } return result } _updateViewToolbarPosition($container, $parent, isServerSide) { if (isServerSide) { return } positionUtils.setup($container, { my: "left bottom", at: "left bottom", of: $parent, offset: "12 -" + this._getViewToolbarYOffset(isServerSide) }) } _isPropertiesPanelEnabled() { return "disabled" !== this.option("propertiesPanel.visibility") && !this.isReadOnlyMode() } _isPropertiesPanelVisible() { return "visible" === this.option("propertiesPanel.visibility") } _renderPropertiesToolbar($parent) { const isServerSide = !hasWindow(); const $container = $("<div>").addClass("dx-diagram-floating-toolbar-container").addClass("dx-diagram-properties-panel-toolbar-container").appendTo($parent); this._propertiesToolbar = this._createComponent($container, DiagramPropertiesToolbar, extend(this._getToolbarBaseOptions(), { buttonStylingMode: "contained", buttonType: "default", locateInMenu: "never" })); this._updatePropertiesToolbarPosition($container, $parent, isServerSide); this._propertiesToolbarResizeCallback = () => { this._updatePropertiesToolbarPosition($container, $parent, isServerSide) } } _updatePropertiesToolbarPosition($container, $parent, isServerSide) { if (isServerSide) { return } positionUtils.setup($container, { my: "right bottom", at: "right bottom", of: $parent, offset: "-12 -12" }) } _renderPropertiesPanel($parent) { const isServerSide = !hasWindow(); const $propertiesPanel = $("<div>").appendTo($parent); const offsetY = 24 + (!isServerSide ? getOuterHeight(this._propertiesToolbar.$element()) : 0); this._propertiesPanel = this._createComponent($propertiesPanel, DiagramPropertiesPanel, { isMobileView: this.isMobileScreenSize(), isVisible: this._isPropertiesPanelVisible(), container: this.$element(), offsetParent: $parent, offsetX: 12, offsetY: offsetY, propertyTabs: this.option("propertiesPanel.tabs"), onCreateToolbar: e => { e.toolbar = this._createComponent(e.$parent, DiagramToolbar, extend(this._getToolbarBaseOptions(), { commands: e.commands, locateInMenu: "never", editorStylingMode: "outlined" })) }, onVisibilityChanging: e => { if (isServerSide) { return } this._updatePropertiesPanelGroupBars(e.component); this._setCustomCommandChecked(DiagramCommandsManager.SHOW_PROPERTIES_PANEL_COMMAND_NAME, e.visible); if (this._toolbox) { if (e.visible && this.isMobileScreenSize()) { this._toolbox.hide() } } }, onVisibilityChanged: e => { if (!e.visible && !this._textInputStarted) { this._captureFocus() } }, onSelectedGroupChanged: _ref3 => { let { component: component } = _ref3; return this._updatePropertiesPanelGroupBars(component) }, onPointerUp: this._onPanelPointerUp.bind(this) }); this._propertiesPanelResizeCallback = () => { const prevIsMobileView = this._propertiesPanel.option("isMobileView"); if (prevIsMobileView !== this.isMobileScreenSize()) { this._propertiesPanel.option({ isMobileView: this.isMobileScreenSize(), isVisible: this._isPropertiesPanelVisible() }); this._setCustomCommandChecked(DiagramCommandsManager.SHOW_PROPERTIES_PANEL_COMMAND_NAME, this._isPropertiesPanelVisible()) } } } _updatePropertiesPanelGroupBars(component) { component.getActiveToolbars().forEach((toolbar => { this._diagramInstance.updateBarItemsState(toolbar.bar) })) } _onPanelPointerUp() { this._captureFocusOnTimeout() } _renderContextMenu($parent) { const $contextMenu = $("<div>").appendTo($parent); this._contextMenu = this._createComponent($contextMenu, diagramContextMenuModule.DiagramContextMenuWrapper, { commands: this.option("contextMenu.commands"), onContentReady: _ref4 => { let { component: component } = _ref4; return this._registerBar(component) }, onVisibilityChanging: _ref5 => { let { component: component } = _ref5; return this._diagramInstance.updateBarItemsState(component.bar) }, onItemClick: itemData => this._onBeforeCommandExecuted(itemData.command), export: this.option("export"), excludeCommands: this._getExcludeCommands(), onInternalCommand: this._onInternalCommand.bind(this), onCustomCommand: this._onCustomCommand.bind(this) }) } _renderContextToolbox($parent) { const isServerSide = !hasWindow(); const category = this.option("contextToolbox.category"); const displayMode = this.option("contextToolbox.displayMode"); const shapes = this.option("contextToolbox.shapes"); const $contextToolbox = $("<div>").appendTo($parent); this._contextToolbox = this._createComponent($contextToolbox, DiagramContextToolbox, { toolboxWidth: this.option("contextToolbox.width"), onShown: e => { if (isServerSide) { return } const $toolboxContainer = $(e.$element); let isTextGroup = "texts" === displayMode; if (!shapes && !category && !isTextGroup) { const group = this._getToolboxGroups().filter((function(g) { return g.category === e.category }))[0]; if (group) { isTextGroup = "texts" === group.displayMode } } this._diagramInstance.createContextToolbox($toolboxContainer[0], isTextGroup, shapes || category || e.category, { shapeIconSpacing: 12, shapeIconCountInRow: this.option("contextToolbox.shapeIconsPerRow") }, (shapeType => { e.callback(shapeType); this._captureFocus(); e.hide() })) } }) } _setCustomCommandChecked(command, checked) { this._toolbars.forEach((tb => { tb.setCommandChecked(command, checked) })) } _onBeforeCommandExecuted(command) { const dialogParameters = DiagramDialogManager.getDialogParameters(command); if (dialogParameters) { this._showDialog(dialogParameters) } return !!dialogParameters } _renderDialog($parent) { const $dialogElement = $("<div>").appendTo($parent); this._dialogInstance = this._createComponent($dialogElement, DiagramDialog, {}) } _showDialog(dialogParameters) { if (this._dialogInstance) { this._dialogInstance.option("onGetContent", dialogParameters.onGetContent); this._dialogInstance.option("onHidden", function() { this._captureFocus() }.bind(this)); this._dialogInstance.option("command", this._diagramInstance.getCommand(dialogParameters.command)); this._dialogInstance.option("title", dialogParameters.title); this._dialogInstance._show() } } _showLoadingIndicator() { this._loadingIndicator = $("<div>").addClass("dx-diagram-loading-indicator"); this._createComponent(this._loadingIndicator, LoadIndicator, {}); const $parent = this._$content || this.$element(); $parent.append(this._loadingIndicator) } _hideLoadingIndicator() { if (!this._loadingIndicator) { return } this._loadingIndicator.remove(); this._loadingIndicator = null } _initDiagram() { const { DiagramControl: DiagramControl } = getDiagram(); this._diagramInstance = new DiagramControl; this._diagramInstance.onChanged = this._raiseDataChangeAction.bind(this); this._diagramInstance.onEdgeInserted = this._raiseEdgeInsertedAction.bind(this); this._diagramInstance.onEdgeUpdated = this._raiseEdgeUpdatedAction.bind(this); this._diagramInstance.onEdgeRemoved = this._raiseEdgeRemovedAction.bind(this); this._diagramInstance.onNodeInserted = this._raiseNodeInsertedAction.bind(this); this._diagramInstance.onNodeUpdated = this._raiseNodeUpdatedAction.bind(this); this._diagramInstance.onNodeRemoved = this._raiseNodeRemovedAction.bind(this); this._diagramInstance.onToolboxDragStart = this._raiseToolboxDragStart.bind(this); this._diagramInstance.onToolboxDragEnd = this._raiseToolboxDragEnd.bind(this); this._diagramInstance.onTextInputStart = this._raiseTextInputStart.bind(this); this._diagramInstance.onTextInputEnd = this._raiseTextInputEnd.bind(this); this._diagramInstance.onToggleFullscreen = this._onToggleFullScreen.bind(this); this._diagramInstance.onShowContextMenu = this._onShowContextMenu.bind(this); this._diagramInstance.onHideContextMenu = this._onHideContextMenu.bind(this); this._diagramInstance.onShowContextToolbox = this._onShowContextToolbox.bind(this); this._diagramInstance.onHideContextToolbox = this._onHideContextToolbox.bind(this); this._diagramInstance.onNativeAction.add({ notifyItemClick: this._raiseItemClickAction.bind(this), notifyItemDblClick: this._raiseItemDblClickAction.bind(this), notifySelectionChanged: this._raiseSelectionChanged.bind(this) }); this._diagramInstance.onRequestOperation = this._raiseRequestEditOperation.bind(this); this._updateEventSubscriptionMethods(); this._updateDefaultItemProperties(); this._updateEditingSettings(); this._updateShapeTexts(); this._updateUnitItems(); this._updateFormatUnitsMethod(); if ("in" !== this.option("units")) { this._updateUnitsState() } if (this.isReadOnlyMode()) { this._updateReadOnlyState() } if (this.option("pageSize")) { if (this.option("pageSize.items")) { this._updatePageSizeItemsState() } if (this.option("pageSize.width") && this.option("pageSize.height")) { this._updatePageSizeState() } } if ("portrait" !== this.option("pageOrientation")) { this._updatePageOrientationState() } if ("#ffffff" !== this.option("pageColor")) { this._updatePageColorState() } if ("in" !== this.option("viewUnits")) { this._updateViewUnitsState() } if (!this.option("showGrid")) { this._updateShowGridState() } if (!this.option("snapToGrid")) { this._updateSnapToGridState() } if (this.option("gridSize")) { this._updateGridSizeState() } if (1 !== this.option("zoomLevel")) { this._updateZoomLevelState() } if (this.option("simpleView")) { this._updateSimpleViewState() } if ("disabled" !== this.option("autoZoomMode")) { this._updateAutoZoomState() } if (this.option("fullScreen")) { const window = getWindow(); if (window && window.self !== window.top) { this.option("fullScreen", false) } else { this._updateFullscreenState() } } this._createOptionsUpdateBar(); if (hasWindow()) { this._diagramInstance.initMeasurer(this.$element()[0]) } this._updateCustomShapes(this._getCustomShapes()); this._refreshDataSources() } _createOptionsUpdateBar() { if (!this.optionsUpdateBar) { this.optionsUpdateBar = new DiagramOptionsUpdateBar(this); this._diagramInstance.registerBar(this.optionsUpdateBar) } } _deleteOptionsUpdateBar() { delete this.optionsUpdateBar } _clean() { if (this._diagramInstance) { this._diagramInstance.cleanMarkup((element => { $(element).empty() })); this._deleteOptionsUpdateBar() } super._clean() } _dispose() { this._killCaptureFocusTimeout(); super._dispose(); if (this._diagramInstance) { this._diagramInstance.dispose(); this._diagramInstance = void 0 } } _executeDiagramCommand(command, parameter) { this._diagramInstance.getCommand(command).execute(parameter) } getNodeDataSource() { return this._nodesOption && this._nodesOption.getDataSource() } getEdgeDataSource() { return this._edgesOption && this._edgesOption.getDataSource() } _refreshDataSources() { this._beginUpdateDiagram(); this._refreshNodesDataSource(); this._refreshEdgesDataSource(); this._endUpdateDiagram() } _refreshNodesDataSource() { if (this._nodesOption) { this._nodesOption._disposeDataSource(); delete this._nodesOption } if (this.option("nodes.dataSource")) { this._nodesOption = new NodesOption(this); this._nodesOption.option("dataSource", this.option("nodes.dataSource")); this._nodesOption._refreshDataSource() } } _refreshEdgesDataSource() { if (this._edgesOption) { this._edgesOption._disposeDataSource(); delete this._edgesOption } if (this.option("edges.dataSource")) { this._edgesOption = new EdgesOption(this); this._edgesOption.option("dataSource", this.option("edges.dataSource")); this._edgesOption._refreshDataSource() } } _getDiagramData() { let value; const { DiagramCommand: DiagramCommand } = getDiagram(); this._executeDiagramCommand(DiagramCommand.Export, (function(data) { value = data })); return value } _setDiagramData(data, keepExistingItems) { const { DiagramCommand: DiagramCommand } = getDiagram(); this._executeDiagramCommand(DiagramCommand.Import, { data: data, keepExistingItems: keepExistingItems }) } isReadOnlyMode() { return this.option("readOnly") || this.option("disabled") } _onDataSourceChanged() { this._bindDiagramData() } _getChangesKeys(changes) { return changes.map((change => { if (isDefined(change.internalKey)) { return change.internalKey } else if (isDefined(change.key)) { return change.key } else { return null } })).filter((key => isDefined(key))) } _createOptionGetter(optionName) { const expr = this.option(optionName); return expr && compileGetter(expr) } _onRequestUpdateLayout(changes) { if (!this._requestLayoutUpdateAction) { this._createRequestLayoutUpdateAction() } const eventArgs = { changes: changes, allowed: false }; this._requestLayoutUpdateAction(eventArgs); return eventArgs.allowed } _createOptionSetter(optionName) { const expr = this.option(optionName); if (isFunction(expr)) { return expr } return expr && compileSetter(expr) } _bindDiagramData() { if (this._updateDiagramLockCount || !this._isBindingMode()) { return } const { DiagramCommand: DiagramCommand, ConnectorLineOption: ConnectorLineOption, ConnectorLineEnding: ConnectorLineEnding } = getDiagram(); let lineOptionGetter; let lineOptionSetter; let startLineEndingGetter; let startLineEndingSetter; let endLineEndingGetter; let endLineEndingSetter; let containerChildrenGetter; let containerChildrenSetter; const data = { nodeDataSource: this._nodesOption && this._nodesOption.getItems(), edgeDataSource: this._edgesOption && this._edgesOption.getItems(), nodeDataImporter: { getKey: this._createOptionGetter("nodes.keyExpr"), setKey: this._createOptionSetter("nodes.keyExpr"), getCustomData: this._createOptionGetter("nodes.customDataExpr"), setCustomData: this._createOptionSetter("nodes.customDataExpr"), getLocked: this._createOptionGetter("nodes.lockedExpr"), setLocked: this._createOptionSetter("nodes.lockedExpr"), getStyle: this._createOptionGetter("nodes.styleExpr"), setStyle: this._createOptionSetter("nodes.styleExpr"), getStyleText: this._createOptionGetter("nodes.textStyleExpr"), setStyleText: this._createOptionSetter("nodes.textStyleExpr"), getZIndex: this._createOptionGetter("nodes.zIndexExpr"), setZIndex: this._createOptionSetter("nodes.zIndexExpr"), getType: this._createOptionGetter("nodes.typeExpr"), setType: this._createOptionSetter("nodes.typeExpr"), getText: this._createOptionGetter("nodes.textExpr"), setText: this._createOptionSetter("nodes.textExpr"), getImage: this._createOptionGetter("nodes.imageUrlExpr"), setImage: this._createOptionSetter("nodes.imageUrlExpr"), getLeft: this._createOptionGetter("nodes.leftExpr"), setLeft: this._createOptionSetter("nodes.leftExpr"), getTop: this._createOptionGetter("nodes.topExpr"), setTop: this._createOptionSetter("nodes.topExpr"), getWidth: this._createOptionGetter("nodes.widthExpr"), setWidth: this._createOptionSetter("nodes.widthExpr"), getHeight: this._createOptionGetter("nodes.heightExpr"), setHeight: this._createOptionSetter("nodes.heightExpr"), getParentKey: this._createOptionGetter("nodes.parentKeyExpr"), setParentKey: this._createOptionSetter("nodes.parentKeyExpr"), getItems: this._createOptionGetter("nodes.itemsExpr"), setItems: this._createOptionSetter("nodes.itemsExpr"), getChildren: containerChildrenGetter = this._createOptionGetter("nodes.containerChildrenExpr"), setChildren: containerChildrenSetter = this._createOptionSetter("nodes.containerChildrenExpr"), getContainerKey: !containerChildrenGetter && !containerChildrenSetter && this._createOptionGetter("nodes.containerKeyExpr"), setContainerKey: !containerChildrenGetter && !containerChildrenSetter && this._createOptionSetter("nodes.containerKeyExpr") }, edgeDataImporter: { getKey: this._createOptionGetter("edges.keyExpr"), setKey: this._createOptionSetter("edges.keyExpr"), getCustomData: this._createOptionGetter("edges.customDataExpr"), setCustomData: this._createOptionSetter("edges.customDataExpr"), getLocked: this._createOptionGetter("edges.lockedExpr"), setLocked: this._createOptionSetter("edges.lockedExpr"), getStyle: this._createOptionGetter("edges.styleExpr"), setStyle: this._createOptionSetter("edges.styleExpr"), getStyleText: this._createOptionGetter("edges.textStyleExpr"), setStyleText: this._createOptionSetter("edges.textStyleExpr"), getZIndex: this._createOptionGetter("edges.zIndexExpr"), setZIndex: this._createOptionSetter("edges.zIndexExpr"), getFrom: this._createOptionGetter("edges.fromExpr"), setFrom: this._createOptionSetter("edges.fromExpr"), getFromPointIndex: this._createOptionGetter("edges.fromPointIndexExpr"), setFromPointIndex: this._createOptionSetter("edges.fromPointIndexExpr"), getTo: this._createOptionGetter("edges.toExpr"), setTo: this._createOptionSetter("edges.toExpr"), getToPointIndex: this._createOptionGetter("edges.toPointIndexExpr"), setToPointIndex: this._createOptionSetter("edges.toPointIndexExpr"), getPoints: this._createOptionGetter("edges.pointsExpr"), setPoints: this._createOptionSetter("edges.pointsExpr"), getText: this._createOptionGetter("edges.textExpr"), setText: this._createOptionSetter("edges.textExpr"), getLineOption: (lineOptionGetter = this._createOptionGetter("edges.lineTypeExpr")) && function(obj) { const lineType = lineOptionGetter(obj); return this._getConnectorLineOption(lineType) }.bind(this), setLineOption: (lineOptionSetter = this._createOptionSetter("edges.lineTypeExpr")) && function(obj, value) { switch (value) { case ConnectorLineOption.Straight: value = "straight"; break; case ConnectorLineOption.Orthogonal: value = "orthogonal" } lineOptionSetter(obj, value) }.bind(this), getStartLineEnding: (startLineEndingGetter = this._createOptionGetter("edges.fromLineEndExpr")) && function(obj) { const lineEnd = startLineEndingGetter(obj); return this._getConnectorLineEnding(lineEnd) }.bind(this), setStartLineEnding: (startLineEndingSetter = this._createOptionSetter("edges.fromLineEndExpr")) && function(obj, value) { switch (value) { case ConnectorLineEnding.Arrow: value = "arrow"; break; case ConnectorLineEnding.OutlinedTriangle: value = "outlinedTriangle"; break; case ConnectorLineEnding.FilledTriangle: value = "filledTriangle"; break; case ConnectorLineEnding.None: value = "none" } startLineEndingSetter(obj, value) }.bind(this), getEndLineEnding: (endLineEndingGetter = this._createOptionGetter("edges.toLineEndExpr")) && function(obj) { const lineEnd = endLineEndingGetter(obj); return this._getConnectorLineEnding(lineEnd) }.bind(this), setEndLineEnding: (endLineEndingSetter = this._createOptionSetter("edges.toLineEndExpr")) && function(obj, value) { switch (value) { case ConnectorLineEnding.Arrow: value = "arrow"; break; case ConnectorLineEnding.OutlinedTriangle: value = "outlinedTriangle"; break; case ConnectorLineEnding.FilledTriangle: value = "filledTriangle"; break; case ConnectorLineEnding.None: value = "none" } endLineEndingSetter(obj, value) }.bind(this) }, layoutParameters: this._getDataBindingLayoutParameters() }; if (data.nodeDataSource) { this._executeDiagramCommand(DiagramCommand.BindDocument, data) } } _reloadContentByChanges(changes, isExternalChanges) { const keys = this._getChangesKeys(changes); const applyLayout = this._onRequestUpdateLayout(changes); this._reloadContent(keys, applyLayout, isExternalChanges) } _reloadContent(itemKeys, applyLayout, isExternalChanges) { this._diagramInstance.reloadContent(itemKeys, (() => { let nodeDataSource; let edgeDataSource; if (this._nodesOption && isExternalChanges) { nodeDataSource = this._nodesOption.getItems() } if (this._edgesOption && isExternalChanges) { edgeDataSource = this._edgesOption.getItems() } return { nodeDataSource: nodeDataSource, edgeDataSource: edgeDataSource } }), applyLayout && this._getDataBindingLayoutParameters(), isExternalChanges) } _getConnectorLineOption(lineType) { const { ConnectorLineOption: ConnectorLineOption } = getDiagram(); if ("straight" === lineType) { return ConnectorLineOption.Straight } else { return ConnectorLineOption.Orthogonal } } _getConnectorLineEnding(lineEnd) { const { ConnectorLineEnding: ConnectorLineEnding } = getDiagram(); switch (lineEnd) { case "arrow": return ConnectorLineEnding.Arrow; case "outlinedTriangle": return ConnectorLineEnding.OutlinedTriangle; case "filledTriangle": return ConnectorLineEnding.FilledTriangle; default: return ConnectorLineEnding.None } } _getDataBindingLayoutParameters() { const { DataLayoutType: DataLayoutType, DataLayoutOrientation: DataLayoutOrientation } = getDiagram(); const layoutParametersOption = this.option("nodes.autoLayout") || "off"; const layoutType = layoutParametersOption.type || layoutParametersOption; const parameters = {}; if ("off" !== layoutType && ("auto" !== layoutType || !this._hasNodePositionExprs())) { if ("tree" === layoutType) { parameters.type = DataLayoutType.Tree } else { parameters.type = DataLayoutType.Sugiyama } switch (layoutParametersOption.orientation) { case "vertical": parameters.orientation = DataLayoutOrientation.Vertical; break; case "horizontal": parameters.orientation = DataLayoutOrientation.Horizontal } if (this.option("edges.fromPointIndexExpr") || this.option("edges.toPointIndexExpr")) { parameters.skipPointIndices = true } } parameters.autoSizeEnabled = !!this.option("nodes.autoSizeEnabled"); return parameters } _hasNodePositionExprs() { return this.option("nodes.topExpr") && this.option("nodes.leftExpr") } _getAutoZoomValue(option) { const { AutoZoomMode: AutoZoomMode } = getDiagram(); switch (option) { case "fitContent": return AutoZoomMode.FitContent; case "fitWidth": return AutoZoomMode.FitToWidth; default: return AutoZoomMode.Disabled } } _isBindingMode() { return this._nodesOption && this._nodesOption.hasItems() || this._edgesOption && this._edgesOption.hasItems() } _beginUpdateDiagram() { this._updateDiagramLockCount++ } _endUpdateDiagram() { this._updateDiagramLockCount = Math.max(this._updateDiagramLockCount - 1, 0); if (!this._updateDiagramLockCount) { this._bindDiagramData() } } _getCustomShapes() { return this.option("customShapes") || [] } _getToolboxGroups() { return DiagramToolboxManager.getGroups(this.option("toolbox.groups")) } _updateAllCustomShapes() { this._diagramInstance.removeAllCustomShapes(); this._updateCustomShapes(this._getCustomShapes()) } _updateCustomShapes(customShapes, prevCustomShapes) { if (Array.isArray(prevCustomShapes)) { this._diagramInstance.removeCustomShapes(prevCustomShapes.map((s => s.type))) } if (Array.isArray(customShapes)) { this._diagramInstance.addCustomShapes(customShapes.map((s => { const templateOption = s.template || this.option("customShapeTemplate"); const template = templateOption && this._getTemplate(templateOption); const toolboxTemplateOption = s.toolboxTemplate || this.option("customShapeToolboxTemplate"); const toolboxTemplate = toolboxTemplateOption && this._getTemplate(toolboxTemplateOption); return { category: s.category, type: s.type, baseType: s.baseType, title: s.title, svgUrl: s.backgroundImageUrl, svgToolboxUrl: s.backgroundImageToolboxUrl, svgLeft: s.backgroundImageLeft, svgTop: s.backgroundImageTop, svgWidth: s.backgroundImageWidth, svgHeight: s.backgroundImageHeight, defaultWidth: s.d