1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639 |
- /**
- * Copyright (c) 2006-2017, JGraph Ltd
- * Copyright (c) 2006-2017, Gaudenz Alder
- */
- /**
- * Class: mxCellRenderer
- *
- * Renders cells into a document object model. The <defaultShapes> is a global
- * map of shapename, constructor pairs that is used in all instances. You can
- * get a list of all available shape names using the following code.
- *
- * In general the cell renderer is in charge of creating, redrawing and
- * destroying the shape and label associated with a cell state, as well as
- * some other graphical objects, namely controls and overlays. The shape
- * hieararchy in the display (ie. the hierarchy in which the DOM nodes
- * appear in the document) does not reflect the cell hierarchy. The shapes
- * are a (flat) sequence of shapes and labels inside the draw pane of the
- * graph view, with some exceptions, namely the HTML labels being placed
- * directly inside the graph container for certain browsers.
- *
- * (code)
- * mxLog.show();
- * for (var i in mxCellRenderer.defaultShapes)
- * {
- * mxLog.debug(i);
- * }
- * (end)
- *
- * Constructor: mxCellRenderer
- *
- * Constructs a new cell renderer with the following built-in shapes:
- * arrow, rectangle, ellipse, rhombus, image, line, label, cylinder,
- * swimlane, connector, actor and cloud.
- */
- function mxCellRenderer() { };
- /**
- * Variable: defaultShapes
- *
- * Static array that contains the globally registered shapes which are
- * known to all instances of this class. For adding new shapes you should
- * use the static <mxCellRenderer.registerShape> function.
- */
- mxCellRenderer.defaultShapes = new Object();
- /**
- * Variable: defaultEdgeShape
- *
- * Defines the default shape for edges. Default is <mxConnector>.
- */
- mxCellRenderer.prototype.defaultEdgeShape = mxConnector;
- /**
- * Variable: defaultVertexShape
- *
- * Defines the default shape for vertices. Default is <mxRectangleShape>.
- */
- mxCellRenderer.prototype.defaultVertexShape = mxRectangleShape;
- /**
- * Variable: defaultTextShape
- *
- * Defines the default shape for labels. Default is <mxText>.
- */
- mxCellRenderer.prototype.defaultTextShape = mxText;
- /**
- * Variable: legacyControlPosition
- *
- * Specifies if the folding icon should ignore the horizontal
- * orientation of a swimlane. Default is true.
- */
- mxCellRenderer.prototype.legacyControlPosition = true;
- /**
- * Variable: legacySpacing
- *
- * Specifies if spacing and label position should be ignored if overflow is
- * fill or width. Default is true for backwards compatiblity.
- */
- mxCellRenderer.prototype.legacySpacing = true;
- /**
- * Variable: antiAlias
- *
- * Anti-aliasing option for new shapes. Default is true.
- */
- mxCellRenderer.prototype.antiAlias = true;
- /**
- * Variable: minSvgStrokeWidth
- *
- * Minimum stroke width for SVG output.
- */
- mxCellRenderer.prototype.minSvgStrokeWidth = 1;
- /**
- * Variable: forceControlClickHandler
- *
- * Specifies if the enabled state of the graph should be ignored in the control
- * click handler (to allow folding in disabled graphs). Default is false.
- */
- mxCellRenderer.prototype.forceControlClickHandler = false;
- /**
- * Function: registerShape
- *
- * Registers the given constructor under the specified key in this instance
- * of the renderer.
- *
- * Example:
- *
- * (code)
- * mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE, mxRectangleShape);
- * (end)
- *
- * Parameters:
- *
- * key - String representing the shape name.
- * shape - Constructor of the <mxShape> subclass.
- */
- mxCellRenderer.registerShape = function(key, shape)
- {
- mxCellRenderer.defaultShapes[key] = shape;
- };
- // Adds default shapes into the default shapes array
- mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE, mxRectangleShape);
- mxCellRenderer.registerShape(mxConstants.SHAPE_ELLIPSE, mxEllipse);
- mxCellRenderer.registerShape(mxConstants.SHAPE_RHOMBUS, mxRhombus);
- mxCellRenderer.registerShape(mxConstants.SHAPE_CYLINDER, mxCylinder);
- mxCellRenderer.registerShape(mxConstants.SHAPE_CONNECTOR, mxConnector);
- mxCellRenderer.registerShape(mxConstants.SHAPE_ACTOR, mxActor);
- mxCellRenderer.registerShape(mxConstants.SHAPE_TRIANGLE, mxTriangle);
- mxCellRenderer.registerShape(mxConstants.SHAPE_HEXAGON, mxHexagon);
- mxCellRenderer.registerShape(mxConstants.SHAPE_CLOUD, mxCloud);
- mxCellRenderer.registerShape(mxConstants.SHAPE_LINE, mxLine);
- mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW, mxArrow);
- mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW_CONNECTOR, mxArrowConnector);
- mxCellRenderer.registerShape(mxConstants.SHAPE_DOUBLE_ELLIPSE, mxDoubleEllipse);
- mxCellRenderer.registerShape(mxConstants.SHAPE_SWIMLANE, mxSwimlane);
- mxCellRenderer.registerShape(mxConstants.SHAPE_IMAGE, mxImageShape);
- mxCellRenderer.registerShape(mxConstants.SHAPE_LABEL, mxLabel);
- /**
- * Function: initializeShape
- *
- * Initializes the shape in the given state by calling its init method with
- * the correct container after configuring it using <configureShape>.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the shape should be initialized.
- */
- mxCellRenderer.prototype.initializeShape = function(state)
- {
- state.shape.dialect = state.view.graph.dialect;
- this.configureShape(state);
- state.shape.init(state.view.getDrawPane());
- };
- /**
- * Function: createShape
- *
- * Creates and returns the shape for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the shape should be created.
- */
- mxCellRenderer.prototype.createShape = function(state)
- {
- var shape = null;
-
- if (state.style != null)
- {
- // Checks if there is a stencil for the name and creates
- // a shape instance for the stencil if one exists
- var stencil = mxStencilRegistry.getStencil(state.style[mxConstants.STYLE_SHAPE]);
-
- if (stencil != null)
- {
- shape = new mxShape(stencil);
- }
- else
- {
- var ctor = this.getShapeConstructor(state);
- shape = new ctor();
- }
- }
-
- return shape;
- };
- /**
- * Function: createIndicatorShape
- *
- * Creates the indicator shape for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the indicator shape should be created.
- */
- mxCellRenderer.prototype.createIndicatorShape = function(state)
- {
- state.shape.indicatorShape = this.getShape(state.view.graph.getIndicatorShape(state));
- };
- /**
- * Function: getShape
- *
- * Returns the shape for the given name from <defaultShapes>.
- */
- mxCellRenderer.prototype.getShape = function(name)
- {
- return (name != null) ? mxCellRenderer.defaultShapes[name] : null;
- };
- /**
- * Function: getShapeConstructor
- *
- * Returns the constructor to be used for creating the shape.
- */
- mxCellRenderer.prototype.getShapeConstructor = function(state)
- {
- var ctor = this.getShape(state.style[mxConstants.STYLE_SHAPE]);
-
- if (ctor == null)
- {
- ctor = (state.view.graph.getModel().isEdge(state.cell)) ?
- this.defaultEdgeShape : this.defaultVertexShape;
- }
-
- return ctor;
- };
- /**
- * Function: configureShape
- *
- * Configures the shape for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the shape should be configured.
- */
- mxCellRenderer.prototype.configureShape = function(state)
- {
- state.shape.apply(state);
- state.shape.image = state.view.graph.getImage(state);
- state.shape.indicatorColor = state.view.graph.getIndicatorColor(state);
- state.shape.indicatorStrokeColor = state.style[mxConstants.STYLE_INDICATOR_STROKECOLOR];
- state.shape.indicatorGradientColor = state.view.graph.getIndicatorGradientColor(state);
- state.shape.indicatorDirection = state.style[mxConstants.STYLE_INDICATOR_DIRECTION];
- state.shape.indicatorImage = state.view.graph.getIndicatorImage(state);
- this.postConfigureShape(state);
- };
- /**
- * Function: postConfigureShape
- *
- * Replaces any reserved words used for attributes, eg. inherit,
- * indicated or swimlane for colors in the shape for the given state.
- * This implementation resolves these keywords on the fill, stroke
- * and gradient color keys.
- */
- mxCellRenderer.prototype.postConfigureShape = function(state)
- {
- if (state.shape != null)
- {
- this.resolveColor(state, 'indicatorGradientColor', mxConstants.STYLE_GRADIENTCOLOR);
- this.resolveColor(state, 'indicatorColor', mxConstants.STYLE_FILLCOLOR);
- this.resolveColor(state, 'gradient', mxConstants.STYLE_GRADIENTCOLOR);
- this.resolveColor(state, 'stroke', mxConstants.STYLE_STROKECOLOR);
- this.resolveColor(state, 'fill', mxConstants.STYLE_FILLCOLOR);
- }
- };
- /**
- * Function: checkPlaceholderStyles
- *
- * Checks if the style of the given <mxCellState> contains 'inherit',
- * 'indicated' or 'swimlane' for colors that support those keywords.
- */
- mxCellRenderer.prototype.checkPlaceholderStyles = function(state)
- {
- // LATER: Check if the color has actually changed
- if (state.style != null)
- {
- var values = ['inherit', 'swimlane', 'indicated'];
- var styles = [mxConstants.STYLE_FILLCOLOR, mxConstants.STYLE_STROKECOLOR,
- mxConstants.STYLE_GRADIENTCOLOR, mxConstants.STYLE_FONTCOLOR];
-
- for (var i = 0; i < styles.length; i++)
- {
- if (mxUtils.indexOf(values, state.style[styles[i]]) >= 0)
- {
- return true;
- }
- }
- }
-
- return false;
- };
- /**
- * Function: resolveColor
- *
- * Resolves special keywords 'inherit', 'indicated' and 'swimlane' and sets
- * the respective color on the shape.
- */
- mxCellRenderer.prototype.resolveColor = function(state, field, key)
- {
- var shape = (key == mxConstants.STYLE_FONTCOLOR) ?
- state.text : state.shape;
-
- if (shape != null)
- {
- var graph = state.view.graph;
- var value = shape[field];
- var referenced = null;
-
- if (value == 'inherit')
- {
- referenced = graph.model.getParent(state.cell);
- }
- else if (value == 'swimlane')
- {
- shape[field] = (key == mxConstants.STYLE_STROKECOLOR ||
- key == mxConstants.STYLE_FONTCOLOR) ?
- '#000000' : '#ffffff';
-
- if (graph.model.getTerminal(state.cell, false) != null)
- {
- referenced = graph.model.getTerminal(state.cell, false);
- }
- else
- {
- referenced = state.cell;
- }
-
- referenced = graph.getSwimlane(referenced);
- key = graph.swimlaneIndicatorColorAttribute;
- }
- else if (value == 'indicated' && state.shape != null)
- {
- shape[field] = state.shape.indicatorColor;
- }
- else if (key != mxConstants.STYLE_FILLCOLOR &&
- value == mxConstants.STYLE_FILLCOLOR &&
- state.shape != null)
- {
- shape[field] = state.style[mxConstants.STYLE_FILLCOLOR];
- }
- else if (key != mxConstants.STYLE_STROKECOLOR &&
- value == mxConstants.STYLE_STROKECOLOR &&
- state.shape != null)
- {
- shape[field] = state.style[mxConstants.STYLE_STROKECOLOR];
- }
-
- if (referenced != null)
- {
- var rstate = graph.getView().getState(referenced);
- shape[field] = null;
-
- if (rstate != null)
- {
- var rshape = (key == mxConstants.STYLE_FONTCOLOR) ? rstate.text : rstate.shape;
-
- if (rshape != null && field != 'indicatorColor')
- {
- shape[field] = rshape[field];
- }
- else
- {
- shape[field] = rstate.style[key];
- }
- }
- }
- }
- };
- /**
- * Function: getLabelValue
- *
- * Returns the value to be used for the label.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the label should be created.
- */
- mxCellRenderer.prototype.getLabelValue = function(state)
- {
- return state.view.graph.getLabel(state.cell);
- };
- /**
- * Function: createLabel
- *
- * Creates the label for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the label should be created.
- */
- mxCellRenderer.prototype.createLabel = function(state, value)
- {
- var graph = state.view.graph;
- var isEdge = graph.getModel().isEdge(state.cell);
-
- if (state.style[mxConstants.STYLE_FONTSIZE] > 0 || state.style[mxConstants.STYLE_FONTSIZE] == null)
- {
- // Avoids using DOM node for empty labels
- var isForceHtml = (graph.isHtmlLabel(state.cell) || (value != null && mxUtils.isNode(value)));
- state.text = new this.defaultTextShape(value, new mxRectangle(),
- (state.style[mxConstants.STYLE_ALIGN] || mxConstants.ALIGN_CENTER),
- graph.getVerticalAlign(state),
- state.style[mxConstants.STYLE_FONTCOLOR],
- state.style[mxConstants.STYLE_FONTFAMILY],
- state.style[mxConstants.STYLE_FONTSIZE],
- state.style[mxConstants.STYLE_FONTSTYLE],
- state.style[mxConstants.STYLE_SPACING],
- state.style[mxConstants.STYLE_SPACING_TOP],
- state.style[mxConstants.STYLE_SPACING_RIGHT],
- state.style[mxConstants.STYLE_SPACING_BOTTOM],
- state.style[mxConstants.STYLE_SPACING_LEFT],
- state.style[mxConstants.STYLE_HORIZONTAL],
- state.style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR],
- state.style[mxConstants.STYLE_LABEL_BORDERCOLOR],
- graph.isWrapping(state.cell) && graph.isHtmlLabel(state.cell),
- graph.isLabelClipped(state.cell),
- state.style[mxConstants.STYLE_OVERFLOW],
- state.style[mxConstants.STYLE_LABEL_PADDING],
- mxUtils.getValue(state.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION));
- state.text.opacity = mxUtils.getValue(state.style, mxConstants.STYLE_TEXT_OPACITY, 100);
- state.text.dialect = (isForceHtml) ? mxConstants.DIALECT_STRICTHTML : state.view.graph.dialect;
- state.text.style = state.style;
- state.text.state = state;
- this.initializeLabel(state, state.text);
-
- // Workaround for touch devices routing all events for a mouse gesture
- // (down, move, up) via the initial DOM node. IE additionally redirects
- // the event via the initial DOM node but the event source is the node
- // under the mouse, so we need to check if this is the case and force
- // getCellAt for the subsequent mouseMoves and the final mouseUp.
- var forceGetCell = false;
-
- var getState = function(evt)
- {
- var result = state;
- if (mxClient.IS_TOUCH || forceGetCell)
- {
- var x = mxEvent.getClientX(evt);
- var y = mxEvent.getClientY(evt);
-
- // Dispatches the drop event to the graph which
- // consumes and executes the source function
- var pt = mxUtils.convertPoint(graph.container, x, y);
- result = graph.view.getState(graph.getCellAt(pt.x, pt.y));
- }
-
- return result;
- };
-
- // TODO: Add handling for special touch device gestures
- mxEvent.addGestureListeners(state.text.node,
- mxUtils.bind(this, function(evt)
- {
- if (this.isLabelEvent(state, evt))
- {
- graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
- forceGetCell = graph.dialect != mxConstants.DIALECT_SVG &&
- mxEvent.getSource(evt).nodeName == 'IMG';
- }
- }),
- mxUtils.bind(this, function(evt)
- {
- if (this.isLabelEvent(state, evt))
- {
- graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
- }
- }),
- mxUtils.bind(this, function(evt)
- {
- if (this.isLabelEvent(state, evt))
- {
- graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
- forceGetCell = false;
- }
- })
- );
- // Uses double click timeout in mxGraph for quirks mode
- if (graph.nativeDblClickEnabled)
- {
- mxEvent.addListener(state.text.node, 'dblclick',
- mxUtils.bind(this, function(evt)
- {
- if (this.isLabelEvent(state, evt))
- {
- graph.dblClick(evt, state.cell);
- mxEvent.consume(evt);
- }
- })
- );
- }
- }
- };
- /**
- * Function: initializeLabel
- *
- * Initiailzes the label with a suitable container.
- *
- * Parameters:
- *
- * state - <mxCellState> whose label should be initialized.
- */
- mxCellRenderer.prototype.initializeLabel = function(state, shape)
- {
- if (mxClient.IS_SVG && mxClient.NO_FO && shape.dialect != mxConstants.DIALECT_SVG)
- {
- shape.init(state.view.graph.container);
- }
- else
- {
- shape.init(state.view.getDrawPane());
- }
- };
- /**
- * Function: createCellOverlays
- *
- * Creates the actual shape for showing the overlay for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the overlay should be created.
- */
- mxCellRenderer.prototype.createCellOverlays = function(state)
- {
- var graph = state.view.graph;
- var overlays = graph.getCellOverlays(state.cell);
- var dict = null;
-
- if (overlays != null)
- {
- dict = new mxDictionary();
-
- for (var i = 0; i < overlays.length; i++)
- {
- var shape = (state.overlays != null) ? state.overlays.remove(overlays[i]) : null;
-
- if (shape == null)
- {
- var tmp = new mxImageShape(new mxRectangle(), overlays[i].image.src);
- tmp.dialect = state.view.graph.dialect;
- tmp.preserveImageAspect = false;
- tmp.overlay = overlays[i];
- this.initializeOverlay(state, tmp);
- this.installCellOverlayListeners(state, overlays[i], tmp);
-
- if (overlays[i].cursor != null)
- {
- tmp.node.style.cursor = overlays[i].cursor;
- }
-
- dict.put(overlays[i], tmp);
- }
- else
- {
- dict.put(overlays[i], shape);
- }
- }
- }
-
- // Removes unused
- if (state.overlays != null)
- {
- state.overlays.visit(function(id, shape)
- {
- shape.destroy();
- });
- }
-
- state.overlays = dict;
- };
- /**
- * Function: initializeOverlay
- *
- * Initializes the given overlay.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the overlay should be created.
- * overlay - <mxImageShape> that represents the overlay.
- */
- mxCellRenderer.prototype.initializeOverlay = function(state, overlay)
- {
- overlay.init(state.view.getOverlayPane());
- };
- /**
- * Function: installOverlayListeners
- *
- * Installs the listeners for the given <mxCellState>, <mxCellOverlay> and
- * <mxShape> that represents the overlay.
- */
- mxCellRenderer.prototype.installCellOverlayListeners = function(state, overlay, shape)
- {
- var graph = state.view.graph;
-
- mxEvent.addListener(shape.node, 'click', function (evt)
- {
- if (graph.isEditing())
- {
- graph.stopEditing(!graph.isInvokesStopCellEditing());
- }
-
- overlay.fireEvent(new mxEventObject(mxEvent.CLICK,
- 'event', evt, 'cell', state.cell));
- });
-
- mxEvent.addGestureListeners(shape.node,
- function (evt)
- {
- mxEvent.consume(evt);
- },
- function (evt)
- {
- graph.fireMouseEvent(mxEvent.MOUSE_MOVE,
- new mxMouseEvent(evt, state));
- });
-
- if (mxClient.IS_TOUCH)
- {
- mxEvent.addListener(shape.node, 'touchend', function (evt)
- {
- overlay.fireEvent(new mxEventObject(mxEvent.CLICK,
- 'event', evt, 'cell', state.cell));
- });
- }
- };
- /**
- * Function: createControl
- *
- * Creates the control for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the control should be created.
- */
- mxCellRenderer.prototype.createControl = function(state)
- {
- var graph = state.view.graph;
- var image = graph.getFoldingImage(state);
-
- if (graph.foldingEnabled && image != null)
- {
- if (state.control == null)
- {
- var b = new mxRectangle(0, 0, image.width, image.height);
- state.control = new mxImageShape(b, image.src);
- state.control.preserveImageAspect = false;
- state.control.dialect = graph.dialect;
- this.initControl(state, state.control, true, this.createControlClickHandler(state));
- }
- }
- else if (state.control != null)
- {
- state.control.destroy();
- state.control = null;
- }
- };
- /**
- * Function: createControlClickHandler
- *
- * Hook for creating the click handler for the folding icon.
- *
- * Parameters:
- *
- * state - <mxCellState> whose control click handler should be returned.
- */
- mxCellRenderer.prototype.createControlClickHandler = function(state)
- {
- var graph = state.view.graph;
-
- return mxUtils.bind(this, function (evt)
- {
- if (this.forceControlClickHandler || graph.isEnabled())
- {
- var collapse = !graph.isCellCollapsed(state.cell);
- graph.foldCells(collapse, false, [state.cell], null, evt);
- mxEvent.consume(evt);
- }
- });
- };
- /**
- * Function: initControl
- *
- * Initializes the given control and returns the corresponding DOM node.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the control should be initialized.
- * control - <mxShape> to be initialized.
- * handleEvents - Boolean indicating if mousedown and mousemove should fire events via the graph.
- * clickHandler - Optional function to implement clicks on the control.
- */
- mxCellRenderer.prototype.initControl = function(state, control, handleEvents, clickHandler)
- {
- var graph = state.view.graph;
-
- // In the special case where the label is in HTML and the display is SVG the image
- // should go into the graph container directly in order to be clickable. Otherwise
- // it is obscured by the HTML label that overlaps the cell.
- var isForceHtml = graph.isHtmlLabel(state.cell) && mxClient.NO_FO &&
- graph.dialect == mxConstants.DIALECT_SVG;
- if (isForceHtml)
- {
- control.dialect = mxConstants.DIALECT_PREFERHTML;
- control.init(graph.container);
- control.node.style.zIndex = 1;
- }
- else
- {
- control.init(state.view.getOverlayPane());
- }
- var node = control.innerNode || control.node;
-
- // Workaround for missing click event on iOS is to check tolerance below
- if (clickHandler != null && !mxClient.IS_IOS)
- {
- if (graph.isEnabled())
- {
- node.style.cursor = 'pointer';
- }
-
- mxEvent.addListener(node, 'click', clickHandler);
- }
-
- if (handleEvents)
- {
- var first = null;
- mxEvent.addGestureListeners(node,
- function (evt)
- {
- first = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt));
- graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
- mxEvent.consume(evt);
- },
- function (evt)
- {
- graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, state));
- },
- function (evt)
- {
- graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, state));
- mxEvent.consume(evt);
- });
-
- // Uses capture phase for event interception to stop bubble phase
- if (clickHandler != null && mxClient.IS_IOS)
- {
- node.addEventListener('touchend', function(evt)
- {
- if (first != null)
- {
- var tol = graph.tolerance;
-
- if (Math.abs(first.x - mxEvent.getClientX(evt)) < tol &&
- Math.abs(first.y - mxEvent.getClientY(evt)) < tol)
- {
- clickHandler.call(clickHandler, evt);
- mxEvent.consume(evt);
- }
- }
- }, true);
- }
- }
-
- return node;
- };
- /**
- * Function: isShapeEvent
- *
- * Returns true if the event is for the shape of the given state. This
- * implementation always returns true.
- *
- * Parameters:
- *
- * state - <mxCellState> whose shape fired the event.
- * evt - Mouse event which was fired.
- */
- mxCellRenderer.prototype.isShapeEvent = function(state, evt)
- {
- return true;
- };
- /**
- * Function: isLabelEvent
- *
- * Returns true if the event is for the label of the given state. This
- * implementation always returns true.
- *
- * Parameters:
- *
- * state - <mxCellState> whose label fired the event.
- * evt - Mouse event which was fired.
- */
- mxCellRenderer.prototype.isLabelEvent = function(state, evt)
- {
- return true;
- };
- /**
- * Function: installListeners
- *
- * Installs the event listeners for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the event listeners should be isntalled.
- */
- mxCellRenderer.prototype.installListeners = function(state)
- {
- var graph = state.view.graph;
- // Workaround for touch devices routing all events for a mouse
- // gesture (down, move, up) via the initial DOM node. Same for
- // HTML images in all IE versions (VML images are working).
- var getState = function(evt)
- {
- var result = state;
-
- if ((graph.dialect != mxConstants.DIALECT_SVG && mxEvent.getSource(evt).nodeName == 'IMG') || mxClient.IS_TOUCH)
- {
- var x = mxEvent.getClientX(evt);
- var y = mxEvent.getClientY(evt);
-
- // Dispatches the drop event to the graph which
- // consumes and executes the source function
- var pt = mxUtils.convertPoint(graph.container, x, y);
- result = graph.view.getState(graph.getCellAt(pt.x, pt.y));
- }
-
- return result;
- };
- mxEvent.addGestureListeners(state.shape.node,
- mxUtils.bind(this, function(evt)
- {
- if (this.isShapeEvent(state, evt))
- {
- graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
- }
- }),
- mxUtils.bind(this, function(evt)
- {
- if (this.isShapeEvent(state, evt))
- {
- graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
- }
- }),
- mxUtils.bind(this, function(evt)
- {
- if (this.isShapeEvent(state, evt))
- {
- graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
- }
- })
- );
-
- // Uses double click timeout in mxGraph for quirks mode
- if (graph.nativeDblClickEnabled)
- {
- mxEvent.addListener(state.shape.node, 'dblclick',
- mxUtils.bind(this, function(evt)
- {
- if (this.isShapeEvent(state, evt))
- {
- graph.dblClick(evt, state.cell);
- mxEvent.consume(evt);
- }
- })
- );
- }
- };
- /**
- * Function: redrawLabel
- *
- * Redraws the label for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> whose label should be redrawn.
- */
- mxCellRenderer.prototype.redrawLabel = function(state, forced)
- {
- var graph = state.view.graph;
- var value = this.getLabelValue(state);
- var wrapping = graph.isWrapping(state.cell);
- var clipping = graph.isLabelClipped(state.cell);
- var isForceHtml = (state.view.graph.isHtmlLabel(state.cell) || (value != null && mxUtils.isNode(value)));
- var dialect = (isForceHtml) ? mxConstants.DIALECT_STRICTHTML : state.view.graph.dialect;
- var overflow = state.style[mxConstants.STYLE_OVERFLOW] || 'visible';
- if (state.text != null && (state.text.wrap != wrapping || state.text.clipped != clipping ||
- state.text.overflow != overflow || state.text.dialect != dialect))
- {
- state.text.destroy();
- state.text = null;
- }
-
- if (state.text == null && value != null && (mxUtils.isNode(value) || value.length > 0))
- {
- this.createLabel(state, value);
- }
- else if (state.text != null && (value == null || value.length == 0))
- {
- state.text.destroy();
- state.text = null;
- }
- if (state.text != null)
- {
- // Forced is true if the style has changed, so to get the updated
- // result in getLabelBounds we apply the new style to the shape
- if (forced)
- {
- // Checks if a full repaint is needed
- if (state.text.lastValue != null && this.isTextShapeInvalid(state, state.text))
- {
- // Forces a full repaint
- state.text.lastValue = null;
- }
-
- state.text.resetStyles();
- state.text.apply(state);
-
- // Special case where value is obtained via hook in graph
- state.text.valign = graph.getVerticalAlign(state);
- }
-
- var bounds = this.getLabelBounds(state);
- var nextScale = this.getTextScale(state);
- this.resolveColor(state, 'color', mxConstants.STYLE_FONTCOLOR);
-
- if (forced || state.text.value != value || state.text.isWrapping != wrapping ||
- state.text.overflow != overflow || state.text.isClipping != clipping ||
- state.text.scale != nextScale || state.text.dialect != dialect ||
- state.text.bounds == null || !state.text.bounds.equals(bounds))
- {
- state.text.dialect = dialect;
- state.text.value = value;
- state.text.bounds = bounds;
- state.text.scale = nextScale;
- state.text.wrap = wrapping;
- state.text.clipped = clipping;
- state.text.overflow = overflow;
-
- // Preserves visible state
- var vis = state.text.node.style.visibility;
- this.redrawLabelShape(state.text);
- state.text.node.style.visibility = vis;
- }
- }
- };
- /**
- * Function: isTextShapeInvalid
- *
- * Returns true if the style for the text shape has changed.
- *
- * Parameters:
- *
- * state - <mxCellState> whose label should be checked.
- * shape - <mxText> shape to be checked.
- */
- mxCellRenderer.prototype.isTextShapeInvalid = function(state, shape)
- {
- function check(property, stylename, defaultValue)
- {
- var result = false;
-
- // Workaround for spacing added to directional spacing
- if (stylename == 'spacingTop' || stylename == 'spacingRight' ||
- stylename == 'spacingBottom' || stylename == 'spacingLeft')
- {
- result = parseFloat(shape[property]) - parseFloat(shape.spacing) !=
- (state.style[stylename] || defaultValue);
- }
- else
- {
- result = shape[property] != (state.style[stylename] || defaultValue);
- }
-
- return result;
- };
- return check('fontStyle', mxConstants.STYLE_FONTSTYLE, mxConstants.DEFAULT_FONTSTYLE) ||
- check('family', mxConstants.STYLE_FONTFAMILY, mxConstants.DEFAULT_FONTFAMILY) ||
- check('size', mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE) ||
- check('color', mxConstants.STYLE_FONTCOLOR, 'black') ||
- check('align', mxConstants.STYLE_ALIGN, '') ||
- check('valign', mxConstants.STYLE_VERTICAL_ALIGN, '') ||
- check('spacing', mxConstants.STYLE_SPACING, 2) ||
- check('spacingTop', mxConstants.STYLE_SPACING_TOP, 0) ||
- check('spacingRight', mxConstants.STYLE_SPACING_RIGHT, 0) ||
- check('spacingBottom', mxConstants.STYLE_SPACING_BOTTOM, 0) ||
- check('spacingLeft', mxConstants.STYLE_SPACING_LEFT, 0) ||
- check('horizontal', mxConstants.STYLE_HORIZONTAL, true) ||
- check('background', mxConstants.STYLE_LABEL_BACKGROUNDCOLOR) ||
- check('border', mxConstants.STYLE_LABEL_BORDERCOLOR) ||
- check('opacity', mxConstants.STYLE_TEXT_OPACITY, 100) ||
- check('textDirection', mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION);
- };
- /**
- * Function: redrawLabelShape
- *
- * Called to invoked redraw on the given text shape.
- *
- * Parameters:
- *
- * shape - <mxText> shape to be redrawn.
- */
- mxCellRenderer.prototype.redrawLabelShape = function(shape)
- {
- shape.redraw();
- };
- /**
- * Function: getTextScale
- *
- * Returns the scaling used for the label of the given state
- *
- * Parameters:
- *
- * state - <mxCellState> whose label scale should be returned.
- */
- mxCellRenderer.prototype.getTextScale = function(state)
- {
- return state.view.scale;
- };
- /**
- * Function: getLabelBounds
- *
- * Returns the bounds to be used to draw the label of the given state.
- *
- * Parameters:
- *
- * state - <mxCellState> whose label bounds should be returned.
- */
- mxCellRenderer.prototype.getLabelBounds = function(state)
- {
- var graph = state.view.graph;
- var scale = state.view.scale;
- var isEdge = graph.getModel().isEdge(state.cell);
- var bounds = new mxRectangle(state.absoluteOffset.x, state.absoluteOffset.y);
- if (isEdge)
- {
- var spacing = state.text.getSpacing();
- bounds.x += spacing.x * scale;
- bounds.y += spacing.y * scale;
-
- var geo = graph.getCellGeometry(state.cell);
-
- if (geo != null)
- {
- bounds.width = Math.max(0, geo.width * scale);
- bounds.height = Math.max(0, geo.height * scale);
- }
- }
- else
- {
- // Inverts label position
- if (state.text.isPaintBoundsInverted())
- {
- var tmp = bounds.x;
- bounds.x = bounds.y;
- bounds.y = tmp;
- }
-
- bounds.x += state.x;
- bounds.y += state.y;
-
- // Minimum of 1 fixes alignment bug in HTML labels
- bounds.width = Math.max(1, state.width);
- bounds.height = Math.max(1, state.height);
- }
- if (state.text.isPaintBoundsInverted())
- {
- // Rotates around center of state
- var t = (state.width - state.height) / 2;
- bounds.x += t;
- bounds.y -= t;
- var tmp = bounds.width;
- bounds.width = bounds.height;
- bounds.height = tmp;
- }
-
- // Shape can modify its label bounds
- if (state.shape != null)
- {
- var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
- var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
-
- if (hpos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_MIDDLE)
- {
- bounds = state.shape.getLabelBounds(bounds);
- }
- }
-
- // Label width style overrides actual label width
- var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
-
- if (lw != null)
- {
- bounds.width = parseFloat(lw) * scale;
- }
-
- if (!isEdge)
- {
- this.rotateLabelBounds(state, bounds);
- }
-
- return bounds;
- };
- /**
- * Function: rotateLabelBounds
- *
- * Adds the shape rotation to the given label bounds and
- * applies the alignment and offsets.
- *
- * Parameters:
- *
- * state - <mxCellState> whose label bounds should be rotated.
- * bounds - <mxRectangle> the rectangle to be rotated.
- */
- mxCellRenderer.prototype.rotateLabelBounds = function(state, bounds)
- {
- bounds.y -= state.text.margin.y * bounds.height;
- bounds.x -= state.text.margin.x * bounds.width;
-
- if (!this.legacySpacing || (state.style[mxConstants.STYLE_OVERFLOW] != 'fill' && state.style[mxConstants.STYLE_OVERFLOW] != 'width'))
- {
- var s = state.view.scale;
- var spacing = state.text.getSpacing();
- bounds.x += spacing.x * s;
- bounds.y += spacing.y * s;
-
- var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
- var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
- var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
-
- bounds.width = Math.max(0, bounds.width - ((hpos == mxConstants.ALIGN_CENTER && lw == null) ? (state.text.spacingLeft * s + state.text.spacingRight * s) : 0));
- bounds.height = Math.max(0, bounds.height - ((vpos == mxConstants.ALIGN_MIDDLE) ? (state.text.spacingTop * s + state.text.spacingBottom * s) : 0));
- }
- var theta = state.text.getTextRotation();
- // Only needed if rotated around another center
- if (theta != 0 && state != null && state.view.graph.model.isVertex(state.cell))
- {
- var cx = state.getCenterX();
- var cy = state.getCenterY();
-
- if (bounds.x != cx || bounds.y != cy)
- {
- var rad = theta * (Math.PI / 180);
- var pt = mxUtils.getRotatedPoint(new mxPoint(bounds.x, bounds.y),
- Math.cos(rad), Math.sin(rad), new mxPoint(cx, cy));
-
- bounds.x = pt.x;
- bounds.y = pt.y;
- }
- }
- };
- /**
- * Function: redrawCellOverlays
- *
- * Redraws the overlays for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> whose overlays should be redrawn.
- */
- mxCellRenderer.prototype.redrawCellOverlays = function(state, forced)
- {
- this.createCellOverlays(state);
- if (state.overlays != null)
- {
- var rot = mxUtils.mod(mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0), 90);
- var rad = mxUtils.toRadians(rot);
- var cos = Math.cos(rad);
- var sin = Math.sin(rad);
-
- state.overlays.visit(function(id, shape)
- {
- var bounds = shape.overlay.getBounds(state);
-
- if (!state.view.graph.getModel().isEdge(state.cell))
- {
- if (state.shape != null && rot != 0)
- {
- var cx = bounds.getCenterX();
- var cy = bounds.getCenterY();
- var point = mxUtils.getRotatedPoint(new mxPoint(cx, cy), cos, sin,
- new mxPoint(state.getCenterX(), state.getCenterY()));
- cx = point.x;
- cy = point.y;
- bounds.x = Math.round(cx - bounds.width / 2);
- bounds.y = Math.round(cy - bounds.height / 2);
- }
- }
-
- if (forced || shape.bounds == null || shape.scale != state.view.scale ||
- !shape.bounds.equals(bounds))
- {
- shape.bounds = bounds;
- shape.scale = state.view.scale;
- shape.redraw();
- }
- });
- }
- };
- /**
- * Function: redrawControl
- *
- * Redraws the control for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> whose control should be redrawn.
- */
- mxCellRenderer.prototype.redrawControl = function(state, forced)
- {
- var image = state.view.graph.getFoldingImage(state);
-
- if (state.control != null && image != null)
- {
- var bounds = this.getControlBounds(state, image.width, image.height);
- var r = (this.legacyControlPosition) ?
- mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0) :
- state.shape.getTextRotation();
- var s = state.view.scale;
-
- if (forced || state.control.scale != s || !state.control.bounds.equals(bounds) ||
- state.control.rotation != r)
- {
- state.control.rotation = r;
- state.control.bounds = bounds;
- state.control.scale = s;
-
- state.control.redraw();
- }
- }
- };
- /**
- * Function: getControlBounds
- *
- * Returns the bounds to be used to draw the control (folding icon) of the
- * given state.
- */
- mxCellRenderer.prototype.getControlBounds = function(state, w, h)
- {
- if (state.control != null)
- {
- var s = state.view.scale;
- var cx = state.getCenterX();
- var cy = state.getCenterY();
-
- if (!state.view.graph.getModel().isEdge(state.cell))
- {
- cx = state.x + w * s;
- cy = state.y + h * s;
-
- if (state.shape != null)
- {
- // TODO: Factor out common code
- var rot = state.shape.getShapeRotation();
-
- if (this.legacyControlPosition)
- {
- rot = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0);
- }
- else
- {
- if (state.shape.isPaintBoundsInverted())
- {
- var t = (state.width - state.height) / 2;
- cx += t;
- cy -= t;
- }
- }
-
- if (rot != 0)
- {
- var rad = mxUtils.toRadians(rot);
- var cos = Math.cos(rad);
- var sin = Math.sin(rad);
-
- var point = mxUtils.getRotatedPoint(new mxPoint(cx, cy), cos, sin,
- new mxPoint(state.getCenterX(), state.getCenterY()));
- cx = point.x;
- cy = point.y;
- }
- }
- }
-
- return (state.view.graph.getModel().isEdge(state.cell)) ?
- new mxRectangle(Math.round(cx - w / 2 * s), Math.round(cy - h / 2 * s), Math.round(w * s), Math.round(h * s))
- : new mxRectangle(Math.round(cx - w / 2 * s), Math.round(cy - h / 2 * s), Math.round(w * s), Math.round(h * s));
- }
-
- return null;
- };
- /**
- * Function: insertStateAfter
- *
- * Inserts the given array of <mxShapes> after the given nodes in the DOM.
- *
- * Parameters:
- *
- * shapes - Array of <mxShapes> to be inserted.
- * node - Node in <drawPane> after which the shapes should be inserted.
- * htmlNode - Node in the graph container after which the shapes should be inserted that
- * will not go into the <drawPane> (eg. HTML labels without foreignObjects).
- */
- mxCellRenderer.prototype.insertStateAfter = function(state, node, htmlNode)
- {
- var shapes = this.getShapesForState(state);
-
- for (var i = 0; i < shapes.length; i++)
- {
- if (shapes[i] != null && shapes[i].node != null)
- {
- var html = shapes[i].node.parentNode != state.view.getDrawPane() &&
- shapes[i].node.parentNode != state.view.getOverlayPane();
- var temp = (html) ? htmlNode : node;
-
- if (temp != null && temp.nextSibling != shapes[i].node)
- {
- if (temp.nextSibling == null)
- {
- temp.parentNode.appendChild(shapes[i].node);
- }
- else
- {
- temp.parentNode.insertBefore(shapes[i].node, temp.nextSibling);
- }
- }
- else if (temp == null)
- {
- // Special case: First HTML node should be first sibling after canvas
- if (shapes[i].node.parentNode == state.view.graph.container)
- {
- var canvas = state.view.canvas;
-
- while (canvas != null && canvas.parentNode != state.view.graph.container)
- {
- canvas = canvas.parentNode;
- }
-
- if (canvas != null && canvas.nextSibling != null)
- {
- if (canvas.nextSibling != shapes[i].node)
- {
- shapes[i].node.parentNode.insertBefore(shapes[i].node, canvas.nextSibling);
- }
- }
- else
- {
- shapes[i].node.parentNode.appendChild(shapes[i].node);
- }
- }
- else if (shapes[i].node.parentNode != null &&
- shapes[i].node.parentNode.firstChild != null &&
- shapes[i].node.parentNode.firstChild != shapes[i].node)
- {
- // Inserts the node as the first child of the parent to implement the order
- shapes[i].node.parentNode.insertBefore(shapes[i].node, shapes[i].node.parentNode.firstChild);
- }
- }
-
- if (html)
- {
- htmlNode = shapes[i].node;
- }
- else
- {
- node = shapes[i].node;
- }
- }
- }
- return [node, htmlNode];
- };
- /**
- * Function: getShapesForState
- *
- * Returns the <mxShapes> for the given cell state in the order in which they should
- * appear in the DOM.
- *
- * Parameters:
- *
- * state - <mxCellState> whose shapes should be returned.
- */
- mxCellRenderer.prototype.getShapesForState = function(state)
- {
- return [state.shape, state.text, state.control];
- };
- /**
- * Function: redraw
- *
- * Updates the bounds or points and scale of the shapes for the given cell
- * state. This is called in mxGraphView.validatePoints as the last step of
- * updating all cells.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the shapes should be updated.
- * force - Optional boolean that specifies if the cell should be reconfiured
- * and redrawn without any additional checks.
- * rendering - Optional boolean that specifies if the cell should actually
- * be drawn into the DOM. If this is false then redraw and/or reconfigure
- * will not be called on the shape.
- */
- mxCellRenderer.prototype.redraw = function(state, force, rendering)
- {
- var shapeChanged = this.redrawShape(state, force, rendering);
- if (state.shape != null && (rendering == null || rendering))
- {
- this.redrawLabel(state, shapeChanged);
- this.redrawCellOverlays(state, shapeChanged);
- this.redrawControl(state, shapeChanged);
- }
- };
- /**
- * Function: redrawShape
- *
- * Redraws the shape for the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> whose label should be redrawn.
- */
- mxCellRenderer.prototype.redrawShape = function(state, force, rendering)
- {
- var model = state.view.graph.model;
- var shapeChanged = false;
- // Forces creation of new shape if shape style has changed
- if (state.shape != null && state.shape.style != null && state.style != null &&
- state.shape.style[mxConstants.STYLE_SHAPE] != state.style[mxConstants.STYLE_SHAPE])
- {
- state.shape.destroy();
- state.shape = null;
- }
-
- if (state.shape == null && state.view.graph.container != null &&
- state.cell != state.view.currentRoot &&
- (model.isVertex(state.cell) || model.isEdge(state.cell)))
- {
- state.shape = this.createShape(state);
-
- if (state.shape != null)
- {
- state.shape.minSvgStrokeWidth = this.minSvgStrokeWidth;
- state.shape.antiAlias = this.antiAlias;
-
- this.createIndicatorShape(state);
- this.initializeShape(state);
- this.createCellOverlays(state);
- this.installListeners(state);
-
- // Forces a refresh of the handler if one exists
- state.view.graph.selectionCellsHandler.updateHandler(state);
- }
- }
- else if (!force && state.shape != null && (!mxUtils.equalEntries(state.shape.style,
- state.style) || this.checkPlaceholderStyles(state)))
- {
- state.shape.resetStyles();
- this.configureShape(state);
- // LATER: Ignore update for realtime to fix reset of current gesture
- state.view.graph.selectionCellsHandler.updateHandler(state);
- force = true;
- }
-
- // Updates indicator shape
- if (state.shape != null && state.shape.indicatorShape !=
- this.getShape(state.view.graph.getIndicatorShape(state)))
- {
- if (state.shape.indicator != null)
- {
- state.shape.indicator.destroy();
- state.shape.indicator = null;
- }
-
- this.createIndicatorShape(state);
-
- if (state.shape.indicatorShape != null)
- {
- state.shape.indicator = new state.shape.indicatorShape();
- state.shape.indicator.dialect = state.shape.dialect;
- state.shape.indicator.init(state.node);
- force = true;
- }
- }
- if (state.shape != null)
- {
- // Handles changes of the collapse icon
- this.createControl(state);
-
- // Redraws the cell if required, ignores changes to bounds if points are
- // defined as the bounds are updated for the given points inside the shape
- if (force || this.isShapeInvalid(state, state.shape))
- {
- if (state.absolutePoints != null)
- {
- state.shape.points = state.absolutePoints.slice();
- state.shape.bounds = null;
- }
- else
- {
- state.shape.points = null;
- state.shape.bounds = new mxRectangle(state.x, state.y, state.width, state.height);
- }
- state.shape.scale = state.view.scale;
-
- if (rendering == null || rendering)
- {
- this.doRedrawShape(state);
- }
- else
- {
- state.shape.updateBoundingBox();
- }
-
- shapeChanged = true;
- }
- }
- return shapeChanged;
- };
- /**
- * Function: doRedrawShape
- *
- * Invokes redraw on the shape of the given state.
- */
- mxCellRenderer.prototype.doRedrawShape = function(state)
- {
- state.shape.redraw();
- };
- /**
- * Function: isShapeInvalid
- *
- * Returns true if the given shape must be repainted.
- */
- mxCellRenderer.prototype.isShapeInvalid = function(state, shape)
- {
- return shape.bounds == null || shape.scale != state.view.scale ||
- (state.absolutePoints == null && !shape.bounds.equals(state)) ||
- (state.absolutePoints != null && !mxUtils.equalPoints(shape.points, state.absolutePoints))
- };
- /**
- * Function: destroy
- *
- * Destroys the shapes associated with the given cell state.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the shapes should be destroyed.
- */
- mxCellRenderer.prototype.destroy = function(state)
- {
- if (state.shape != null)
- {
- if (state.text != null)
- {
- state.text.destroy();
- state.text = null;
- }
-
- if (state.overlays != null)
- {
- state.overlays.visit(function(id, shape)
- {
- shape.destroy();
- });
-
- state.overlays = null;
- }
- if (state.control != null)
- {
- state.control.destroy();
- state.control = null;
- }
-
- state.shape.destroy();
- state.shape = null;
- }
- };
|