var BaseNode = require("./BaseNode"); var CenteringTextElement = require("../../util/CenteringTextElement"); var drawTools = require("../drawTools")(); module.exports = (function (){ var o = function ( graph ){ BaseNode.apply(this, arguments); var that = this, collapsible = false, radius = 50, collapsingGroupElement, pinGroupElement, haloGroupElement = null, rectangularRepresentation = false, renderingElement, textBlock; this.setRectangularRepresentation = function ( val ){ rectangularRepresentation = val; }; this.getRectangularRepresentation = function (){ return rectangularRepresentation; }; this.getHalos = function (){ return haloGroupElement; }; // Properties this.collapsible = function ( p ){ if ( !arguments.length ) return collapsible; collapsible = p; return this; }; this.textBlock = function ( p ){ if ( !arguments.length ) return textBlock; textBlock = p; return this; }; /** * This might not be equal to the actual radius, because the instance count is used for its calculation. * @param p * @returns {*} */ this.radius = function ( p ){ if ( !arguments.length ) return radius; radius = p; return this; }; // Functions this.setHoverHighlighting = function ( enable ){ that.nodeElement().selectAll("circle").classed("hovered", enable); }; this.textWidth = function ( yOffset ){ var availableWidth = this.actualRadius() * 2; // if the text is not placed in the center of the circle, it can't have the full width if ( yOffset ) { var relativeOffset = Math.abs(yOffset) / this.actualRadius(); var isOffsetInsideOfNode = relativeOffset <= 1; if ( isOffsetInsideOfNode ) { availableWidth = Math.cos(relativeOffset) * availableWidth; } else { availableWidth = 0; } } return availableWidth; }; this.toggleFocus = function (){ that.focused(!that.focused()); if ( that.nodeElement() ) that.nodeElement().select("circle").classed("focused", that.focused()); graph.resetSearchHighlight(); graph.options().searchMenu().clearText(); }; this.actualRadius = function (){ if ( !graph.options().scaleNodesByIndividuals() || that.individuals().length <= 0 ) { return that.radius(); } else { // we could "listen" for radius and maxIndividualCount changes, but this is easier var MULTIPLIER = 8, additionalRadius = Math.log(that.individuals().length + 1) * MULTIPLIER + 5; return that.radius() + additionalRadius; } }; this.distanceToBorder = function (){ return that.actualRadius(); }; this.removeHalo = function (){ if ( that.halo() ) { that.halo(false); if ( haloGroupElement ) { haloGroupElement.remove(); } } }; this.drawHalo = function ( pulseAnimation ){ that.halo(true); if ( rectangularRepresentation === true ) { haloGroupElement = drawTools.drawRectHalo(that.nodeElement(), 80, 80, 5); } else { haloGroupElement = drawTools.drawHalo(that.nodeElement(), that.actualRadius(), this.removeHalo); } if ( pulseAnimation === false ) { var pulseItem = haloGroupElement.selectAll(".searchResultA"); pulseItem.classed("searchResultA", false); pulseItem.classed("searchResultB", true); pulseItem.attr("animationRunning", false); } }; /** * Draws the pin on a round node on a position depending on its radius. */ this.drawPin = function (){ that.pinned(true); var dx = (-3.5 / 5) * that.actualRadius(), dy = (-7 / 10) * that.actualRadius(); pinGroupElement = drawTools.drawPin(that.nodeElement(), dx, dy, this.removePin, graph.options().showDraggerObject, graph.options().useAccuracyHelper()); }; /** * Removes the pin and refreshs the graph to update the force layout. */ this.removePin = function (){ that.pinned(false); if ( pinGroupElement ) { pinGroupElement.remove(); } graph.updateStyle(); }; this.drawCollapsingButton = function (){ collapsingGroupElement = that.nodeElement() .append("g") .classed("hidden-in-export", true) .attr("transform", function (){ var dx = (-2 / 5) * that.actualRadius(), dy = (1 / 2) * that.actualRadius(); return "translate(" + dx + "," + dy + ")"; }); collapsingGroupElement.append("rect") .classed("class pin feature", true) .attr("x", 0) .attr("y", 0) .attr("width", 40) .attr("height", 24); collapsingGroupElement.append("line") .attr("x1", 13) .attr("y1", 12) .attr("x2", 27) .attr("y2", 12); collapsingGroupElement.append("line") .attr("x1", 20) .attr("y1", 6) .attr("x2", 20) .attr("y2", 18); }; /** * Draws a circular node. * @param parentElement the element to which this node will be appended * @param [additionalCssClasses] additional css classes */ this.draw = function ( parentElement, additionalCssClasses ){ var cssClasses = that.collectCssClasses(); that.nodeElement(parentElement); var bgColor = that.backgroundColor(); if ( bgColor === null ) bgColor = undefined; if ( that.attributes().indexOf("deprecated") > -1 ) { bgColor = undefined; } if ( additionalCssClasses instanceof Array ) { cssClasses = cssClasses.concat(additionalCssClasses); } if ( rectangularRepresentation === true ) { renderingElement = drawTools.appendRectangularClass(parentElement, 80, 80, cssClasses, that.labelForCurrentLanguage(), bgColor); } else { renderingElement = drawTools.appendCircularClass(parentElement, that.actualRadius(), cssClasses, that.labelForCurrentLanguage(), bgColor); } that.postDrawActions(parentElement); }; this.redrawElement = function (){ renderingElement.remove(); textBlock.remove(); var bgColor = that.backgroundColor(); if ( that.attributes().indexOf("deprecated") > -1 ) { bgColor = undefined; } var cssClasses = that.collectCssClasses(); if ( rectangularRepresentation === true ) { renderingElement = drawTools.appendRectangularClass(that.nodeElement(), 80, 80, cssClasses, that.labelForCurrentLanguage(), bgColor); } else { renderingElement = drawTools.appendCircularClass(that.nodeElement(), that.actualRadius(), cssClasses, that.labelForCurrentLanguage(), bgColor); } that.postDrawActions(that.nodeElement()); }; /** * Common actions that should be invoked after drawing a node. */ this.postDrawActions = function (){ that.textBlock(createTextBlock()); that.addMouseListeners(); if ( that.pinned() ) { that.drawPin(); } if ( that.halo() ) { that.drawHalo(false); } if ( that.collapsible() ) { that.drawCollapsingButton(); } }; this.redrawLabelText = function (){ that.textBlock().remove(); that.textBlock(createTextBlock()); renderingElement.select("title").text(that.labelForCurrentLanguage()); }; function createTextBlock(){ var bgColor = that.backgroundColor(); if ( that.attributes().indexOf("deprecated") > -1 ) bgColor = undefined; var textBlock = new CenteringTextElement(that.nodeElement(), bgColor); var equivalentsString = that.equivalentsString(); var suffixForFollowingEquivalents = equivalentsString ? "," : ""; textBlock.addText(that.labelForCurrentLanguage(), "", suffixForFollowingEquivalents); textBlock.addEquivalents(equivalentsString); if ( !graph.options().compactNotation() ) { textBlock.addSubText(that.indicationString()); } textBlock.addInstanceCount(that.individuals().length); return textBlock; } this.equivalentsString = function (){ var equivalentClasses = that.equivalents(); if ( !equivalentClasses ) { return; } return equivalentClasses .map(function ( node ){ return node.labelForCurrentLanguage(); }) .join(", "); }; }; o.prototype = Object.create(BaseNode.prototype); o.prototype.constructor = o; return o; }());