Actualización

This commit is contained in:
Xes
2025-04-10 12:49:05 +02:00
parent 4aff98e77b
commit 1cdd00920f
9151 changed files with 1800913 additions and 0 deletions

View File

@@ -0,0 +1,314 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxCellHighlight
*
* A helper class to highlight cells. Here is an example for a given cell.
*
* (code)
* var highlight = new mxCellHighlight(graph, '#ff0000', 2);
* highlight.highlight(graph.view.getState(cell)));
* (end)
*
* Constructor: mxCellHighlight
*
* Constructs a cell highlight.
*/
function mxCellHighlight(graph, highlightColor, strokeWidth, dashed)
{
if (graph != null)
{
this.graph = graph;
this.highlightColor = (highlightColor != null) ? highlightColor : mxConstants.DEFAULT_VALID_COLOR;
this.strokeWidth = (strokeWidth != null) ? strokeWidth : mxConstants.HIGHLIGHT_STROKEWIDTH;
this.dashed = (dashed != null) ? dashed : false;
this.opacity = mxConstants.HIGHLIGHT_OPACITY;
// Updates the marker if the graph changes
this.repaintHandler = mxUtils.bind(this, function()
{
// Updates reference to state
if (this.state != null)
{
var tmp = this.graph.view.getState(this.state.cell);
if (tmp == null)
{
this.hide();
}
else
{
this.state = tmp;
this.repaint();
}
}
});
this.graph.getView().addListener(mxEvent.SCALE, this.repaintHandler);
this.graph.getView().addListener(mxEvent.TRANSLATE, this.repaintHandler);
this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.repaintHandler);
this.graph.getModel().addListener(mxEvent.CHANGE, this.repaintHandler);
// Hides the marker if the current root changes
this.resetHandler = mxUtils.bind(this, function()
{
this.hide();
});
this.graph.getView().addListener(mxEvent.DOWN, this.resetHandler);
this.graph.getView().addListener(mxEvent.UP, this.resetHandler);
}
};
/**
* Variable: keepOnTop
*
* Specifies if the highlights should appear on top of everything
* else in the overlay pane. Default is false.
*/
mxCellHighlight.prototype.keepOnTop = false;
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
mxCellHighlight.prototype.graph = true;
/**
* Variable: state
*
* Reference to the <mxCellState>.
*/
mxCellHighlight.prototype.state = null;
/**
* Variable: spacing
*
* Specifies the spacing between the highlight for vertices and the vertex.
* Default is 2.
*/
mxCellHighlight.prototype.spacing = 2;
/**
* Variable: resetHandler
*
* Holds the handler that automatically invokes reset if the highlight
* should be hidden.
*/
mxCellHighlight.prototype.resetHandler = null;
/**
* Function: setHighlightColor
*
* Sets the color of the rectangle used to highlight drop targets.
*
* Parameters:
*
* color - String that represents the new highlight color.
*/
mxCellHighlight.prototype.setHighlightColor = function(color)
{
this.highlightColor = color;
if (this.shape != null)
{
this.shape.stroke = color;
}
};
/**
* Function: drawHighlight
*
* Creates and returns the highlight shape for the given state.
*/
mxCellHighlight.prototype.drawHighlight = function()
{
this.shape = this.createShape();
this.repaint();
if (!this.keepOnTop && this.shape.node.parentNode.firstChild != this.shape.node)
{
this.shape.node.parentNode.insertBefore(this.shape.node, this.shape.node.parentNode.firstChild);
}
};
/**
* Function: createShape
*
* Creates and returns the highlight shape for the given state.
*/
mxCellHighlight.prototype.createShape = function()
{
var shape = this.graph.cellRenderer.createShape(this.state);
shape.svgStrokeTolerance = this.graph.tolerance;
shape.points = this.state.absolutePoints;
shape.apply(this.state);
shape.stroke = this.highlightColor;
shape.opacity = this.opacity;
shape.isDashed = this.dashed;
shape.isShadow = false;
shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
shape.init(this.graph.getView().getOverlayPane());
mxEvent.redirectMouseEvents(shape.node, this.graph, this.state);
if (this.graph.dialect != mxConstants.DIALECT_SVG)
{
shape.pointerEvents = false;
}
else
{
shape.svgPointerEvents = 'stroke';
}
return shape;
};
/**
* Function: repaint
*
* Updates the highlight after a change of the model or view.
*/
mxCellHighlight.prototype.getStrokeWidth = function(state)
{
return this.strokeWidth;
};
/**
* Function: repaint
*
* Updates the highlight after a change of the model or view.
*/
mxCellHighlight.prototype.repaint = function()
{
if (this.state != null && this.shape != null)
{
this.shape.scale = this.state.view.scale;
if (this.graph.model.isEdge(this.state.cell))
{
this.shape.strokewidth = this.getStrokeWidth();
this.shape.points = this.state.absolutePoints;
this.shape.outline = false;
}
else
{
this.shape.bounds = new mxRectangle(this.state.x - this.spacing, this.state.y - this.spacing,
this.state.width + 2 * this.spacing, this.state.height + 2 * this.spacing);
this.shape.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
this.shape.strokewidth = this.getStrokeWidth() / this.state.view.scale;
this.shape.outline = true;
}
// Uses cursor from shape in highlight
if (this.state.shape != null)
{
this.shape.setCursor(this.state.shape.getCursor());
}
// Workaround for event transparency in VML with transparent color
// is to use a non-transparent color with near zero opacity
if (mxClient.IS_QUIRKS || document.documentMode == 8)
{
if (this.shape.stroke == 'transparent')
{
// KNOWN: Quirks mode does not seem to catch events if
// we do not force an update of the DOM via a change such
// as mxLog.debug. Since IE6 is EOL we do not add a fix.
this.shape.stroke = 'white';
this.shape.opacity = 1;
}
else
{
this.shape.opacity = this.opacity;
}
}
this.shape.redraw();
}
};
/**
* Function: hide
*
* Resets the state of the cell marker.
*/
mxCellHighlight.prototype.hide = function()
{
this.highlight(null);
};
/**
* Function: mark
*
* Marks the <markedState> and fires a <mark> event.
*/
mxCellHighlight.prototype.highlight = function(state)
{
if (this.state != state)
{
if (this.shape != null)
{
this.shape.destroy();
this.shape = null;
}
this.state = state;
if (this.state != null)
{
this.drawHighlight();
}
}
};
/**
* Function: isHighlightAt
*
* Returns true if this highlight is at the given position.
*/
mxCellHighlight.prototype.isHighlightAt = function(x, y)
{
var hit = false;
// Quirks mode is currently not supported as it used a different coordinate system
if (this.shape != null && document.elementFromPoint != null && !mxClient.IS_QUIRKS)
{
var elt = document.elementFromPoint(x, y);
while (elt != null)
{
if (elt == this.shape.node)
{
hit = true;
break;
}
elt = elt.parentNode;
}
}
return hit;
};
/**
* Function: destroy
*
* Destroys the handler and all its resources and DOM nodes.
*/
mxCellHighlight.prototype.destroy = function()
{
this.graph.getView().removeListener(this.resetHandler);
this.graph.getView().removeListener(this.repaintHandler);
this.graph.getModel().removeListener(this.repaintHandler);
if (this.shape != null)
{
this.shape.destroy();
this.shape = null;
}
};

View File

@@ -0,0 +1,430 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxCellMarker
*
* A helper class to process mouse locations and highlight cells.
*
* Helper class to highlight cells. To add a cell marker to an existing graph
* for highlighting all cells, the following code is used:
*
* (code)
* var marker = new mxCellMarker(graph);
* graph.addMouseListener({
* mouseDown: function() {},
* mouseMove: function(sender, me)
* {
* marker.process(me);
* },
* mouseUp: function() {}
* });
* (end)
*
* Event: mxEvent.MARK
*
* Fires after a cell has been marked or unmarked. The <code>state</code>
* property contains the marked <mxCellState> or null if no state is marked.
*
* Constructor: mxCellMarker
*
* Constructs a new cell marker.
*
* Parameters:
*
* graph - Reference to the enclosing <mxGraph>.
* validColor - Optional marker color for valid states. Default is
* <mxConstants.DEFAULT_VALID_COLOR>.
* invalidColor - Optional marker color for invalid states. Default is
* <mxConstants.DEFAULT_INVALID_COLOR>.
* hotspot - Portion of the width and hight where a state intersects a
* given coordinate pair. A value of 0 means always highlight. Default is
* <mxConstants.DEFAULT_HOTSPOT>.
*/
function mxCellMarker(graph, validColor, invalidColor, hotspot)
{
mxEventSource.call(this);
if (graph != null)
{
this.graph = graph;
this.validColor = (validColor != null) ? validColor : mxConstants.DEFAULT_VALID_COLOR;
this.invalidColor = (validColor != null) ? invalidColor : mxConstants.DEFAULT_INVALID_COLOR;
this.hotspot = (hotspot != null) ? hotspot : mxConstants.DEFAULT_HOTSPOT;
this.highlight = new mxCellHighlight(graph);
}
};
/**
* Extends mxEventSource.
*/
mxUtils.extend(mxCellMarker, mxEventSource);
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
mxCellMarker.prototype.graph = null;
/**
* Variable: enabled
*
* Specifies if the marker is enabled. Default is true.
*/
mxCellMarker.prototype.enabled = true;
/**
* Variable: hotspot
*
* Specifies the portion of the width and height that should trigger
* a highlight. The area around the center of the cell to be marked is used
* as the hotspot. Possible values are between 0 and 1. Default is
* mxConstants.DEFAULT_HOTSPOT.
*/
mxCellMarker.prototype.hotspot = mxConstants.DEFAULT_HOTSPOT;
/**
* Variable: hotspotEnabled
*
* Specifies if the hotspot is enabled. Default is false.
*/
mxCellMarker.prototype.hotspotEnabled = false;
/**
* Variable: validColor
*
* Holds the valid marker color.
*/
mxCellMarker.prototype.validColor = null;
/**
* Variable: invalidColor
*
* Holds the invalid marker color.
*/
mxCellMarker.prototype.invalidColor = null;
/**
* Variable: currentColor
*
* Holds the current marker color.
*/
mxCellMarker.prototype.currentColor = null;
/**
* Variable: validState
*
* Holds the marked <mxCellState> if it is valid.
*/
mxCellMarker.prototype.validState = null;
/**
* Variable: markedState
*
* Holds the marked <mxCellState>.
*/
mxCellMarker.prototype.markedState = null;
/**
* Function: setEnabled
*
* Enables or disables event handling. This implementation
* updates <enabled>.
*
* Parameters:
*
* enabled - Boolean that specifies the new enabled state.
*/
mxCellMarker.prototype.setEnabled = function(enabled)
{
this.enabled = enabled;
};
/**
* Function: isEnabled
*
* Returns true if events are handled. This implementation
* returns <enabled>.
*/
mxCellMarker.prototype.isEnabled = function()
{
return this.enabled;
};
/**
* Function: setHotspot
*
* Sets the <hotspot>.
*/
mxCellMarker.prototype.setHotspot = function(hotspot)
{
this.hotspot = hotspot;
};
/**
* Function: getHotspot
*
* Returns the <hotspot>.
*/
mxCellMarker.prototype.getHotspot = function()
{
return this.hotspot;
};
/**
* Function: setHotspotEnabled
*
* Specifies whether the hotspot should be used in <intersects>.
*/
mxCellMarker.prototype.setHotspotEnabled = function(enabled)
{
this.hotspotEnabled = enabled;
};
/**
* Function: isHotspotEnabled
*
* Returns true if hotspot is used in <intersects>.
*/
mxCellMarker.prototype.isHotspotEnabled = function()
{
return this.hotspotEnabled;
};
/**
* Function: hasValidState
*
* Returns true if <validState> is not null.
*/
mxCellMarker.prototype.hasValidState = function()
{
return this.validState != null;
};
/**
* Function: getValidState
*
* Returns the <validState>.
*/
mxCellMarker.prototype.getValidState = function()
{
return this.validState;
};
/**
* Function: getMarkedState
*
* Returns the <markedState>.
*/
mxCellMarker.prototype.getMarkedState = function()
{
return this.markedState;
};
/**
* Function: reset
*
* Resets the state of the cell marker.
*/
mxCellMarker.prototype.reset = function()
{
this.validState = null;
if (this.markedState != null)
{
this.markedState = null;
this.unmark();
}
};
/**
* Function: process
*
* Processes the given event and cell and marks the state returned by
* <getState> with the color returned by <getMarkerColor>. If the
* markerColor is not null, then the state is stored in <markedState>. If
* <isValidState> returns true, then the state is stored in <validState>
* regardless of the marker color. The state is returned regardless of the
* marker color and valid state.
*/
mxCellMarker.prototype.process = function(me)
{
var state = null;
if (this.isEnabled())
{
state = this.getState(me);
this.setCurrentState(state, me);
}
return state;
};
/**
* Function: setCurrentState
*
* Sets and marks the current valid state.
*/
mxCellMarker.prototype.setCurrentState = function(state, me, color)
{
var isValid = (state != null) ? this.isValidState(state) : false;
color = (color != null) ? color : this.getMarkerColor(me.getEvent(), state, isValid);
if (isValid)
{
this.validState = state;
}
else
{
this.validState = null;
}
if (state != this.markedState || color != this.currentColor)
{
this.currentColor = color;
if (state != null && this.currentColor != null)
{
this.markedState = state;
this.mark();
}
else if (this.markedState != null)
{
this.markedState = null;
this.unmark();
}
}
};
/**
* Function: markCell
*
* Marks the given cell using the given color, or <validColor> if no color is specified.
*/
mxCellMarker.prototype.markCell = function(cell, color)
{
var state = this.graph.getView().getState(cell);
if (state != null)
{
this.currentColor = (color != null) ? color : this.validColor;
this.markedState = state;
this.mark();
}
};
/**
* Function: mark
*
* Marks the <markedState> and fires a <mark> event.
*/
mxCellMarker.prototype.mark = function()
{
this.highlight.setHighlightColor(this.currentColor);
this.highlight.highlight(this.markedState);
this.fireEvent(new mxEventObject(mxEvent.MARK, 'state', this.markedState));
};
/**
* Function: unmark
*
* Hides the marker and fires a <mark> event.
*/
mxCellMarker.prototype.unmark = function()
{
this.mark();
};
/**
* Function: isValidState
*
* Returns true if the given <mxCellState> is a valid state. If this
* returns true, then the state is stored in <validState>. The return value
* of this method is used as the argument for <getMarkerColor>.
*/
mxCellMarker.prototype.isValidState = function(state)
{
return true;
};
/**
* Function: getMarkerColor
*
* Returns the valid- or invalidColor depending on the value of isValid.
* The given <mxCellState> is ignored by this implementation.
*/
mxCellMarker.prototype.getMarkerColor = function(evt, state, isValid)
{
return (isValid) ? this.validColor : this.invalidColor;
};
/**
* Function: getState
*
* Uses <getCell>, <getStateToMark> and <intersects> to return the
* <mxCellState> for the given <mxMouseEvent>.
*/
mxCellMarker.prototype.getState = function(me)
{
var view = this.graph.getView();
var cell = this.getCell(me);
var state = this.getStateToMark(view.getState(cell));
return (state != null && this.intersects(state, me)) ? state : null;
};
/**
* Function: getCell
*
* Returns the <mxCell> for the given event and cell. This returns the
* given cell.
*/
mxCellMarker.prototype.getCell = function(me)
{
return me.getCell();
};
/**
* Function: getStateToMark
*
* Returns the <mxCellState> to be marked for the given <mxCellState> under
* the mouse. This returns the given state.
*/
mxCellMarker.prototype.getStateToMark = function(state)
{
return state;
};
/**
* Function: intersects
*
* Returns true if the given coordinate pair intersects the given state.
* This returns true if the <hotspot> is 0 or the coordinates are inside
* the hotspot for the given cell state.
*/
mxCellMarker.prototype.intersects = function(state, me)
{
if (this.hotspotEnabled)
{
return mxUtils.intersectsHotspot(state, me.getGraphX(), me.getGraphY(),
this.hotspot, mxConstants.MIN_HOTSPOT_SIZE,
mxConstants.MAX_HOTSPOT_SIZE);
}
return true;
};
/**
* Function: destroy
*
* Destroys the handler and all its resources and DOM nodes.
*/
mxCellMarker.prototype.destroy = function()
{
this.graph.getView().removeListener(this.resetHandler);
this.graph.getModel().removeListener(this.resetHandler);
this.highlight.destroy();
};

