123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641 |
- /**
- * 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;
- }
- };
- __mxOutput.mxCellRenderer = typeof mxCellRenderer !== 'undefined' ? mxCellRenderer : undefined;
|