123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- /**
- * Licensed Materials - Property of IBM IBM Cognos Products: Modeling UI (C) Copyright IBM Corp. 2015, 2020
- * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP
- * Schedule Contract with IBM Corp.
- */
- define([
- 'jquery',
- 'underscore',
- 'bi/commons/ui/core/Class',
- 'bi/commons/utils/Utils',
- '../ui/JoinPopupView',
- './StringUtils',
- './NodeStylesUtils',
- './utils',
- 'bi/commons/utils/BidiUtil',
- '../../StringResourcesBridge',
- ], function($, _, Class, CommonUtils, JoinPopupView, StringUtils, NodeStylesUtils, utils, BidiUtil, resources) {
- var d3;
- var DiagramContentProvider = Class.extend({
- CommonUtils: CommonUtils,
- init: function(options) {
- DiagramContentProvider.inherited('init', this);
- _.extend(this, options);
- d3 = options.d3;
- this._dblClicked = false;
- },
- setModel: function(model) {
- this._model = model;
- },
- getModel: function(){
- return this._model;
- },
- /**
- * Will draw links in the diagram based on this definition
- *
- * @param {array}
- * links - A d3 array of links in a force diagram
- */
- drawLinks: function(SVGContext, links) {
- var d3Link = SVGContext.diagramContainer.selectAll('.mui-diagram-link')
- .data(links, function(d) {
- return [d.identifier, d.instanceType, d.isValid, d.isLeftMincardZero, d.isRightMincardZero, d.sourceCardinality, d.targetCardinality];
- });
- // exit old links
- d3Link.exit().remove();
- var link = d3Link.enter().insert('g').attr(
- 'class', function(d) {
- var referenceClass = d.instanceType === 'reference' ? 'mui-reference-link ' : '';
- return 'mui-diagram-link ' + referenceClass + d.source.identifier + ' ' + d.target.identifier + ' ' + d.identifier;
- });
- link.append('line').attr('class', 'mui-diagram-link-interact-line');
- link.append('line').attr('class', 'mui-diagram-link-display-line');
- // validation icon
- var validationIconGroup = utils.d3Attrs(link.append('g'), NodeStylesUtils.diagramNodeStyles.validationLinkGroupIconStyle).on('click', function(data) {
- d3.event.stopPropagation();
- if (data.validationCallback) {
- data.validationCallback(d3.event);
- }
- });
- utils.d3Attrs(validationIconGroup.append('use'), NodeStylesUtils.diagramNodeStyles.validationLinkIconStyle);
- utils.d3Attrs(validationIconGroup.append('circle'), NodeStylesUtils.diagramNodeStyles.validationLinkCircleStyle);
- // group for link cardinality
- var linkCardinality = link.append('g').attr('class', 'mui-diagram-cardinality');
- this._drawLinkCardinalityNotation(linkCardinality, true);
- this._drawLinkCardinalityNotation(linkCardinality, false);
- this._attachLinkActions(link);
- // update links
- this._updateLinkProperties(link);
- this.animateLinkTick(link);
- return link.merge(d3Link);
- },
- /**
- * Update any existing properties on the links
- *
- * @param {array}
- * links - A d3 array of links in a force diagram
- */
- _updateLinkProperties: function(links) {
- links.each(function(d) {
- var node = d3.select(this);
- var representedLink = d.linksRepresented && d.linksRepresented[0] || {};
- // update cardinality source and target labels
- node.select('.mui-diagram-cardinality-source').classed('hidden', d.hideCardinality).classed('zero', representedLink.isLeftMincardZero).select('text')
- .text(d.sourceCardinality);
- node.select('.mui-diagram-cardinality-target').classed('hidden', d.hideCardinality).classed('zero', representedLink.isRightMincardZero).select('text')
- .text(d.targetCardinality);
- });
- },
- /**
- * A private method for styling cardinality markers
- * @param {SVG} linkGroup - an SVG element representing a link
- * @param {boolean} isSource - Indicates which end of the link a cardinality value belongs to ('true' is the source, 'false' is the target)
- */
- _drawLinkCardinalityNotation: function(linkGroup, isSource) {
- // group for cardinality
- var cardinalityGroup = linkGroup.append('g').attr('class', isSource ? 'mui-diagram-cardinality-source' : 'mui-diagram-cardinality-target');
- // rect styled as a circle
- // ... need to add the attributes directly on the node
- // as not all SVG element attributes can be styled with CSS
- // see: http://www.w3.org/TR/SVG/propidx.html
- utils.d3Attrs(cardinalityGroup.append('rect'), NodeStylesUtils.CardinalityStyle.boxStyle);
- // the cardinality relation text
- utils.d3Attrs(cardinalityGroup.append('text'), NodeStylesUtils.CardinalityStyle.textStyle);
- },
- /**
- * Will draw nodes in the diagram based on this definition
- *
- * @param {array}
- * d3Nodes - A d3 array of nodes in a force diagram
- */
- drawNodes: function(SVGContext, nodes) {
- var d3Node = SVGContext.diagramContainer.selectAll('.mui-diagram-node')
- .data(nodes, function(d) {
- return [d.tableRowCount, d.displayTableInfo, d.identifier, d.label, d.screenTip, d.filter, d.hidden, d.instanceType, d.isValid];
- });
- d3Node.exit().remove();
- var box = d3Node.enter().insert('g').attr(
- 'class', function(d) {
- return 'mui-diagram-node ' + d.identifier;
- });
- box.append('rect').attr('class', 'mui-diagram-node-box').append('title').text(function (d) {
- return BidiUtil.enforceTextDirection(d.screenTip ? (d.screenTip + ' : ' + d.label) : d.label);
- });
- var boxContent = box.append('g').attr('class', function(d) {
- return 'mui-diagram-node-content ' + d.identifier;
- });
- var validationIconGroup = boxContent.append('g').on('mousedown', function (data) {
- d3.event.stopPropagation();
- if (data.validationCallback) {
- data.validationCallback(d3.event);
- }
- });
- utils.d3Attrs(validationIconGroup.append('use'), NodeStylesUtils.diagramNodeStyles.validationNodeIconStyle);
- utils.d3Attrs(validationIconGroup.append('circle'), NodeStylesUtils.diagramNodeStyles.validationNodeCircleStyle);
- // text
- var self = this;
- boxContent.append('text').text(function(d) {
- var isIconPresent = this.__data__.filter || d.instanceType === 'reference' || d.instanceType === 'package' || !d.isValid;
- return BidiUtil.enforceTextDirection(StringUtils.shortenTextMidEllipsis(d.label, StringUtils.getMaxDiagramLabelLength(isIconPresent, d.label)));
- });
- // filter icon
- boxContent.append('use').attr('class', 'mui-diagram-filter');
- // reference icon
- boxContent.append('use').attr('class', 'mui-daigram-reference');
- // table details
- var boxTableDetailsContent = box.append('g').attr('class', function(d) {
- return 'mui-diagram-table-details ' + d.identifier;
- });
- boxTableDetailsContent.append('rect');
- boxTableDetailsContent.append('text').text(function(d) {
- return BidiUtil.enforceTextDirection(d.statisticsLoading ? resources.get('moduleRowCount', { rowCount: '--' }) : d.tableRowCount);
- });
- // style it
- this.styleNode(box);
- this._attachNodeActions(box);
- this.animateNodeTick(box);
- return box.merge(d3Node);
- },
- styleNode: function(box) {
- // rect
- utils.d3Attrs(box.select('.mui-diagram-node-box'), NodeStylesUtils.diagramNodeStyles.boxStyle);
- utils.d3Styles(box.select('.mui-diagram-node-box'), NodeStylesUtils.diagramNodeStyles.boxColorStyle);
- // text
- utils.d3Attrs(box.select('.mui-diagram-node-content').select('text'), NodeStylesUtils.diagramNodeStyles.textStyle);
- // filter icon
- utils.d3Attrs(box.select('.mui-diagram-filter'), NodeStylesUtils.diagramNodeStyles.filterIconStyle);
- // reference icon
- utils.d3Attrs(box.select('.mui-diagram-reference'), NodeStylesUtils.diagramNodeStyles.referenceIconStyle);
- // table details box
- utils.d3Attrs(box.select('.mui-diagram-table-details').select('rect'), NodeStylesUtils.diagramNodeStyles.tableDetailsBoxStyle);
- utils.d3Attrs(box.select('.mui-diagram-table-details').select('text'), NodeStylesUtils.diagramNodeStyles.tableDetailsTextStyle);
- },
- styleLink: function(box) {
- box.classed('mui-reference-link', function(d) {
- return d.instanceType === 'reference';
- });
- },
- _attachLinkActions: function(links) {
- var currentModel = this._model;
- var position = {};
- var handle;
- links.on(
- 'contextmenu', function(selectedLink, i, g){
- var event = d3.event;
- event.preventDefault();
- if (!utils.isOffset(position, {x: event.clientX, y: event.clientY})) {
- this.diagramStore.showContextMenu(event.pageX, event.pageY, [selectedLink.moserObject]);
- }
- return false;
- }.bind(this));
- links.on(
- 'click', function(selectedObjects, i, g){
- var event = d3.event;
- event.stopPropagation();
- this.diagramStore.hideContextMenu();
- setTimeout(function() {
- if (this._dblClicked) {
- return;
- }
- $(this.el).find('.mui_JoinPopup ').remove();
- var rect = this.el.getBoundingClientRect();
- var popupView = new JoinPopupView({
- relations: selectedObjects.linksRepresented,
- source: selectedObjects.source,
- target: selectedObjects.target,
- position: [event.clientX - rect.left, event.clientY - rect.top],
- $el: $(this.el),
- model: currentModel
- });
- popupView.render();
- }.bind(this), 300);
- }.bind(this));
- links.on(
- 'dblclick', function(selectedLink, i, g) {
- var event = d3.event;
- this._dblClicked = true;
- event.stopPropagation();
- this.diagramStore.editRelationship(selectedLink.moserObject).then(function() {
- this._dblClicked = false;
- }.bind(this));
- return false;
- }.bind(this));
- },
- handleDiagramNodeSelection: function(e){
- var isMultiSelect = this.CommonUtils.isControlKey(d3.event);
- var isRightClick = (d3.event.button === 2);
- var target = d3.select(d3.event.currentTarget);
- var targetIsSelected = target.classed('mui-diagram-selected');
- var targetIdentifier = e.identifier;
- var targetMoserObj = e.moserObject;
- if (isMultiSelect) {
- //don't de-select on a right click -> context menu will appear over unselected node
- if (!isRightClick || !targetIsSelected) {
- d3.select(d3.event.target.parentNode).classed('mui-diagram-selected', function(){
- return !d3.select(this).classed('mui-diagram-selected');
- });
- }
- if (target.classed('mui-diagram-selected')) {
- this.diagramStore.appendToSelection(targetMoserObj);
- } else {
- this.diagramStore.removeFromSelection(targetMoserObj);
- }
- //single click
- } else {
- //On sinle-click, deselect all other nodes than the target node.
- //on a right click where the target is already selected, do nothing
- if(!isRightClick || !targetIsSelected) {
- d3.select(d3.event.target.parentNode).classed('mui-diagram-selected', true);
- this.diagramStore.setSelection([targetMoserObj]);
- }
- }
- },
- /**
- * Defines action handlers for specific inputs and attaches them to actions on a node
- *
- * @param {array}
- * nodes - An array of d3 nodes in the diagram
- */
- _attachNodeActions: function(nodes) {
- var position = {};
- nodes.on(
- 'contextmenu', function(selectedNode, i, g) {
- var event = d3.event;
- event.preventDefault();
- if (!utils.isOffset(position, {x: event.clientX, y: event.clientY})) {
- var rect = this.el.getBoundingClientRect();
- this.handleDiagramNodeSelection(event.currentTarget.__data__);
- this.diagramStore.showContextMenu(event.pageX, event.pageY, undefined, {
- x: event.pageX - rect.left,
- y: event.pageY - rect.top
- }, selectedNode.moserObject);
- }
- return false;
- }.bind(this));
- nodes.on(
- 'click', function(selectedNode, i, g) {
- var event = d3.event;
- event.stopPropagation();
- !utils.isOffset(position, {x: event.clientX, y: event.clientY}) && this.handleDiagramNodeSelection(event.currentTarget.__data__);
- }.bind(this));
- nodes.on(
- 'mousedown', function(selectedNode, i, g) {
- var event = d3.event;
- position.x = event.clientX;
- position.y = event.clientY;
- });
- },
- /**
- * Defines how to animate links on a d3 'tick' event
- *
- * @param {array}
- * link - A d3 array of links in the diagram view
- */
- animateLinkTick: function(link) {
- var lineDefinition = {
- x1: function(linkData) {
- return (linkData.source.x);
- },
- x2: function(linkData) {
- return (linkData.target.x);
- },
- y1: function(linkData) {
- var tableDetailsOffset = NodeStylesUtils.diagramNodeStyles.tableDetailsBoxStyle.height / 2,
- source = linkData.source;
- return (source.y + (source.displayTableInfo ? tableDetailsOffset : 0));
- },
- y2: function(linkData) {
- var tableDetailsOffset = NodeStylesUtils.diagramNodeStyles.tableDetailsBoxStyle.height / 2,
- target = linkData.target;
- return (target.y + (target.displayTableInfo ? tableDetailsOffset : 0));
- }
- };
- utils.d3Attrs(link.select('.mui-diagram-link-display-line'), lineDefinition);
- utils.d3Attrs(link.select('.mui-diagram-link-interact-line'), lineDefinition);
- link.select('.mui_validationIcon').attr('transform', function(d) {
- return 'translate('+ (((d.source.x + d.target.x ) / 2 ) - 6) + ',' + ((( d.source.y + d.target.y ) / 2 ) - 6) + ')';
- });
- link.select('.mui-diagram-cardinality-source').attr('transform', function(linkData) {
- var coords = utils.getCardinalityPosition(linkData, 'source');
- return 'translate(' + coords.x + ',' + coords.y + ')';
- });
- link.select('.mui-diagram-cardinality-target').attr('transform', function(linkData) {
- var coords = utils.getCardinalityPosition(linkData, 'target');
- return 'translate(' + coords.x + ',' + coords.y + ')';
- });
- return link;
- },
- /**
- * Defines how to animate nodes on a d3 'tick' event
- *
- * @param {array}
- * node - A d3 array of nodes in the diagram view
- */
- animateNodeTick: function(node) {
- return node.attr('transform', function(d) {
- return 'translate(' + d.x + ', ' + d.y + ')';
- });
- }
- });
- return DiagramContentProvider;
- });
|