View File

@@ -0,0 +1,145 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxCellTracker
*
* Event handler that highlights cells. Inherits from <mxCellMarker>.
*
* Example:
*
* (code)
* new mxCellTracker(graph, '#00FF00');
* (end)
*
* For detecting dragEnter, dragOver and dragLeave on cells, the following
* code can be used:
*
* (code)
* graph.addMouseListener(
* {
* cell: null,
* mouseDown: function(sender, me) { },
* mouseMove: function(sender, me)
* {
* var tmp = me.getCell();
*
* if (tmp != this.cell)
* {
* if (this.cell != null)
* {
* this.dragLeave(me.getEvent(), this.cell);
* }
*
* this.cell = tmp;
*
* if (this.cell != null)
* {
* this.dragEnter(me.getEvent(), this.cell);
* }
* }
*
* if (this.cell != null)
* {
* this.dragOver(me.getEvent(), this.cell);
* }
* },
* mouseUp: function(sender, me) { },
* dragEnter: function(evt, cell)
* {
* mxLog.debug('dragEnter', cell.value);
* },
* dragOver: function(evt, cell)
* {
* mxLog.debug('dragOver', cell.value);
* },
* dragLeave: function(evt, cell)
* {
* mxLog.debug('dragLeave', cell.value);
* }
* });
* (end)
*
* Constructor: mxCellTracker
*
* Constructs an event handler that highlights cells.
*
* Parameters:
*
* graph - Reference to the enclosing <mxGraph>.
* color - Color of the highlight. Default is blue.
* funct - Optional JavaScript function that is used to override
* <mxCellMarker.getCell>.
*/
function mxCellTracker(graph, color, funct)
{
mxCellMarker.call(this, graph, color);
this.graph.addMouseListener(this);
if (funct != null)
{
this.getCell = funct;
}
// Automatic deallocation of memory
if (mxClient.IS_IE)
{
mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
{
this.destroy();
}));
}
};
/**
* Extends mxCellMarker.
*/
mxUtils.extend(mxCellTracker, mxCellMarker);
/**
* Function: mouseDown
*
* Ignores the event. The event is not consumed.
*/
mxCellTracker.prototype.mouseDown = function(sender, me) { };
/**
* Function: mouseMove
*
* Handles the event by highlighting the cell under the mousepointer if it
* is over the hotspot region of the cell.
*/
mxCellTracker.prototype.mouseMove = function(sender, me)
{
if (this.isEnabled())
{
this.process(me);
}
};
/**
* Function: mouseUp
*
* Handles the event by reseting the highlight.
*/
mxCellTracker.prototype.mouseUp = function(sender, me) { };
/**
* Function: destroy
*
* Destroys the object and all its resources and DOM nodes. This doesn't
* normally need to be called. It is called automatically when the window
* unloads.
*/
mxCellTracker.prototype.destroy = function()
{
if (!this.destroyed)
{
this.destroyed = true;
this.graph.removeMouseListener(this);
mxCellMarker.prototype.destroy.apply(this);
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,517 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxConstraintHandler
*
* Handles constraints on connection targets. This class is in charge of
* showing fixed points when the mouse is over a vertex and handles constraints
* to establish new connections.
*
* Constructor: mxConstraintHandler
*
* Constructs an new constraint handler.
*
* Parameters:
*
* graph - Reference to the enclosing <mxGraph>.
* factoryMethod - Optional function to create the edge. The function takes
* the source and target <mxCell> as the first and second argument and
* returns the <mxCell> that represents the new edge.
*/
function mxConstraintHandler(graph)
{
this.graph = graph;
// Adds a graph model listener to update the current focus on changes
this.resetHandler = mxUtils.bind(this, function(sender, evt)
{
if (this.currentFocus != null && this.graph.view.getState(this.currentFocus.cell) == null)
{
this.reset();
}
else
{
this.redraw();
}
});
this.graph.model.addListener(mxEvent.CHANGE, this.resetHandler);
this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.resetHandler);
this.graph.view.addListener(mxEvent.TRANSLATE, this.resetHandler);
this.graph.view.addListener(mxEvent.SCALE, this.resetHandler);
this.graph.addListener(mxEvent.ROOT, this.resetHandler);
};
/**
* Variable: pointImage
*
* <mxImage> to be used as the image for fixed connection points.
*/
mxConstraintHandler.prototype.pointImage = new mxImage(mxClient.imageBasePath + '/point.gif', 5, 5);
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
mxConstraintHandler.prototype.graph = null;
/**
* Variable: enabled
*
* Specifies if events are handled. Default is true.
*/
mxConstraintHandler.prototype.enabled = true;
/**
* Variable: highlightColor
*
* Specifies the color for the highlight. Default is <mxConstants.DEFAULT_VALID_COLOR>.
*/
mxConstraintHandler.prototype.highlightColor = mxConstants.DEFAULT_VALID_COLOR;
/**
* Function: isEnabled
*
* Returns true if events are handled. This implementation
* returns <enabled>.
*/
mxConstraintHandler.prototype.isEnabled = function()
{
return this.enabled;
};
/**
* Function: setEnabled
*
* Enables or disables event handling. This implementation
* updates <enabled>.
*
* Parameters:
*
* enabled - Boolean that specifies the new enabled state.
*/
mxConstraintHandler.prototype.setEnabled = function(enabled)
{
this.enabled = enabled;
};
/**
* Function: reset
*
* Resets the state of this handler.
*/
mxConstraintHandler.prototype.reset = function()
{
if (this.focusIcons != null)
{
for (var i = 0; i < this.focusIcons.length; i++)
{
this.focusIcons[i].destroy();
}
this.focusIcons = null;
}
if (this.focusHighlight != null)
{
this.focusHighlight.destroy();
this.focusHighlight = null;
}
this.currentConstraint = null;
this.currentFocusArea = null;
this.currentPoint = null;
this.currentFocus = null;
this.focusPoints = null;
};
/**
* Function: getTolerance
*
* Returns the tolerance to be used for intersecting connection points. This
* implementation returns <mxGraph.tolerance>.
*
* Parameters:
*
* me - <mxMouseEvent> whose tolerance should be returned.
*/
mxConstraintHandler.prototype.getTolerance = function(me)
{
return this.graph.getTolerance();
};
/**
* Function: getImageForConstraint
*
* Returns the tolerance to be used for intersecting connection points.
*/
mxConstraintHandler.prototype.getImageForConstraint = function(state, constraint, point)
{
return this.pointImage;
};
/**
* Function: isEventIgnored
*
* Returns true if the given <mxMouseEvent> should be ignored in <update>. This
* implementation always returns false.
*/
mxConstraintHandler.prototype.isEventIgnored = function(me, source)
{
return false;
};
/**
* Function: isStateIgnored
*
* Returns true if the given state should be ignored. This always returns false.
*/
mxConstraintHandler.prototype.isStateIgnored = function(state, source)
{
return false;
};
/**
* Function: destroyIcons
*
* Destroys the <focusIcons> if they exist.
*/
mxConstraintHandler.prototype.destroyIcons = function()
{
if (this.focusIcons != null)
{
for (var i = 0; i < this.focusIcons.length; i++)
{
this.focusIcons[i].destroy();
}
this.focusIcons = null;
this.focusPoints = null;
}
};
/**
* Function: destroyFocusHighlight
*
* Destroys the <focusHighlight> if one exists.
*/
mxConstraintHandler.prototype.destroyFocusHighlight = function()
{
if (this.focusHighlight != null)
{
this.focusHighlight.destroy();
this.focusHighlight = null;
}
};
/**
* Function: isKeepFocusEvent
*
* Returns true if the current focused state should not be changed for the given event.
* This returns true if shift and alt are pressed.
*/
mxConstraintHandler.prototype.isKeepFocusEvent = function(me)
{
return mxEvent.isShiftDown(me.getEvent());
};
/**
* Function: getCellForEvent
*
* Returns the cell for the given event.
*/
mxConstraintHandler.prototype.getCellForEvent = function(me, point)
{
var cell = me.getCell();
// Gets cell under actual point if different from event location
if (cell == null && point != null && (me.getGraphX() != point.x || me.getGraphY() != point.y))
{
cell = this.graph.getCellAt(point.x, point.y);
}
// Uses connectable parent vertex if one exists
if (cell != null && !this.graph.isCellConnectable(cell))
{
var parent = this.graph.getModel().getParent(cell);
if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
{
cell = parent;
}
}
return cell;
};
/**
* Function: update
*
* Updates the state of this handler based on the given <mxMouseEvent>.
* Source is a boolean indicating if the cell is a source or target.
*/
mxConstraintHandler.prototype.update = function(me, source, existingEdge, point)
{
if (this.isEnabled() && !this.isEventIgnored(me))
{
// Lazy installation of mouseleave handler
if (this.mouseleaveHandler == null && this.graph.container != null)
{
this.mouseleaveHandler = mxUtils.bind(this, function()
{
this.reset();
});
mxEvent.addListener(this.graph.container, 'mouseleave', this.resetHandler);
}
var tol = this.getTolerance(me);
var x = (point != null) ? point.x : me.getGraphX();
var y = (point != null) ? point.y : me.getGraphY();
var grid = new mxRectangle(x - tol, y - tol, 2 * tol, 2 * tol);
var mouse = new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol);
var state = this.graph.view.getState(this.getCellForEvent(me, point));
// Keeps focus icons visible while over vertex bounds and no other cell under mouse or shift is pressed
if (!this.isKeepFocusEvent(me) && (this.currentFocusArea == null || this.currentFocus == null ||
(state != null) || !this.graph.getModel().isVertex(this.currentFocus.cell) ||
!mxUtils.intersects(this.currentFocusArea, mouse)) && (state != this.currentFocus))
{
this.currentFocusArea = null;
this.currentFocus = null;
this.setFocus(me, state, source);
}
this.currentConstraint = null;
this.currentPoint = null;
var minDistSq = null;
if (this.focusIcons != null && this.constraints != null &&
(state == null || this.currentFocus == state))
{
var cx = mouse.getCenterX();
var cy = mouse.getCenterY();
for (var i = 0; i < this.focusIcons.length; i++)
{
var dx = cx - this.focusIcons[i].bounds.getCenterX();
var dy = cy - this.focusIcons[i].bounds.getCenterY();
var tmp = dx * dx + dy * dy;
if ((this.intersects(this.focusIcons[i], mouse, source, existingEdge) || (point != null &&
this.intersects(this.focusIcons[i], grid, source, existingEdge))) &&
(minDistSq == null || tmp < minDistSq))
{
this.currentConstraint = this.constraints[i];
this.currentPoint = this.focusPoints[i];
minDistSq = tmp;
var tmp = this.focusIcons[i].bounds.clone();
tmp.grow(mxConstants.HIGHLIGHT_SIZE + 1);
tmp.width -= 1;
tmp.height -= 1;
if (this.focusHighlight == null)
{
var hl = this.createHighlightShape();
hl.dialect = (this.graph.dialect == mxConstants.DIALECT_SVG) ?
mxConstants.DIALECT_SVG : mxConstants.DIALECT_VML;
hl.pointerEvents = false;
hl.init(this.graph.getView().getOverlayPane());
this.focusHighlight = hl;
var getState = mxUtils.bind(this, function()
{
return (this.currentFocus != null) ? this.currentFocus : state;
});
mxEvent.redirectMouseEvents(hl.node, this.graph, getState);
}
this.focusHighlight.bounds = tmp;
this.focusHighlight.redraw();
}
}
}
if (this.currentConstraint == null)
{
this.destroyFocusHighlight();
}
}
else
{
this.currentConstraint = null;
this.currentFocus = null;
this.currentPoint = null;
}
};
/**
* Function: redraw
*
* Transfers the focus to the given state as a source or target terminal. If
* the handler is not enabled then the outline is painted, but the constraints
* are ignored.
*/
mxConstraintHandler.prototype.redraw = function()
{
if (this.currentFocus != null && this.constraints != null && this.focusIcons != null)
{
var state = this.graph.view.getState(this.currentFocus.cell);
this.currentFocus = state;
this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
for (var i = 0; i < this.constraints.length; i++)
{
var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
var img = this.getImageForConstraint(state, this.constraints[i], cp);
var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
Math.round(cp.y - img.height / 2), img.width, img.height);
this.focusIcons[i].bounds = bounds;
this.focusIcons[i].redraw();
this.currentFocusArea.add(this.focusIcons[i].bounds);
this.focusPoints[i] = cp;
}
}
};
/**
* Function: setFocus
*
* Transfers the focus to the given state as a source or target terminal. If
* the handler is not enabled then the outline is painted, but the constraints
* are ignored.
*/
mxConstraintHandler.prototype.setFocus = function(me, state, source)
{
this.constraints = (state != null && !this.isStateIgnored(state, source) &&
this.graph.isCellConnectable(state.cell)) ? ((this.isEnabled()) ?
(this.graph.getAllConnectionConstraints(state, source) || []) : []) : null;
// Only uses cells which have constraints
if (this.constraints != null)
{
this.currentFocus = state;
this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
if (this.focusIcons != null)
{
for (var i = 0; i < this.focusIcons.length; i++)
{
this.focusIcons[i].destroy();
}
this.focusIcons = null;
this.focusPoints = null;
}
this.focusPoints = [];
this.focusIcons = [];
for (var i = 0; i < this.constraints.length; i++)
{
var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
var img = this.getImageForConstraint(state, this.constraints[i], cp);
var src = img.src;
var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
Math.round(cp.y - img.height / 2), img.width, img.height);
var icon = new mxImageShape(bounds, src);
icon.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
icon.preserveImageAspect = false;
icon.init(this.graph.getView().getDecoratorPane());
// Fixes lost event tracking for images in quirks / IE8 standards
if (mxClient.IS_QUIRKS || document.documentMode == 8)
{
mxEvent.addListener(icon.node, 'dragstart', function(evt)
{
mxEvent.consume(evt);
return false;
});
}
// Move the icon behind all other overlays
if (icon.node.previousSibling != null)
{
icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
}
var getState = mxUtils.bind(this, function()
{
return (this.currentFocus != null) ? this.currentFocus : state;
});
icon.redraw();
mxEvent.redirectMouseEvents(icon.node, this.graph, getState);
this.currentFocusArea.add(icon.bounds);
this.focusIcons.push(icon);
this.focusPoints.push(cp);
}
this.currentFocusArea.grow(this.getTolerance(me));
}
else
{
this.destroyIcons();
this.destroyFocusHighlight();
}
};
/**
* Function: createHighlightShape
*
* Create the shape used to paint the highlight.
*
* Returns true if the given icon intersects the given point.
*/
mxConstraintHandler.prototype.createHighlightShape = function()
{
var hl = new mxRectangleShape(null, this.highlightColor, this.highlightColor, mxConstants.HIGHLIGHT_STROKEWIDTH);
hl.opacity = mxConstants.HIGHLIGHT_OPACITY;
return hl;
};
/**
* Function: intersects
*
* Returns true if the given icon intersects the given rectangle.
*/
mxConstraintHandler.prototype.intersects = function(icon, mouse, source, existingEdge)
{
return mxUtils.intersects(icon.bounds, mouse);
};
/**
* Function: destroy
*
* Destroy this handler.
*/
mxConstraintHandler.prototype.destroy = function()
{
this.reset();
if (this.resetHandler != null)
{
this.graph.model.removeListener(this.resetHandler);
this.graph.view.removeListener(this.resetHandler);
this.graph.removeListener(this.resetHandler);
this.resetHandler = null;
}
if (this.mouseleaveHandler != null && this.graph.container != null)
{
mxEvent.removeListener(this.graph.container, 'mouseleave', this.mouseleaveHandler);
this.mouseleaveHandler = null;
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,409 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
function mxEdgeSegmentHandler(state)
{
mxEdgeHandler.call(this, state);
};
/**
* Extends mxEdgeHandler.
*/
mxUtils.extend(mxEdgeSegmentHandler, mxElbowEdgeHandler);
/**
* Function: getCurrentPoints
*
* Returns the current absolute points.
*/
mxEdgeSegmentHandler.prototype.getCurrentPoints = function()
{
var pts = this.state.absolutePoints;
if (pts != null)
{
// Special case for straight edges where we add a virtual middle handle for moving the edge
if (pts.length == 2 || (pts.length == 3 && (pts[0].x == pts[1].x && pts[1].x == pts[2].x ||
pts[0].y == pts[1].y && pts[1].y == pts[2].y)))
{
var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
pts = [pts[0], new mxPoint(cx, cy), new mxPoint(cx, cy), pts[pts.length - 1]];
}
}
return pts;
};
/**
* Function: getPreviewPoints
*
* Updates the given preview state taking into account the state of the constraint handler.
*/
mxEdgeSegmentHandler.prototype.getPreviewPoints = function(point)
{
if (this.isSource || this.isTarget)
{
return mxElbowEdgeHandler.prototype.getPreviewPoints.apply(this, arguments);
}
else
{
var pts = this.getCurrentPoints();
var last = this.convertPoint(pts[0].clone(), false);
point = this.convertPoint(point.clone(), false);
var result = [];
for (var i = 1; i < pts.length; i++)
{
var pt = this.convertPoint(pts[i].clone(), false);
if (i == this.index)
{
if (Math.round(last.x - pt.x) == 0)
{
last.x = point.x;
pt.x = point.x;
}
if (Math.round(last.y - pt.y) == 0)
{
last.y = point.y;
pt.y = point.y;
}
}
if (i < pts.length - 1)
{
result.push(pt);
}
last = pt;
}
// Replaces single point that intersects with source or target
if (result.length == 1)
{
var source = this.state.getVisibleTerminalState(true);
var target = this.state.getVisibleTerminalState(false);
var scale = this.state.view.getScale();
var tr = this.state.view.getTranslate();
var x = result[0].x * scale + tr.x;
var y = result[0].y * scale + tr.y;
if ((source != null && mxUtils.contains(source, x, y)) ||
(target != null && mxUtils.contains(target, x, y)))
{
result = [point, point];
}
}
return result;
}
};
/**
* Function: updatePreviewState
*
* Overridden to perform optimization of the edge style result.
*/
mxEdgeSegmentHandler.prototype.updatePreviewState = function(edge, point, terminalState, me)
{
mxEdgeHandler.prototype.updatePreviewState.apply(this, arguments);
// Checks and corrects preview by running edge style again
if (!this.isSource && !this.isTarget)
{
point = this.convertPoint(point.clone(), false);
var pts = edge.absolutePoints;
var pt0 = pts[0];
var pt1 = pts[1];
var result = [];
for (var i = 2; i < pts.length; i++)
{
var pt2 = pts[i];
// Merges adjacent segments only if more than 2 to allow for straight edges
if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
{
result.push(this.convertPoint(pt1.clone(), false));
}
pt0 = pt1;
pt1 = pt2;
}
var source = this.state.getVisibleTerminalState(true);
var target = this.state.getVisibleTerminalState(false);
var rpts = this.state.absolutePoints;
// A straight line is represented by 3 handles
if (result.length == 0 && (Math.round(pts[0].x - pts[pts.length - 1].x) == 0 ||
Math.round(pts[0].y - pts[pts.length - 1].y) == 0))
{
result = [point, point];
}
// Handles special case of transitions from straight vertical to routed
else if (pts.length == 5 && result.length == 2 && source != null && target != null &&
rpts != null && Math.round(rpts[0].x - rpts[rpts.length - 1].x) == 0)
{
var view = this.graph.getView();
var scale = view.getScale();
var tr = view.getTranslate();
var y0 = view.getRoutingCenterY(source) / scale - tr.y;
// Use fixed connection point y-coordinate if one exists
var sc = this.graph.getConnectionConstraint(edge, source, true);
if (sc != null)
{
var pt = this.graph.getConnectionPoint(source, sc);
if (pt != null)
{
this.convertPoint(pt, false);
y0 = pt.y;
}
}
var ye = view.getRoutingCenterY(target) / scale - tr.y;
// Use fixed connection point y-coordinate if one exists
var tc = this.graph.getConnectionConstraint(edge, target, false);
if (tc)
{
var pt = this.graph.getConnectionPoint(target, tc);
if (pt != null)
{
this.convertPoint(pt, false);
ye = pt.y;
}
}
result = [new mxPoint(point.x, y0), new mxPoint(point.x, ye)];
}
this.points = result;
// LATER: Check if points and result are different
edge.view.updateFixedTerminalPoints(edge, source, target);
edge.view.updatePoints(edge, this.points, source, target);
edge.view.updateFloatingTerminalPoints(edge, source, target);
}
};
/**
* Overriden to merge edge segments.
*/
mxEdgeSegmentHandler.prototype.connect = function(edge, terminal, isSource, isClone, me)
{
var model = this.graph.getModel();
var geo = model.getGeometry(edge);
var result = null;
// Merges adjacent edge segments
if (geo != null && geo.points != null && geo.points.length > 0)
{
var pts = this.abspoints;
var pt0 = pts[0];
var pt1 = pts[1];
result = [];
for (var i = 2; i < pts.length; i++)
{
var pt2 = pts[i];
// Merges adjacent segments only if more than 2 to allow for straight edges
if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
{
result.push(this.convertPoint(pt1.clone(), false));
}
pt0 = pt1;
pt1 = pt2;
}
}
model.beginUpdate();
try
{
if (result != null)
{
var geo = model.getGeometry(edge);
if (geo != null)
{
geo = geo.clone();
geo.points = result;
model.setGeometry(edge, geo);
}
}
edge = mxEdgeHandler.prototype.connect.apply(this, arguments);
}
finally
{
model.endUpdate();
}
return edge;
};
/**
* Function: getTooltipForNode
*
* Returns no tooltips.
*/
mxEdgeSegmentHandler.prototype.getTooltipForNode = function(node)
{
return null;
};
/**
* Function: createBends
*
* Adds custom bends for the center of each segment.
*/
mxEdgeSegmentHandler.prototype.start = function(x, y, index)
{
mxEdgeHandler.prototype.start.apply(this, arguments);
if (this.bends[index] != null && !this.isSource && !this.isTarget)
{
mxUtils.setOpacity(this.bends[index].node, 100);
}
};
/**
* Function: createBends
*
* Adds custom bends for the center of each segment.
*/
mxEdgeSegmentHandler.prototype.createBends = function()
{
var bends = [];
// Source
var bend = this.createHandleShape(0);
this.initBend(bend);
bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
bends.push(bend);
var pts = this.getCurrentPoints();
// Waypoints (segment handles)
if (this.graph.isCellBendable(this.state.cell))
{
if (this.points == null)
{
this.points = [];
}
for (var i = 0; i < pts.length - 1; i++)
{
bend = this.createVirtualBend();
bends.push(bend);
var horizontal = Math.round(pts[i].x - pts[i + 1].x) == 0;
// Special case where dy is 0 as well
if (Math.round(pts[i].y - pts[i + 1].y) == 0 && i < pts.length - 2)
{
horizontal = Math.round(pts[i].x - pts[i + 2].x) == 0;
}
bend.setCursor((horizontal) ? 'col-resize' : 'row-resize');
this.points.push(new mxPoint(0,0));
}
}
// Target
var bend = this.createHandleShape(pts.length);
this.initBend(bend);
bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
bends.push(bend);
return bends;
};
/**
* Function: redraw
*
* Overridden to invoke <refresh> before the redraw.
*/
mxEdgeSegmentHandler.prototype.redraw = function()
{
this.refresh();
mxEdgeHandler.prototype.redraw.apply(this, arguments);
};
/**
* Function: redrawInnerBends
*
* Updates the position of the custom bends.
*/
mxEdgeSegmentHandler.prototype.redrawInnerBends = function(p0, pe)
{
if (this.graph.isCellBendable(this.state.cell))
{
var pts = this.getCurrentPoints();
if (pts != null && pts.length > 1)
{
var straight = false;
// Puts handle in the center of straight edges
if (pts.length == 4 && Math.round(pts[1].x - pts[2].x) == 0 && Math.round(pts[1].y - pts[2].y) == 0)
{
straight = true;
if (Math.round(pts[0].y - pts[pts.length - 1].y) == 0)
{
var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
pts[1] = new mxPoint(cx, pts[1].y);
pts[2] = new mxPoint(cx, pts[2].y);
}
else
{
var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
pts[1] = new mxPoint(pts[1].x, cy);
pts[2] = new mxPoint(pts[2].x, cy);
}
}
for (var i = 0; i < pts.length - 1; i++)
{
if (this.bends[i + 1] != null)
{
var p0 = pts[i];
var pe = pts[i + 1];
var pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
var b = this.bends[i + 1].bounds;
this.bends[i + 1].bounds = new mxRectangle(Math.floor(pt.x - b.width / 2),
Math.floor(pt.y - b.height / 2), b.width, b.height);
this.bends[i + 1].redraw();
if (this.manageLabelHandle)
{
this.checkLabelHandle(this.bends[i + 1].bounds);
}
}
}
if (straight)
{
mxUtils.setOpacity(this.bends[1].node, this.virtualBendOpacity);
mxUtils.setOpacity(this.bends[3].node, this.virtualBendOpacity);
}
}
}
};

View File

@@ -0,0 +1,229 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxElbowEdgeHandler
*
* Graph event handler that reconnects edges and modifies control points and
* the edge label location. Uses <mxTerminalMarker> for finding and
* highlighting new source and target vertices. This handler is automatically
* created in <mxGraph.createHandler>. It extends <mxEdgeHandler>.
*
* Constructor: mxEdgeHandler
*
* Constructs an edge handler for the specified <mxCellState>.
*
* Parameters:
*
* state - <mxCellState> of the cell to be modified.
*/
function mxElbowEdgeHandler(state)
{
mxEdgeHandler.call(this, state);
};
/**
* Extends mxEdgeHandler.
*/
mxUtils.extend(mxElbowEdgeHandler, mxEdgeHandler);
/**
* Specifies if a double click on the middle handle should call
* <mxGraph.flipEdge>. Default is true.
*/
mxElbowEdgeHandler.prototype.flipEnabled = true;
/**
* Variable: doubleClickOrientationResource
*
* Specifies the resource key for the tooltip to be displayed on the single
* control point for routed edges. If the resource for this key does not
* exist then the value is used as the error message. Default is
* 'doubleClickOrientation'.
*/
mxElbowEdgeHandler.prototype.doubleClickOrientationResource =
(mxClient.language != 'none') ? 'doubleClickOrientation' : '';
/**
* Function: createBends
*
* Overrides <mxEdgeHandler.createBends> to create custom bends.
*/
mxElbowEdgeHandler.prototype.createBends = function()
{
var bends = [];
// Source
var bend = this.createHandleShape(0);
this.initBend(bend);
bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
bends.push(bend);
// Virtual
bends.push(this.createVirtualBend(mxUtils.bind(this, function(evt)
{
if (!mxEvent.isConsumed(evt) && this.flipEnabled)
{
this.graph.flipEdge(this.state.cell, evt);
mxEvent.consume(evt);
}
})));
this.points.push(new mxPoint(0,0));
// Target
bend = this.createHandleShape(2);
this.initBend(bend);
bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
bends.push(bend);
return bends;
};
/**
* Function: createVirtualBend
*
* Creates a virtual bend that supports double clicking and calls
* <mxGraph.flipEdge>.
*/
mxElbowEdgeHandler.prototype.createVirtualBend = function(dblClickHandler)
{
var bend = this.createHandleShape();
this.initBend(bend, dblClickHandler);
bend.setCursor(this.getCursorForBend());
if (!this.graph.isCellBendable(this.state.cell))
{
bend.node.style.display = 'none';
}
return bend;
};
/**
* Function: getCursorForBend
*
* Returns the cursor to be used for the bend.
*/
mxElbowEdgeHandler.prototype.getCursorForBend = function()
{
return (this.state.style[mxConstants.STYLE_EDGE] == mxEdgeStyle.TopToBottom ||
this.state.style[mxConstants.STYLE_EDGE] == mxConstants.EDGESTYLE_TOPTOBOTTOM ||
((this.state.style[mxConstants.STYLE_EDGE] == mxEdgeStyle.ElbowConnector ||
this.state.style[mxConstants.STYLE_EDGE] == mxConstants.EDGESTYLE_ELBOW)&&
this.state.style[mxConstants.STYLE_ELBOW] == mxConstants.ELBOW_VERTICAL)) ?
'row-resize' : 'col-resize';
};
/**
* Function: getTooltipForNode
*
* Returns the tooltip for the given node.
*/
mxElbowEdgeHandler.prototype.getTooltipForNode = function(node)
{
var tip = null;
if (this.bends != null && this.bends[1] != null && (node == this.bends[1].node ||
node.parentNode == this.bends[1].node))
{
tip = this.doubleClickOrientationResource;
tip = mxResources.get(tip) || tip; // translate
}
return tip;
};
/**
* Function: convertPoint
*
* Converts the given point in-place from screen to unscaled, untranslated
* graph coordinates and applies the grid.
*
* Parameters:
*
* point - <mxPoint> to be converted.
* gridEnabled - Boolean that specifies if the grid should be applied.
*/
mxElbowEdgeHandler.prototype.convertPoint = function(point, gridEnabled)
{
var scale = this.graph.getView().getScale();
var tr = this.graph.getView().getTranslate();
var origin = this.state.origin;
if (gridEnabled)
{
point.x = this.graph.snap(point.x);
point.y = this.graph.snap(point.y);
}
point.x = Math.round(point.x / scale - tr.x - origin.x);
point.y = Math.round(point.y / scale - tr.y - origin.y);
return point;
};
/**
* Function: redrawInnerBends
*
* Updates and redraws the inner bends.
*
* Parameters:
*
* p0 - <mxPoint> that represents the location of the first point.
* pe - <mxPoint> that represents the location of the last point.
*/
mxElbowEdgeHandler.prototype.redrawInnerBends = function(p0, pe)
{
var g = this.graph.getModel().getGeometry(this.state.cell);
var pts = this.state.absolutePoints;
var pt = null;
// Keeps the virtual bend on the edge shape
if (pts.length > 1)
{
p0 = pts[1];
pe = pts[pts.length - 2];
}
else if (g.points != null && g.points.length > 0)
{
pt = pts[0];
}
if (pt == null)
{
pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
}
else
{
pt = new mxPoint(this.graph.getView().scale * (pt.x + this.graph.getView().translate.x + this.state.origin.x),
this.graph.getView().scale * (pt.y + this.graph.getView().translate.y + this.state.origin.y));
}
// Makes handle slightly bigger if the yellow label handle
// exists and intersects this green handle
var b = this.bends[1].bounds;
var w = b.width;
var h = b.height;
var bounds = new mxRectangle(Math.round(pt.x - w / 2), Math.round(pt.y - h / 2), w, h);
if (this.manageLabelHandle)
{
this.checkLabelHandle(bounds);
}
else if (this.handleImage == null && this.labelShape.visible && mxUtils.intersects(bounds, this.labelShape.bounds))
{
w = mxConstants.HANDLE_SIZE + 3;
h = mxConstants.HANDLE_SIZE + 3;
bounds = new mxRectangle(Math.floor(pt.x - w / 2), Math.floor(pt.y - h / 2), w, h);
}
this.bends[1].bounds = bounds;
this.bends[1].redraw();
if (this.manageLabelHandle)
{
this.checkLabelHandle(this.bends[1].bounds);
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,351 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxHandle
*
* Implements a single custom handle for vertices.
*
* Constructor: mxHandle
*
* Constructs a new handle for the given state.
*
* Parameters:
*
* state - <mxCellState> of the cell to be handled.
*/
function mxHandle(state, cursor, image)
{
this.graph = state.view.graph;
this.state = state;
this.cursor = (cursor != null) ? cursor : this.cursor;
this.image = (image != null) ? image : this.image;
this.init();
};
/**
* Variable: cursor
*
* Specifies the cursor to be used for this handle. Default is 'default'.
*/
mxHandle.prototype.cursor = 'default';
/**
* Variable: image
*
* Specifies the <mxImage> to be used to render the handle. Default is null.
*/
mxHandle.prototype.image = null;
/**
* Variable: image
*
* Specifies the <mxImage> to be used to render the handle. Default is null.
*/
mxHandle.prototype.ignoreGrid = false;
/**
* Function: getPosition
*
* Hook for subclassers to return the current position of the handle.
*/
mxHandle.prototype.getPosition = function(bounds) { };
/**
* Function: setPosition
*
* Hooks for subclassers to update the style in the <state>.
*/
mxHandle.prototype.setPosition = function(bounds, pt, me) { };
/**
* Function: execute
*
* Hook for subclassers to execute the handle.
*/
mxHandle.prototype.execute = function() { };
/**
* Function: copyStyle
*
* Sets the cell style with the given name to the corresponding value in <state>.
*/
mxHandle.prototype.copyStyle = function(key)
{
this.graph.setCellStyles(key, this.state.style[key], [this.state.cell]);
};
/**
* Function: processEvent
*
* Processes the given <mxMouseEvent> and invokes <setPosition>.
*/
mxHandle.prototype.processEvent = function(me)
{
var scale = this.graph.view.scale;
var tr = this.graph.view.translate;
var pt = new mxPoint(me.getGraphX() / scale - tr.x, me.getGraphY() / scale - tr.y);
// Center shape on mouse cursor
if (this.shape != null && this.shape.bounds != null)
{
pt.x -= this.shape.bounds.width / scale / 4;
pt.y -= this.shape.bounds.height / scale / 4;
}
// Snaps to grid for the rotated position then applies the rotation for the direction after that
var alpha1 = -mxUtils.toRadians(this.getRotation());
var alpha2 = -mxUtils.toRadians(this.getTotalRotation()) - alpha1;
pt = this.flipPoint(this.rotatePoint(this.snapPoint(this.rotatePoint(pt, alpha1),
this.ignoreGrid || !this.graph.isGridEnabledEvent(me.getEvent())), alpha2));
this.setPosition(this.state.getPaintBounds(), pt, me);
this.positionChanged();
this.redraw();
};
/**
* Function: positionChanged
*
* Called after <setPosition> has been called in <processEvent>. This repaints
* the state using <mxCellRenderer>.
*/
mxHandle.prototype.positionChanged = function()
{
if (this.state.text != null)
{
this.state.text.apply(this.state);
}
if (this.state.shape != null)
{
this.state.shape.apply(this.state);
}
this.graph.cellRenderer.redraw(this.state, true);
};
/**
* Function: getRotation
*
* Returns the rotation defined in the style of the cell.
*/
mxHandle.prototype.getRotation = function()
{
if (this.state.shape != null)
{
return this.state.shape.getRotation();
}
return 0;
};
/**
* Function: getTotalRotation
*
* Returns the rotation from the style and the rotation from the direction of
* the cell.
*/
mxHandle.prototype.getTotalRotation = function()
{
if (this.state.shape != null)
{
return this.state.shape.getShapeRotation();
}
return 0;
};
/**
* Function: init
*
* Creates and initializes the shapes required for this handle.
*/
mxHandle.prototype.init = function()
{
var html = this.isHtmlRequired();
if (this.image != null)
{
this.shape = new mxImageShape(new mxRectangle(0, 0, this.image.width, this.image.height), this.image.src);
this.shape.preserveImageAspect = false;
}
else
{
this.shape = this.createShape(html);
}
this.initShape(html);
};
/**
* Function: createShape
*
* Creates and returns the shape for this handle.
*/
mxHandle.prototype.createShape = function(html)
{
var bounds = new mxRectangle(0, 0, mxConstants.HANDLE_SIZE, mxConstants.HANDLE_SIZE);
return new mxRectangleShape(bounds, mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
};
/**
* Function: initShape
*
* Initializes <shape> and sets its cursor.
*/
mxHandle.prototype.initShape = function(html)
{
if (html && this.shape.isHtmlAllowed())
{
this.shape.dialect = mxConstants.DIALECT_STRICTHTML;
this.shape.init(this.graph.container);
}
else
{
this.shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
if (this.cursor != null)
{
this.shape.init(this.graph.getView().getOverlayPane());
}
}
mxEvent.redirectMouseEvents(this.shape.node, this.graph, this.state);
this.shape.node.style.cursor = this.cursor;
};
/**
* Function: redraw
*
* Renders the shape for this handle.
*/
mxHandle.prototype.redraw = function()
{
if (this.shape != null && this.state.shape != null)
{
var pt = this.getPosition(this.state.getPaintBounds());
if (pt != null)
{
var alpha = mxUtils.toRadians(this.getTotalRotation());
pt = this.rotatePoint(this.flipPoint(pt), alpha);
var scale = this.graph.view.scale;
var tr = this.graph.view.translate;
this.shape.bounds.x = Math.floor((pt.x + tr.x) * scale - this.shape.bounds.width / 2);
this.shape.bounds.y = Math.floor((pt.y + tr.y) * scale - this.shape.bounds.height / 2);
// Needed to force update of text bounds
this.shape.redraw();
}
}
};
/**
* Function: isHtmlRequired
*
* Returns true if this handle should be rendered in HTML. This returns true if
* the text node is in the graph container.
*/
mxHandle.prototype.isHtmlRequired = function()
{
return this.state.text != null && this.state.text.node.parentNode == this.graph.container;
};
/**
* Function: rotatePoint
*
* Rotates the point by the given angle.
*/
mxHandle.prototype.rotatePoint = function(pt, alpha)
{
var bounds = this.state.getCellBounds();
var cx = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
var cos = Math.cos(alpha);
var sin = Math.sin(alpha);
return mxUtils.getRotatedPoint(pt, cos, sin, cx);
};
/**
* Function: flipPoint
*
* Flips the given point vertically and/or horizontally.
*/
mxHandle.prototype.flipPoint = function(pt)
{
if (this.state.shape != null)
{
var bounds = this.state.getCellBounds();
if (this.state.shape.flipH)
{
pt.x = 2 * bounds.x + bounds.width - pt.x;
}
if (this.state.shape.flipV)
{
pt.y = 2 * bounds.y + bounds.height - pt.y;
}
}
return pt;
};
/**
* Function: snapPoint
*
* Snaps the given point to the grid if ignore is false. This modifies
* the given point in-place and also returns it.
*/
mxHandle.prototype.snapPoint = function(pt, ignore)
{
if (!ignore)
{
pt.x = this.graph.snap(pt.x);
pt.y = this.graph.snap(pt.y);
}
return pt;
};
/**
* Function: setVisible
*
* Shows or hides this handle.
*/
mxHandle.prototype.setVisible = function(visible)
{
if (this.shape != null && this.shape.node != null)
{
this.shape.node.style.display = (visible) ? '' : 'none';
}
};
/**
* Function: reset
*
* Resets the state of this handle by setting its visibility to true.
*/
mxHandle.prototype.reset = function()
{
this.setVisible(true);
this.state.style = this.graph.getCellStyle(this.state.cell);
this.positionChanged();
};
/**
* Function: destroy
*
* Destroys this handle.
*/
mxHandle.prototype.destroy = function()
{
if (this.shape != null)
{
this.shape.destroy();
this.shape = null;
}
};

View File

@@ -0,0 +1,428 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxKeyHandler
*
* Event handler that listens to keystroke events. This is not a singleton,
* however, it is normally only required once if the target is the document
* element (default).
*
* This handler installs a key event listener in the topmost DOM node and
* processes all events that originate from descandants of <mxGraph.container>
* or from the topmost DOM node. The latter means that all unhandled keystrokes
* are handled by this object regardless of the focused state of the <graph>.
*
* Example:
*
* The following example creates a key handler that listens to the delete key
* (46) and deletes the selection cells if the graph is enabled.
*
* (code)
* var keyHandler = new mxKeyHandler(graph);
* keyHandler.bindKey(46, function(evt)
* {
* if (graph.isEnabled())
* {
* graph.removeCells();
* }
* });
* (end)
*
* Keycodes:
*
* See http://tinyurl.com/yp8jgl or http://tinyurl.com/229yqw for a list of
* keycodes or install a key event listener into the document element and print
* the key codes of the respective events to the console.
*
* To support the Command key and the Control key on the Mac, the following
* code can be used.
*
* (code)
* keyHandler.getFunction = function(evt)
* {
* if (evt != null)
* {
* return (mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey)) ? this.controlKeys[evt.keyCode] : this.normalKeys[evt.keyCode];
* }
*
* return null;
* };
* (end)
*
* Constructor: mxKeyHandler
*
* Constructs an event handler that executes functions bound to specific
* keystrokes.
*
* Parameters:
*
* graph - Reference to the associated <mxGraph>.
* target - Optional reference to the event target. If null, the document
* element is used as the event target, that is, the object where the key
* event listener is installed.
*/
function mxKeyHandler(graph, target)
{
if (graph != null)
{
this.graph = graph;
this.target = target || document.documentElement;
// Creates the arrays to map from keycodes to functions
this.normalKeys = [];
this.shiftKeys = [];
this.controlKeys = [];
this.controlShiftKeys = [];
this.keydownHandler = mxUtils.bind(this, function(evt)
{
this.keyDown(evt);
});
// Installs the keystroke listener in the target
mxEvent.addListener(this.target, 'keydown', this.keydownHandler);
// Automatically deallocates memory in IE
if (mxClient.IS_IE)
{
mxEvent.addListener(window, 'unload',
mxUtils.bind(this, function()
{
this.destroy();
})
);
}
}
};
/**
* Variable: graph
*
* Reference to the <mxGraph> associated with this handler.
*/
mxKeyHandler.prototype.graph = null;
/**
* Variable: target
*
* Reference to the target DOM, that is, the DOM node where the key event
* listeners are installed.
*/
mxKeyHandler.prototype.target = null;
/**
* Variable: normalKeys
*
* Maps from keycodes to functions for non-pressed control keys.
*/
mxKeyHandler.prototype.normalKeys = null;
/**
* Variable: shiftKeys
*
* Maps from keycodes to functions for pressed shift keys.
*/
mxKeyHandler.prototype.shiftKeys = null;
/**
* Variable: controlKeys
*
* Maps from keycodes to functions for pressed control keys.
*/
mxKeyHandler.prototype.controlKeys = null;
/**
* Variable: controlShiftKeys
*
* Maps from keycodes to functions for pressed control and shift keys.
*/
mxKeyHandler.prototype.controlShiftKeys = null;
/**
* Variable: enabled
*
* Specifies if events are handled. Default is true.
*/
mxKeyHandler.prototype.enabled = true;
/**
* Function: isEnabled
*
* Returns true if events are handled. This implementation returns
* <enabled>.
*/
mxKeyHandler.prototype.isEnabled = function()
{
return this.enabled;
};
/**
* Function: setEnabled
*
* Enables or disables event handling by updating <enabled>.
*
* Parameters:
*
* enabled - Boolean that specifies the new enabled state.
*/
mxKeyHandler.prototype.setEnabled = function(enabled)
{
this.enabled = enabled;
};
/**
* Function: bindKey
*
* Binds the specified keycode to the given function. This binding is used
* if the control key is not pressed.
*
* Parameters:
*
* code - Integer that specifies the keycode.
* funct - JavaScript function that takes the key event as an argument.
*/
mxKeyHandler.prototype.bindKey = function(code, funct)
{
this.normalKeys[code] = funct;
};
/**
* Function: bindShiftKey
*
* Binds the specified keycode to the given function. This binding is used
* if the shift key is pressed.
*
* Parameters:
*
* code - Integer that specifies the keycode.
* funct - JavaScript function that takes the key event as an argument.
*/
mxKeyHandler.prototype.bindShiftKey = function(code, funct)
{
this.shiftKeys[code] = funct;
};
/**
* Function: bindControlKey
*
* Binds the specified keycode to the given function. This binding is used
* if the control key is pressed.
*
* Parameters:
*
* code - Integer that specifies the keycode.
* funct - JavaScript function that takes the key event as an argument.
*/
mxKeyHandler.prototype.bindControlKey = function(code, funct)
{
this.controlKeys[code] = funct;
};
/**
* Function: bindControlShiftKey
*
* Binds the specified keycode to the given function. This binding is used
* if the control and shift key are pressed.
*
* Parameters:
*
* code - Integer that specifies the keycode.
* funct - JavaScript function that takes the key event as an argument.
*/
mxKeyHandler.prototype.bindControlShiftKey = function(code, funct)
{
this.controlShiftKeys[code] = funct;
};
/**
* Function: isControlDown
*
* Returns true if the control key is pressed. This uses <mxEvent.isControlDown>.
*
* Parameters:
*
* evt - Key event whose control key pressed state should be returned.
*/
mxKeyHandler.prototype.isControlDown = function(evt)
{
return mxEvent.isControlDown(evt);
};
/**
* Function: getFunction
*
* Returns the function associated with the given key event or null if no
* function is associated with the given event.
*
* Parameters:
*
* evt - Key event whose associated function should be returned.
*/
mxKeyHandler.prototype.getFunction = function(evt)
{
if (evt != null && !mxEvent.isAltDown(evt))
{
if (this.isControlDown(evt))
{
if (mxEvent.isShiftDown(evt))
{
return this.controlShiftKeys[evt.keyCode];
}
else
{
return this.controlKeys[evt.keyCode];
}
}
else
{
if (mxEvent.isShiftDown(evt))
{
return this.shiftKeys[evt.keyCode];
}
else
{
return this.normalKeys[evt.keyCode];
}
}
}
return null;
};
/**
* Function: isGraphEvent
*
* Returns true if the event should be processed by this handler, that is,
* if the event source is either the target, one of its direct children, a
* descendant of the <mxGraph.container>, or the <mxGraph.cellEditor> of the
* <graph>.
*
* Parameters:
*
* evt - Key event that represents the keystroke.
*/
mxKeyHandler.prototype.isGraphEvent = function(evt)
{
var source = mxEvent.getSource(evt);
// Accepts events from the target object or
// in-place editing inside graph
if ((source == this.target || source.parentNode == this.target) ||
(this.graph.cellEditor != null && this.graph.cellEditor.isEventSource(evt)))
{
return true;
}
// Accepts events from inside the container
return mxUtils.isAncestorNode(this.graph.container, source);
};
/**
* Function: keyDown
*
* Handles the event by invoking the function bound to the respective keystroke
* if <isEnabledForEvent> returns true for the given event and if
* <isEventIgnored> returns false, except for escape for which
* <isEventIgnored> is not invoked.
*
* Parameters:
*
* evt - Key event that represents the keystroke.
*/
mxKeyHandler.prototype.keyDown = function(evt)
{
if (this.isEnabledForEvent(evt))
{
// Cancels the editing if escape is pressed
if (evt.keyCode == 27 /* Escape */)
{
this.escape(evt);
}
// Invokes the function for the keystroke
else if (!this.isEventIgnored(evt))
{
var boundFunction = this.getFunction(evt);
if (boundFunction != null)
{
boundFunction(evt);
mxEvent.consume(evt);
}
}
}
};
/**
* Function: isEnabledForEvent
*
* Returns true if the given event should be handled. <isEventIgnored> is
* called later if the event is not an escape key stroke, in which case
* <escape> is called. This implementation returns true if <isEnabled>
* returns true for both, this handler and <graph>, if the event is not
* consumed and if <isGraphEvent> returns true.
*
* Parameters:
*
* evt - Key event that represents the keystroke.
*/
mxKeyHandler.prototype.isEnabledForEvent = function(evt)
{
return (this.graph.isEnabled() && !mxEvent.isConsumed(evt) &&
this.isGraphEvent(evt) && this.isEnabled());
};
/**
* Function: isEventIgnored
*
* Returns true if the given keystroke should be ignored. This returns
* graph.isEditing().
*
* Parameters:
*
* evt - Key event that represents the keystroke.
*/
mxKeyHandler.prototype.isEventIgnored = function(evt)
{
return this.graph.isEditing();
};
/**
* Function: escape
*
* Hook to process ESCAPE keystrokes. This implementation invokes
* <mxGraph.stopEditing> to cancel the current editing, connecting
* and/or other ongoing modifications.
*
* Parameters:
*
* evt - Key event that represents the keystroke. Possible keycode in this
* case is 27 (ESCAPE).
*/
mxKeyHandler.prototype.escape = function(evt)
{
if (this.graph.isEscapeEnabled())
{
this.graph.escape(evt);
}
};
/**
* Function: destroy
*
* Destroys the handler and all its references into the DOM. This does
* normally not need to be called, it is called automatically when the
* window unloads (in IE).
*/
mxKeyHandler.prototype.destroy = function()
{
if (this.target != null && this.keydownHandler != null)
{
mxEvent.removeListener(this.target, 'keydown', this.keydownHandler);
this.keydownHandler = null;
}
this.target = null;
};

View File

@@ -0,0 +1,485 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxPanningHandler
*
* Event handler that pans and creates popupmenus. To use the left
* mousebutton for panning without interfering with cell moving and
* resizing, use <isUseLeftButton> and <isIgnoreCell>. For grid size
* steps while panning, use <useGrid>. This handler is built-into
* <mxGraph.panningHandler> and enabled using <mxGraph.setPanning>.
*
* Constructor: mxPanningHandler
*
* Constructs an event handler that creates a <mxPopupMenu>
* and pans the graph.
*
* Event: mxEvent.PAN_START
*
* Fires when the panning handler changes its <active> state to true. The
* <code>event</code> property contains the corresponding <mxMouseEvent>.
*
* Event: mxEvent.PAN
*
* Fires while handle is processing events. The <code>event</code> property contains
* the corresponding <mxMouseEvent>.
*
* Event: mxEvent.PAN_END
*
* Fires when the panning handler changes its <active> state to false. The
* <code>event</code> property contains the corresponding <mxMouseEvent>.
*/
function mxPanningHandler(graph)
{
if (graph != null)
{
this.graph = graph;
this.graph.addMouseListener(this);
// Handles force panning event
this.forcePanningHandler = mxUtils.bind(this, function(sender, evt)
{
var evtName = evt.getProperty('eventName');
var me = evt.getProperty('event');
if (evtName == mxEvent.MOUSE_DOWN && this.isForcePanningEvent(me))
{
this.start(me);
this.active = true;
this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me));
me.consume();
}
});
this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.forcePanningHandler);
// Handles pinch gestures
this.gestureHandler = mxUtils.bind(this, function(sender, eo)
{
if (this.isPinchEnabled())
{
var evt = eo.getProperty('event');
if (!mxEvent.isConsumed(evt) && evt.type == 'gesturestart')
{
this.initialScale = this.graph.view.scale;
// Forces start of panning when pinch gesture starts
if (!this.active && this.mouseDownEvent != null)
{
this.start(this.mouseDownEvent);
this.mouseDownEvent = null;
}
}
else if (evt.type == 'gestureend' && this.initialScale != null)
{
this.initialScale = null;
}
if (this.initialScale != null)
{
var value = Math.round(this.initialScale * evt.scale * 100) / 100;
if (this.minScale != null)
{
value = Math.max(this.minScale, value);
}
if (this.maxScale != null)
{
value = Math.min(this.maxScale, value);
}
if (this.graph.view.scale != value)
{
this.graph.zoomTo(value);
mxEvent.consume(evt);
}
}
}
});
this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
this.mouseUpListener = mxUtils.bind(this, function()
{
if (this.active)
{
this.reset();
}
});
// Stops scrolling on every mouseup anywhere in the document
mxEvent.addListener(document, 'mouseup', this.mouseUpListener);
}
};
/**
* Extends mxEventSource.
*/
mxPanningHandler.prototype = new mxEventSource();
mxPanningHandler.prototype.constructor = mxPanningHandler;
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
mxPanningHandler.prototype.graph = null;
/**
* Variable: useLeftButtonForPanning
*
* Specifies if panning should be active for the left mouse button.
* Setting this to true may conflict with <mxRubberband>. Default is false.
*/
mxPanningHandler.prototype.useLeftButtonForPanning = false;
/**
* Variable: usePopupTrigger
*
* Specifies if <mxEvent.isPopupTrigger> should also be used for panning.
*/
mxPanningHandler.prototype.usePopupTrigger = true;
/**
* Variable: ignoreCell
*
* Specifies if panning should be active even if there is a cell under the
* mousepointer. Default is false.
*/
mxPanningHandler.prototype.ignoreCell = false;
/**
* Variable: previewEnabled
*
* Specifies if the panning should be previewed. Default is true.
*/
mxPanningHandler.prototype.previewEnabled = true;
/**
* Variable: useGrid
*
* Specifies if the panning steps should be aligned to the grid size.
* Default is false.
*/
mxPanningHandler.prototype.useGrid = false;
/**
* Variable: panningEnabled
*
* Specifies if panning should be enabled. Default is true.
*/
mxPanningHandler.prototype.panningEnabled = true;
/**
* Variable: pinchEnabled
*
* Specifies if pinch gestures should be handled as zoom. Default is true.
*/
mxPanningHandler.prototype.pinchEnabled = true;
/**
* Variable: maxScale
*
* Specifies the maximum scale. Default is 8.
*/
mxPanningHandler.prototype.maxScale = 8;
/**
* Variable: minScale
*
* Specifies the minimum scale. Default is 0.01.
*/
mxPanningHandler.prototype.minScale = 0.01;
/**
* Variable: dx
*
* Holds the current horizontal offset.
*/
mxPanningHandler.prototype.dx = null;
/**
* Variable: dy
*
* Holds the current vertical offset.
*/
mxPanningHandler.prototype.dy = null;
/**
* Variable: startX
*
* Holds the x-coordinate of the start point.
*/
mxPanningHandler.prototype.startX = 0;
/**
* Variable: startY
*
* Holds the y-coordinate of the start point.
*/
mxPanningHandler.prototype.startY = 0;
/**
* Function: isActive
*
* Returns true if the handler is currently active.
*/
mxPanningHandler.prototype.isActive = function()
{
return this.active || this.initialScale != null;
};
/**
* Function: isPanningEnabled
*
* Returns <panningEnabled>.
*/
mxPanningHandler.prototype.isPanningEnabled = function()
{
return this.panningEnabled;
};
/**
* Function: setPanningEnabled
*
* Sets <panningEnabled>.
*/
mxPanningHandler.prototype.setPanningEnabled = function(value)
{
this.panningEnabled = value;
};
/**
* Function: isPinchEnabled
*
* Returns <pinchEnabled>.
*/
mxPanningHandler.prototype.isPinchEnabled = function()
{
return this.pinchEnabled;
};
/**
* Function: setPinchEnabled
*
* Sets <pinchEnabled>.
*/
mxPanningHandler.prototype.setPinchEnabled = function(value)
{
this.pinchEnabled = value;
};
/**
* Function: isPanningTrigger
*
* Returns true if the given event is a panning trigger for the optional
* given cell. This returns true if control-shift is pressed or if
* <usePopupTrigger> is true and the event is a popup trigger.
*/
mxPanningHandler.prototype.isPanningTrigger = function(me)
{
var evt = me.getEvent();
return (this.useLeftButtonForPanning && me.getState() == null &&
mxEvent.isLeftMouseButton(evt)) || (mxEvent.isControlDown(evt) &&
mxEvent.isShiftDown(evt)) || (this.usePopupTrigger && mxEvent.isPopupTrigger(evt));
};
/**
* Function: isForcePanningEvent
*
* Returns true if the given <mxMouseEvent> should start panning. This
* implementation always returns true if <ignoreCell> is true or for
* multi touch events.
*/
mxPanningHandler.prototype.isForcePanningEvent = function(me)
{
return this.ignoreCell || mxEvent.isMultiTouchEvent(me.getEvent());
};
/**
* Function: mouseDown
*
* Handles the event by initiating the panning. By consuming the event all
* subsequent events of the gesture are redirected to this handler.
*/
mxPanningHandler.prototype.mouseDown = function(sender, me)
{
this.mouseDownEvent = me;
if (!me.isConsumed() && this.isPanningEnabled() && !this.active && this.isPanningTrigger(me))
{
this.start(me);
this.consumePanningTrigger(me);
}
};
/**
* Function: start
*
* Starts panning at the given event.
*/
mxPanningHandler.prototype.start = function(me)
{
this.dx0 = -this.graph.container.scrollLeft;
this.dy0 = -this.graph.container.scrollTop;
// Stores the location of the trigger event
this.startX = me.getX();
this.startY = me.getY();
this.dx = null;
this.dy = null;
this.panningTrigger = true;
};
/**
* Function: consumePanningTrigger
*
* Consumes the given <mxMouseEvent> if it was a panning trigger in
* <mouseDown>. The default is to invoke <mxMouseEvent.consume>. Note that this
* will block any further event processing. If you haven't disabled built-in
* context menus and require immediate selection of the cell on mouseDown in
* Safari and/or on the Mac, then use the following code:
*
* (code)
* mxPanningHandler.prototype.consumePanningTrigger = function(me)
* {
* if (me.evt.preventDefault)
* {
* me.evt.preventDefault();
* }
*
* // Stops event processing in IE
* me.evt.returnValue = false;
*
* // Sets local consumed state
* if (!mxClient.IS_SF && !mxClient.IS_MAC)
* {
* me.consumed = true;
* }
* };
* (end)
*/
mxPanningHandler.prototype.consumePanningTrigger = function(me)
{
me.consume();
};
/**
* Function: mouseMove
*
* Handles the event by updating the panning on the graph.
*/
mxPanningHandler.prototype.mouseMove = function(sender, me)
{
this.dx = me.getX() - this.startX;
this.dy = me.getY() - this.startY;
if (this.active)
{
if (this.previewEnabled)
{
// Applies the grid to the panning steps
if (this.useGrid)
{
this.dx = this.graph.snap(this.dx);
this.dy = this.graph.snap(this.dy);
}
this.graph.panGraph(this.dx + this.dx0, this.dy + this.dy0);
}
this.fireEvent(new mxEventObject(mxEvent.PAN, 'event', me));
}
else if (this.panningTrigger)
{
var tmp = this.active;
// Panning is activated only if the mouse is moved
// beyond the graph tolerance
this.active = Math.abs(this.dx) > this.graph.tolerance || Math.abs(this.dy) > this.graph.tolerance;
if (!tmp && this.active)
{
this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me));
}
}
if (this.active || this.panningTrigger)
{
me.consume();
}
};
/**
* Function: mouseUp
*
* Handles the event by setting the translation on the view or showing the
* popupmenu.
*/
mxPanningHandler.prototype.mouseUp = function(sender, me)
{
if (this.active)
{
if (this.dx != null && this.dy != null)
{
// Ignores if scrollbars have been used for panning
if (!this.graph.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.graph.container))
{
var scale = this.graph.getView().scale;
var t = this.graph.getView().translate;
this.graph.panGraph(0, 0);
this.panGraph(t.x + this.dx / scale, t.y + this.dy / scale);
}
me.consume();
}
this.fireEvent(new mxEventObject(mxEvent.PAN_END, 'event', me));
}
this.reset();
};
/**
* Function: mouseUp
*
* Handles the event by setting the translation on the view or showing the
* popupmenu.
*/
mxPanningHandler.prototype.reset = function()
{
this.panningTrigger = false;
this.mouseDownEvent = null;
this.active = false;
this.dx = null;
this.dy = null;
};
/**
* Function: panGraph
*
* Pans <graph> by the given amount.
*/
mxPanningHandler.prototype.panGraph = function(dx, dy)
{
this.graph.getView().setTranslate(dx, dy);
};
/**
* Function: destroy
*
* Destroys the handler and all its resources and DOM nodes.
*/
mxPanningHandler.prototype.destroy = function()
{
this.graph.removeMouseListener(this);
this.graph.removeListener(this.forcePanningHandler);
this.graph.removeListener(this.gestureHandler);
mxEvent.removeListener(document, 'mouseup', this.mouseUpListener);
};

View File

@@ -0,0 +1,218 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxPopupMenuHandler
*
* Event handler that creates popupmenus.
*
* Constructor: mxPopupMenuHandler
*
* Constructs an event handler that creates a <mxPopupMenu>.
*/
function mxPopupMenuHandler(graph, factoryMethod)
{
if (graph != null)
{
this.graph = graph;
this.factoryMethod = factoryMethod;
this.graph.addMouseListener(this);
// Does not show menu if any touch gestures take place after the trigger
this.gestureHandler = mxUtils.bind(this, function(sender, eo)
{
this.inTolerance = false;
});
this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
this.init();
}
};
/**
* Extends mxPopupMenu.
*/
mxPopupMenuHandler.prototype = new mxPopupMenu();
mxPopupMenuHandler.prototype.constructor = mxPopupMenuHandler;
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
mxPopupMenuHandler.prototype.graph = null;
/**
* Variable: selectOnPopup
*
* Specifies if cells should be selected if a popupmenu is displayed for
* them. Default is true.
*/
mxPopupMenuHandler.prototype.selectOnPopup = true;
/**
* Variable: clearSelectionOnBackground
*
* Specifies if cells should be deselected if a popupmenu is displayed for
* the diagram background. Default is true.
*/
mxPopupMenuHandler.prototype.clearSelectionOnBackground = true;
/**
* Variable: triggerX
*
* X-coordinate of the mouse down event.
*/
mxPopupMenuHandler.prototype.triggerX = null;
/**
* Variable: triggerY
*
* Y-coordinate of the mouse down event.
*/
mxPopupMenuHandler.prototype.triggerY = null;
/**
* Variable: screenX
*
* Screen X-coordinate of the mouse down event.
*/
mxPopupMenuHandler.prototype.screenX = null;
/**
* Variable: screenY
*
* Screen Y-coordinate of the mouse down event.
*/
mxPopupMenuHandler.prototype.screenY = null;
/**
* Function: init
*
* Initializes the shapes required for this vertex handler.
*/
mxPopupMenuHandler.prototype.init = function()
{
// Supercall
mxPopupMenu.prototype.init.apply(this);
// Hides the tooltip if the mouse is over
// the context menu
mxEvent.addGestureListeners(this.div, mxUtils.bind(this, function(evt)
{
this.graph.tooltipHandler.hide();
}));
};
/**
* Function: isSelectOnPopup
*
* Hook for returning if a cell should be selected for a given <mxMouseEvent>.
* This implementation returns <selectOnPopup>.
*/
mxPopupMenuHandler.prototype.isSelectOnPopup = function(me)
{
return this.selectOnPopup;
};
/**
* Function: mouseDown
*
* Handles the event by initiating the panning. By consuming the event all
* subsequent events of the gesture are redirected to this handler.
*/
mxPopupMenuHandler.prototype.mouseDown = function(sender, me)
{
if (this.isEnabled() && !mxEvent.isMultiTouchEvent(me.getEvent()))
{
// Hides the popupmenu if is is being displayed
this.hideMenu();
this.triggerX = me.getGraphX();
this.triggerY = me.getGraphY();
this.screenX = mxEvent.getMainEvent(me.getEvent()).screenX;
this.screenY = mxEvent.getMainEvent(me.getEvent()).screenY;
this.popupTrigger = this.isPopupTrigger(me);
this.inTolerance = true;
}
};
/**
* Function: mouseMove
*
* Handles the event by updating the panning on the graph.
*/
mxPopupMenuHandler.prototype.mouseMove = function(sender, me)
{
// Popup trigger may change on mouseUp so ignore it
if (this.inTolerance && this.screenX != null && this.screenY != null)
{
if (Math.abs(mxEvent.getMainEvent(me.getEvent()).screenX - this.screenX) > this.graph.tolerance ||
Math.abs(mxEvent.getMainEvent(me.getEvent()).screenY - this.screenY) > this.graph.tolerance)
{
this.inTolerance = false;
}
}
};
/**
* Function: mouseUp
*
* Handles the event by setting the translation on the view or showing the
* popupmenu.
*/
mxPopupMenuHandler.prototype.mouseUp = function(sender, me)
{
if (this.popupTrigger && this.inTolerance && this.triggerX != null && this.triggerY != null)
{
var cell = this.getCellForPopupEvent(me);
// Selects the cell for which the context menu is being displayed
if (this.graph.isEnabled() && this.isSelectOnPopup(me) &&
cell != null && !this.graph.isCellSelected(cell))
{
this.graph.setSelectionCell(cell);
}
else if (this.clearSelectionOnBackground && cell == null)
{
this.graph.clearSelection();
}
// Hides the tooltip if there is one
this.graph.tooltipHandler.hide();
// Menu is shifted by 1 pixel so that the mouse up event
// is routed via the underlying shape instead of the DIV
var origin = mxUtils.getScrollOrigin();
this.popup(me.getX() + origin.x + 1, me.getY() + origin.y + 1, cell, me.getEvent());
me.consume();
}
this.popupTrigger = false;
this.inTolerance = false;
};
/**
* Function: getCellForPopupEvent
*
* Hook to return the cell for the mouse up popup trigger handling.
*/
mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me)
{
return me.getCell();
};
/**
* Function: destroy
*
* Destroys the handler and all its resources and DOM nodes.
*/
mxPopupMenuHandler.prototype.destroy = function()
{
this.graph.removeMouseListener(this);
this.graph.removeListener(this.gestureHandler);
// Supercall
mxPopupMenu.prototype.destroy.apply(this);
};

View File

@@ -0,0 +1,429 @@
/**
* Copyright (c) 2006-2016, JGraph Ltd
* Copyright (c) 2006-2016, Gaudenz Alder
*/
/**
* Class: mxRubberband
*
* Event handler that selects rectangular regions. This is not built-into
* <mxGraph>. To enable rubberband selection in a graph, use the following code.
*
* Example:
*
* (code)
* var rubberband = new mxRubberband(graph);
* (end)
*
* Constructor: mxRubberband
*
* Constructs an event handler that selects rectangular regions in the graph
* using rubberband selection.
*/
function mxRubberband(graph)
{
if (graph != null)
{
this.graph = graph;
this.graph.addMouseListener(this);
// Handles force rubberband event
this.forceRubberbandHandler = mxUtils.bind(this, function(sender, evt)
{
var evtName = evt.getProperty('eventName');
var me = evt.getProperty('event');
if (evtName == mxEvent.MOUSE_DOWN && this.isForceRubberbandEvent(me))
{
var offset = mxUtils.getOffset(this.graph.container);
var origin = mxUtils.getScrollOrigin(this.graph.container);
origin.x -= offset.x;
origin.y -= offset.y;
this.start(me.getX() + origin.x, me.getY() + origin.y);
me.consume(false);
}
});
this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.forceRubberbandHandler);
// Repaints the marquee after autoscroll
this.panHandler = mxUtils.bind(this, function()
{
this.repaint();
});
this.graph.addListener(mxEvent.PAN, this.panHandler);
// Does not show menu if any touch gestures take place after the trigger
this.gestureHandler = mxUtils.bind(this, function(sender, eo)
{
if (this.first != null)
{
this.reset();
}
});
this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
// Automatic deallocation of memory
if (mxClient.IS_IE)
{
mxEvent.addListener(window, 'unload',
mxUtils.bind(this, function()
{
this.destroy();
})
);
}
}
};
/**
* Variable: defaultOpacity
*
* Specifies the default opacity to be used for the rubberband div. Default
* is 20.
*/
mxRubberband.prototype.defaultOpacity = 20;
/**
* Variable: enabled
*
* Specifies if events are handled. Default is true.
*/
mxRubberband.prototype.enabled = true;
/**
* Variable: div
*
* Holds the DIV element which is currently visible.
*/
mxRubberband.prototype.div = null;
/**
* Variable: sharedDiv
*
* Holds the DIV element which is used to display the rubberband.
*/
mxRubberband.prototype.sharedDiv = null;
/**
* Variable: currentX
*
* Holds the value of the x argument in the last call to <update>.
*/
mxRubberband.prototype.currentX = 0;
/**
* Variable: currentY
*
* Holds the value of the y argument in the last call to <update>.
*/
mxRubberband.prototype.currentY = 0;
/**
* Variable: fadeOut
*
* Optional fade out effect. Default is false.
*/
mxRubberband.prototype.fadeOut = false;
/**
* Function: isEnabled
*
* Returns true if events are handled. This implementation returns
* <enabled>.
*/
mxRubberband.prototype.isEnabled = function()
{
return this.enabled;
};
/**
* Function: setEnabled
*
* Enables or disables event handling. This implementation updates
* <enabled>.
*/
mxRubberband.prototype.setEnabled = function(enabled)
{
this.enabled = enabled;
};
/**
* Function: isForceRubberbandEvent
*
* Returns true if the given <mxMouseEvent> should start rubberband selection.
* This implementation returns true if the alt key is pressed.
*/
mxRubberband.prototype.isForceRubberbandEvent = function(me)
{
return mxEvent.isAltDown(me.getEvent());
};
/**
* Function: mouseDown
*
* Handles the event by initiating a rubberband selection. By consuming the
* event all subsequent events of the gesture are redirected to this
* handler.
*/
mxRubberband.prototype.mouseDown = function(sender, me)
{
if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled() &&
me.getState() == null && !mxEvent.isMultiTouchEvent(me.getEvent()))
{
var offset = mxUtils.getOffset(this.graph.container);
var origin = mxUtils.getScrollOrigin(this.graph.container);
origin.x -= offset.x;
origin.y -= offset.y;
this.start(me.getX() + origin.x, me.getY() + origin.y);
// Does not prevent the default for this event so that the
// event processing chain is still executed even if we start
// rubberbanding. This is required eg. in ExtJs to hide the
// current context menu. In mouseMove we'll make sure we're
// not selecting anything while we're rubberbanding.
me.consume(false);
}
};
/**
* Function: start
*
* Sets the start point for the rubberband selection.
*/
mxRubberband.prototype.start = function(x, y)
{
this.first = new mxPoint(x, y);
var container = this.graph.container;
function createMouseEvent(evt)
{
var me = new mxMouseEvent(evt);
var pt = mxUtils.convertPoint(container, me.getX(), me.getY());
me.graphX = pt.x;
me.graphY = pt.y;
return me;
};
this.dragHandler = mxUtils.bind(this, function(evt)
{
this.mouseMove(this.graph, createMouseEvent(evt));
});
this.dropHandler = mxUtils.bind(this, function(evt)
{
this.mouseUp(this.graph, createMouseEvent(evt));
});
// Workaround for rubberband stopping if the mouse leaves the container in Firefox
if (mxClient.IS_FF)
{
mxEvent.addGestureListeners(document, null, this.dragHandler, this.dropHandler);
}
};
/**
* Function: mouseMove
*
* Handles the event by updating therubberband selection.
*/
mxRubberband.prototype.mouseMove = function(sender, me)
{
if (!me.isConsumed() && this.first != null)
{
var origin = mxUtils.getScrollOrigin(this.graph.container);
var offset = mxUtils.getOffset(this.graph.container);
origin.x -= offset.x;
origin.y -= offset.y;
var x = me.getX() + origin.x;
var y = me.getY() + origin.y;
var dx = this.first.x - x;
var dy = this.first.y - y;
var tol = this.graph.tolerance;
if (this.div != null || Math.abs(dx) > tol || Math.abs(dy) > tol)
{
if (this.div == null)
{
this.div = this.createShape();
}
// Clears selection while rubberbanding. This is required because
// the event is not consumed in mouseDown.
mxUtils.clearSelection();
this.update(x, y);
me.consume();
}
}
};
/**
* Function: createShape
*
* Creates the rubberband selection shape.
*/
mxRubberband.prototype.createShape = function()
{
if (this.sharedDiv == null)
{
this.sharedDiv = document.createElement('div');
this.sharedDiv.className = 'mxRubberband';
mxUtils.setOpacity(this.sharedDiv, this.defaultOpacity);
}
this.graph.container.appendChild(this.sharedDiv);
var result = this.sharedDiv;
if (mxClient.IS_SVG && (!mxClient.IS_IE || document.documentMode >= 10) && this.fadeOut)
{
this.sharedDiv = null;
}
return result;
};
/**
* Function: isActive
*
* Returns true if this handler is active.
*/
mxRubberband.prototype.isActive = function(sender, me)
{
return this.div != null && this.div.style.display != 'none';
};
/**
* Function: mouseUp
*
* Handles the event by selecting the region of the rubberband using
* <mxGraph.selectRegion>.
*/
mxRubberband.prototype.mouseUp = function(sender, me)
{
var active = this.isActive();
this.reset();
if (active)
{
this.execute(me.getEvent());
me.consume();
}
};
/**
* Function: execute
*
* Resets the state of this handler and selects the current region
* for the given event.
*/
mxRubberband.prototype.execute = function(evt)
{
var rect = new mxRectangle(this.x, this.y, this.width, this.height);
this.graph.selectRegion(rect, evt);
};
/**
* Function: reset
*
* Resets the state of the rubberband selection.
*/
mxRubberband.prototype.reset = function()
{
if (this.div != null)
{
if (mxClient.IS_SVG && (!mxClient.IS_IE || document.documentMode >= 10) && this.fadeOut)
{
var temp = this.div;
mxUtils.setPrefixedStyle(temp.style, 'transition', 'all 0.2s linear');
temp.style.pointerEvents = 'none';
temp.style.opacity = 0;
window.setTimeout(function()
{
temp.parentNode.removeChild(temp);
}, 200);
}
else
{
this.div.parentNode.removeChild(this.div);
}
}
mxEvent.removeGestureListeners(document, null, this.dragHandler, this.dropHandler);
this.dragHandler = null;
this.dropHandler = null;
this.currentX = 0;
this.currentY = 0;
this.first = null;
this.div = null;
};
/**
* Function: update
*
* Sets <currentX> and <currentY> and calls <repaint>.
*/
mxRubberband.prototype.update = function(x, y)
{
this.currentX = x;
this.currentY = y;
this.repaint();
};
/**
* Function: repaint
*
* Computes the bounding box and updates the style of the <div>.
*/
mxRubberband.prototype.repaint = function()
{
if (this.div != null)
{
var x = this.currentX - this.graph.panDx;
var y = this.currentY - this.graph.panDy;
this.x = Math.min(this.first.x, x);
this.y = Math.min(this.first.y, y);
this.width = Math.max(this.first.x, x) - this.x;
this.height = Math.max(this.first.y, y) - this.y;
var dx = (mxClient.IS_VML) ? this.graph.panDx : 0;
var dy = (mxClient.IS_VML) ? this.graph.panDy : 0;
this.div.style.left = (this.x + dx) + 'px';
this.div.style.top = (this.y + dy) + 'px';
this.div.style.width = Math.max(1, this.width) + 'px';
this.div.style.height = Math.max(1, this.height) + 'px';
}
};
/**
* Function: destroy
*
* Destroys the handler and all its resources and DOM nodes. This does
* normally not need to be called, it is called automatically when the
* window unloads.
*/
mxRubberband.prototype.destroy = function()
{
if (!this.destroyed)
{
this.destroyed = true;
this.graph.removeMouseListener(this);
this.graph.removeListener(this.forceRubberbandHandler);
this.graph.removeListener(this.panHandler);
this.reset();
if (this.sharedDiv != null)
{
this.sharedDiv = null;
}
}
};

View File

@@ -0,0 +1,297 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxSelectionCellsHandler
*
* An event handler that manages cell handlers and invokes their mouse event
* processing functions.
*
* Group: Events
*
* Event: mxEvent.ADD
*
* Fires if a cell has been added to the selection. The <code>state</code>
* property contains the <mxCellState> that has been added.
*
* Event: mxEvent.REMOVE
*
* Fires if a cell has been remove from the selection. The <code>state</code>
* property contains the <mxCellState> that has been removed.
*
* Parameters:
*
* graph - Reference to the enclosing <mxGraph>.
*/
function mxSelectionCellsHandler(graph)
{
mxEventSource.call(this);
this.graph = graph;
this.handlers = new mxDictionary();
this.graph.addMouseListener(this);
this.refreshHandler = mxUtils.bind(this, function(sender, evt)
{
if (this.isEnabled())
{
this.refresh();
}
});
this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.refreshHandler);
this.graph.getModel().addListener(mxEvent.CHANGE, this.refreshHandler);
this.graph.getView().addListener(mxEvent.SCALE, this.refreshHandler);
this.graph.getView().addListener(mxEvent.TRANSLATE, this.refreshHandler);
this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.refreshHandler);
this.graph.getView().addListener(mxEvent.DOWN, this.refreshHandler);
this.graph.getView().addListener(mxEvent.UP, this.refreshHandler);
};
/**
* Extends mxEventSource.
*/
mxUtils.extend(mxSelectionCellsHandler, mxEventSource);
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
mxSelectionCellsHandler.prototype.graph = null;
/**
* Variable: enabled
*
* Specifies if events are handled. Default is true.
*/
mxSelectionCellsHandler.prototype.enabled = true;
/**
* Variable: refreshHandler
*
* Keeps a reference to an event listener for later removal.
*/
mxSelectionCellsHandler.prototype.refreshHandler = null;
/**
* Variable: maxHandlers
*
* Defines the maximum number of handlers to paint individually. Default is 100.
*/
mxSelectionCellsHandler.prototype.maxHandlers = 100;
/**
* Variable: handlers
*
* <mxDictionary> that maps from cells to handlers.
*/
mxSelectionCellsHandler.prototype.handlers = null;
/**
* Function: isEnabled
*
* Returns <enabled>.
*/
mxSelectionCellsHandler.prototype.isEnabled = function()
{
return this.enabled;
};
/**
* Function: setEnabled
*
* Sets <enabled>.
*/
mxSelectionCellsHandler.prototype.setEnabled = function(value)
{
this.enabled = value;
};
/**
* Function: getHandler
*
* Returns the handler for the given cell.
*/
mxSelectionCellsHandler.prototype.getHandler = function(cell)
{
return this.handlers.get(cell);
};
/**
* Function: reset
*
* Resets all handlers.
*/
mxSelectionCellsHandler.prototype.reset = function()
{
this.handlers.visit(function(key, handler)
{
handler.reset.apply(handler);
});
};
/**
* Function: refresh
*
* Reloads or updates all handlers.
*/
mxSelectionCellsHandler.prototype.refresh = function()
{
// Removes all existing handlers
var oldHandlers = this.handlers;
this.handlers = new mxDictionary();
// Creates handles for all selection cells
var tmp = this.graph.getSelectionCells();
for (var i = 0; i < tmp.length; i++)
{
var state = this.graph.view.getState(tmp[i]);
if (state != null)
{
var handler = oldHandlers.remove(tmp[i]);
if (handler != null)
{
if (handler.state != state)
{
handler.destroy();
handler = null;
}
else if (!this.isHandlerActive(handler))
{
if (handler.refresh != null)
{
handler.refresh();
}
handler.redraw();
}
}
if (handler == null)
{
handler = this.graph.createHandler(state);
this.fireEvent(new mxEventObject(mxEvent.ADD, 'state', state));
}
if (handler != null)
{
this.handlers.put(tmp[i], handler);
}
}
}
// Destroys all unused handlers
oldHandlers.visit(mxUtils.bind(this, function(key, handler)
{
this.fireEvent(new mxEventObject(mxEvent.REMOVE, 'state', handler.state));
handler.destroy();
}));
};
/**
* Function: isHandlerActive
*
* Returns true if the given handler is active and should not be redrawn.
*/
mxSelectionCellsHandler.prototype.isHandlerActive = function(handler)
{
return handler.index != null;
};
/**
* Function: updateHandler
*
* Updates the handler for the given shape if one exists.
*/
mxSelectionCellsHandler.prototype.updateHandler = function(state)
{
var handler = this.handlers.remove(state.cell);
if (handler != null)
{
handler.destroy();
handler = this.graph.createHandler(state);
if (handler != null)
{
this.handlers.put(state.cell, handler);
}
}
};
/**
* Function: mouseDown
*
* Redirects the given event to the handlers.
*/
mxSelectionCellsHandler.prototype.mouseDown = function(sender, me)
{
if (this.graph.isEnabled() && this.isEnabled())
{
var args = [sender, me];
this.handlers.visit(function(key, handler)
{
handler.mouseDown.apply(handler, args);
});
}
};
/**
* Function: mouseMove
*
* Redirects the given event to the handlers.
*/
mxSelectionCellsHandler.prototype.mouseMove = function(sender, me)
{
if (this.graph.isEnabled() && this.isEnabled())
{
var args = [sender, me];
this.handlers.visit(function(key, handler)
{
handler.mouseMove.apply(handler, args);
});
}
};
/**
* Function: mouseUp
*
* Redirects the given event to the handlers.
*/
mxSelectionCellsHandler.prototype.mouseUp = function(sender, me)
{
if (this.graph.isEnabled() && this.isEnabled())
{
var args = [sender, me];
this.handlers.visit(function(key, handler)
{
handler.mouseUp.apply(handler, args);
});
}
};
/**
* Function: destroy
*
* Destroys the handler and all its resources and DOM nodes.
*/
mxSelectionCellsHandler.prototype.destroy = function()
{
this.graph.removeMouseListener(this);
if (this.refreshHandler != null)
{
this.graph.getSelectionModel().removeListener(this.refreshHandler);
this.graph.getModel().removeListener(this.refreshHandler);
this.graph.getView().removeListener(this.refreshHandler);
this.refreshHandler = null;
}
};

View File

@@ -0,0 +1,337 @@
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxTooltipHandler
*
* Graph event handler that displays tooltips. <mxGraph.getTooltip> is used to
* get the tooltip for a cell or handle. This handler is built-into
* <mxGraph.tooltipHandler> and enabled using <mxGraph.setTooltips>.
*
* Example:
*
* (code>
* new mxTooltipHandler(graph);
* (end)
*
* Constructor: mxTooltipHandler
*
* Constructs an event handler that displays tooltips with the specified
* delay (in milliseconds). If no delay is specified then a default delay
* of 500 ms (0.5 sec) is used.
*
* Parameters:
*
* graph - Reference to the enclosing <mxGraph>.
* delay - Optional delay in milliseconds.
*/
function mxTooltipHandler(graph, delay)
{
if (graph != null)
{
this.graph = graph;
this.delay = delay || 500;
this.graph.addMouseListener(this);
}
};
/**
* Variable: zIndex
*
* Specifies the zIndex for the tooltip and its shadow. Default is 10005.
*/
mxTooltipHandler.prototype.zIndex = 10005;
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
mxTooltipHandler.prototype.graph = null;
/**
* Variable: delay
*
* Delay to show the tooltip in milliseconds. Default is 500.
*/
mxTooltipHandler.prototype.delay = null;
/**
* Variable: ignoreTouchEvents
*
* Specifies if touch and pen events should be ignored. Default is true.
*/
mxTooltipHandler.prototype.ignoreTouchEvents = true;
/**
* Variable: hideOnHover
*
* Specifies if the tooltip should be hidden if the mouse is moved over the
* current cell. Default is false.
*/
mxTooltipHandler.prototype.hideOnHover = false;
/**
* Variable: destroyed
*
* True if this handler was destroyed using <destroy>.
*/
mxTooltipHandler.prototype.destroyed = false;
/**
* Variable: enabled
*
* Specifies if events are handled. Default is true.
*/
mxTooltipHandler.prototype.enabled = true;
/**
* Function: isEnabled
*
* Returns true if events are handled. This implementation
* returns <enabled>.
*/
mxTooltipHandler.prototype.isEnabled = function()
{
return this.enabled;
};
/**
* Function: setEnabled
*
* Enables or disables event handling. This implementation
* updates <enabled>.
*/
mxTooltipHandler.prototype.setEnabled = function(enabled)
{
this.enabled = enabled;
};
/**
* Function: isHideOnHover
*
* Returns <hideOnHover>.
*/
mxTooltipHandler.prototype.isHideOnHover = function()
{
return this.hideOnHover;
};
/**
* Function: setHideOnHover
*
* Sets <hideOnHover>.
*/
mxTooltipHandler.prototype.setHideOnHover = function(value)
{
this.hideOnHover = value;
};
/**
* Function: init
*
* Initializes the DOM nodes required for this tooltip handler.
*/
mxTooltipHandler.prototype.init = function()
{
if (document.body != null)
{
this.div = document.createElement('div');
this.div.className = 'mxTooltip';
this.div.style.visibility = 'hidden';
document.body.appendChild(this.div);
mxEvent.addGestureListeners(this.div, mxUtils.bind(this, function(evt)
{
this.hideTooltip();
}));
}
};
/**
* Function: mouseDown
*
* Handles the event by initiating a rubberband selection. By consuming the
* event all subsequent events of the gesture are redirected to this
* handler.
*/
mxTooltipHandler.prototype.mouseDown = function(sender, me)
{
this.reset(me, false);
this.hideTooltip();
};
/**
* Function: mouseMove
*
* Handles the event by updating the rubberband selection.
*/
mxTooltipHandler.prototype.mouseMove = function(sender, me)
{
if (me.getX() != this.lastX || me.getY() != this.lastY)
{
this.reset(me, true);
if (this.isHideOnHover() || me.getState() != this.state || (me.getSource() != this.node &&
(!this.stateSource || (me.getState() != null && this.stateSource ==
(me.isSource(me.getState().shape) || !me.isSource(me.getState().text))))))
{
this.hideTooltip();
}
}
this.lastX = me.getX();
this.lastY = me.getY();
};
/**
* Function: mouseUp
*
* Handles the event by resetting the tooltip timer or hiding the existing
* tooltip.
*/
mxTooltipHandler.prototype.mouseUp = function(sender, me)
{
this.reset(me, true);
this.hideTooltip();
};
/**
* Function: resetTimer
*
* Resets the timer.
*/
mxTooltipHandler.prototype.resetTimer = function()
{
if (this.thread != null)
{
window.clearTimeout(this.thread);
this.thread = null;
}
};
/**
* Function: reset
*
* Resets and/or restarts the timer to trigger the display of the tooltip.
*/
mxTooltipHandler.prototype.reset = function(me, restart)
{
if (!this.ignoreTouchEvents || mxEvent.isMouseEvent(me.getEvent()))
{
this.resetTimer();
if (restart && this.isEnabled() && me.getState() != null && (this.div == null ||
this.div.style.visibility == 'hidden'))
{
var state = me.getState();
var node = me.getSource();
var x = me.getX();
var y = me.getY();
var stateSource = me.isSource(state.shape) || me.isSource(state.text);
this.thread = window.setTimeout(mxUtils.bind(this, function()
{
if (!this.graph.isEditing() && !this.graph.popupMenuHandler.isMenuShowing() && !this.graph.isMouseDown)
{
// Uses information from inside event cause using the event at
// this (delayed) point in time is not possible in IE as it no
// longer contains the required information (member not found)
var tip = this.graph.getTooltip(state, node, x, y);
this.show(tip, x, y);
this.state = state;
this.node = node;
this.stateSource = stateSource;
}
}), this.delay);
}
}
};
/**
* Function: hide
*
* Hides the tooltip and resets the timer.
*/
mxTooltipHandler.prototype.hide = function()
{
this.resetTimer();
this.hideTooltip();
};
/**
* Function: hideTooltip
*
* Hides the tooltip.
*/
mxTooltipHandler.prototype.hideTooltip = function()
{
if (this.div != null)
{
this.div.style.visibility = 'hidden';
this.div.innerHTML = '';
}
};
/**
* Function: show
*
* Shows the tooltip for the specified cell and optional index at the
* specified location (with a vertical offset of 10 pixels).
*/
mxTooltipHandler.prototype.show = function(tip, x, y)
{
if (!this.destroyed && tip != null && tip.length > 0)
{
// Initializes the DOM nodes if required
if (this.div == null)
{
this.init();
}
var origin = mxUtils.getScrollOrigin();
this.div.style.zIndex = this.zIndex;
this.div.style.left = (x + origin.x) + 'px';
this.div.style.top = (y + mxConstants.TOOLTIP_VERTICAL_OFFSET +
origin.y) + 'px';
if (!mxUtils.isNode(tip))
{
this.div.innerHTML = tip.replace(/\n/g, '<br>');
}
else
{
this.div.innerHTML = '';
this.div.appendChild(tip);
}
this.div.style.visibility = '';
mxUtils.fit(this.div);
}
};
/**
* Function: destroy
*
* Destroys the handler and all its resources and DOM nodes.
*/
mxTooltipHandler.prototype.destroy = function()
{
if (!this.destroyed)
{
this.graph.removeMouseListener(this);
mxEvent.release(this.div);
if (this.div != null && this.div.parentNode != null)
{
this.div.parentNode.removeChild(this.div);
}
this.destroyed = true;
this.div = null;
}
};

File diff suppressed because it is too large Load Diff