.
*/
var $model;
/**
* Variable: stylesheet
*
* Holds the .
*/
var $stylesheet;
/**
* Variable: view
*
* Holds the .
*/
var $view;
/**
* Variable: gridSize
*
* Specifies the grid size. Default is 10.
*/
var $gridSize = 10;
/**
* Variable: labelsVisible
*
* Specifies if labels should be visible. This is used in
* . Default is true.
*/
var $labelsVisible = true;
/**
* Variable: defaultLoopStyle
*
* to be used for loops. This is a fallback for
* loops if the is undefined. Default is
* .
*/
var $defaultLoopStyle = "mxEdgeStyle.Loop";
/**
* Variable: imageBundles
*
* Holds the list of image bundles.
*/
protected $imageBundles = array();
/**
* Constructor: mxGraphModel
*
* Constructs a new graph model using the specified
* root cell.
*/
function mxGraph($model = null, $stylesheet = null)
{
$this->model = ($model != null) ? $model : new mxGraphModel();
$this->stylesheet = ($stylesheet != null) ? $stylesheet : $this->createStylesheet();
$this->view = $this->createGraphView();
$this->view->revalidate();
$this->model->addListener(mxEvent::$GRAPH_MODEL_CHANGED, $this);
}
/**
* Function: createStylesheet
*
* Creates a new to be used in this graph.
*/
function createStylesheet()
{
return new mxStylesheet();
}
/**
* Function: createGraphView
*
* Creates a new to be used in this graph.
*/
function createGraphView()
{
return new mxGraphView($this);
}
/**
* Function: getModel
*
* Returns the that contains the cells.
*/
function getModel()
{
return $this->model;
}
/**
* Function: getStylesheet
*
* Returns the that defines the style.
*/
function getStylesheet()
{
return $this->stylesheet;
}
/**
* Function: getView
*
* Returns the that contains the .
*/
function getView()
{
return $this->view;
}
/**
* Function: getDefaultParent
*
* Returns the first child child of . The value returned
* by this function should be used as the parent for new cells (aka default
* layer).
*/
function getDefaultParent()
{
$model = $this->model;
return $model->getChildAt($model->getRoot(), 0);
}
/**
* Function: convertValueToString
*
* Returns the textual representation for the given cell. This
* implementation returns the nodename or string-representation of the user
* object.
*/
function convertValueToString($cell)
{
$result = $this->model->getValue($cell);
return ($result != null) ? $result : "";
}
/**
* Function: getLabel
*
* Returns a string or DOM node that represents the label for the given
* cell. This implementation uses if
* is true. Otherwise it returns an empty string.
*/
function getLabel($cell)
{
$result = "";
if ($cell != null)
{
$state = $this->view->getState($cell);
$style = ($state != null) ?
$state->style : $this->getCellStyle($cell);
if ($this->labelsVisible &&
!mxUtils::getValue($style, mxConstants::$STYLE_NOLABEL, false))
{
$result = $this->convertValueToString($cell);
}
}
return $result;
}
/**
* Function: getChildOffsetForCell
*
* Returns the offset to be used for the cells inside the given cell. The
* root and layer cells may be identified using and
* . For all other current roots, the
* field points to the respective cell, so that
* the following holds: cell == this.view.currentRoot. This implementation
* returns null.
*
* Parameters:
*
* cell - whose offset should be returned.
*/
function getChildOffsetForCell($cell)
{
return null;
}
/**
* Function: isOrthogonal
*
* Returns true if perimeter points should be computed such that the
* resulting edge has only horizontal or vertical segments.
*
* Parameters:
*
* edge - that represents the edge.
*/
function isOrthogonal($edge)
{
if (isset($edge->style[mxConstants::$STYLE_ORTHOGONAL]))
{
return mxUtils::getValue($edge->style, mxConstants::$STYLE_ORTHOGONAL);
}
$edgeStyle = $this->view->getEdgeStyle($edge, null, null, null);
return $edgeStyle === mxEdgeStyle::$ElbowConnector ||
$edgeStyle === mxEdgeStyle::$SideToSide ||
$edgeStyle === mxEdgeStyle::$TopToBottom ||
$edgeStyle === mxEdgeStyle::$EntityRelation;
}
/**
* Function: isCellVisible
*
* Returns true if the given cell is visible.
*/
function isCellVisible($cell)
{
return $this->model->isVisible($cell);
}
/**
* Function: isCellCollapsed
*
* Returns true if the given cell is collapsed.
*/
function isCellCollapsed($cell)
{
return $this->model->isCollapsed($cell);
}
/**
* Function: isCellCollapsed
*
* Returns true if the given cell is connectable.
*/
function isCellConnectable($cell)
{
return $this->model->isConnectable($cell);
}
/**
* Function: getCellGeometry
*
* Returns the for the given .
*/
function getCellGeometry($cell)
{
return $this->model->getGeometry($cell);
}
/**
* Function: getCellStyle
*/
function getCellStyle($cell)
{
$style = ($this->model->isVertex($cell)) ?
$this->stylesheet->getDefaultVertexStyle() :
$this->stylesheet->getDefaultEdgeStyle();
$name = $this->model->getStyle($cell);
if ($name != null)
{
$style = $this->postProcessCellStyle($this->stylesheet->getCellStyle($name, $style));
}
if ($style == null)
{
$style = array();
}
return $style;
}
/**
* Function: postProcessCellStyle
*
* Tries to resolve the value for the image style in the image bundles and
* turns short data URIs as defined in mxImageBundle to data URIs as
* defined in RFC 2397 of the IETF.
*/
function postProcessCellStyle($style)
{
if (isset($style) && array_key_exists(mxConstants::$STYLE_IMAGE, $style))
{
$key = $style[mxConstants::$STYLE_IMAGE];
$image = $this->getImageFromBundles($key);
if (isset($image))
{
$style[mxConstants::$STYLE_IMAGE] = $image;
}
else
{
$image = $key;
}
// Converts short data uris to normal data uris
if (isset($image) && substr($image, 0, 11) == "data:image/")
{
$comma = strpos($image, ",");
if ($comma !== false)
{
$image = substr($image, 0, $comma).";base64,".
substr($image, $comma + 1);
}
$style[mxConstants::$STYLE_IMAGE] = $image;
}
}
return $style;
}
/**
* Function: setCellStyles
*
* Sets the key to value in the styles of the given cells. This will modify
* the existing cell styles in-place and override any existing assignment
* for the given key. If no cells are specified, then the selection cells
* are changed. If no value is specified, then the respective key is
* removed from the styles.
*
* Parameters:
*
* key - String representing the key to be assigned.
* value - String representing the new value for the key.
* cells - Array of to change the style for.
*/
function setCellStyles($key, $value, $cells)
{
mxUtils::setCellStyles($this->model, $cells, $key, $value);
}
/**
* Function: addBundle
*
* Adds the specified .
*/
function addImageBundle($bundle)
{
array_push($this->imageBundles, $bundle);
}
/**
* Function: removeImageBundle
*
* Removes the specified .
*/
function removeImageBundle($bundle)
{
$tmp = array();
for ($i = 0; $i < sizeof($this->imageBundles); $i++)
{
if ($this->imageBundles[$i] !== $bundle)
{
array_push($tmp, $this->imageBundles[$i]);
}
}
$this->imageBundles = $tmp;
}
/**
* Function: getImageFromBundles
*
* Searches all for the specified key and returns the value
* for the first match or null if the key is not found.
*/
function getImageFromBundles($key)
{
if (isset($key))
{
for ($i = 0; $i < sizeof($this->imageBundles); $i++)
{
$image = $this->imageBundles[$i]->getImage($key);
if (isset($image))
{
return $image;
}
}
}
return null;
}
/**
* Function: getImageBundles
*
* Returns the .
*/
function getImageBundles()
{
return $this->imageBundles;
}
/**
* Function: setImageBundles
*
* Sets the .
*/
function setImageBundles($value)
{
$this->imageBundles = $value;
}
/**
* Function: insertVertex
*
* Adds a new vertex into the given parent using value as the user
* object and the given coordinates as the of the new vertex.
* The id and style are used for the respective properties of the new
* , which is returned.
*
* Parameters:
*
* parent - that specifies the parent of the new vertex.
* id - Optional string that defines the Id of the new vertex.
* value - Object to be used as the user object.
* x - Integer that defines the x coordinate of the vertex.
* y - Integer that defines the y coordinate of the vertex.
* width - Integer that defines the width of the vertex.
* height - Integer that defines the height of the vertex.
* style - Optional string that defines the cell style.
* relative - Optional boolean that specifies if the geometry is relative.
* Default is false.
*/
function insertVertex($parent, $id = null, $value = null, $x = 0, $y = 0,
$width = 1, $height = 1, $style = null, $relative = false)
{
if ($parent == null)
{
$parent = $this->getDefaultParent();
}
$vertex = $this->createVertex($parent, $id, $value, $x, $y, $width, $height, $style, $relative);
$index = $this->model->getChildCount($parent);
return $this->model->add($parent, $vertex, $index);
}
/**
* Function: createVertex
*
* Creates the vertex to be used in insertVertex.
*/
function createVertex($parent, $id = null, $value = null, $x = 0, $y = 0,
$width = 1, $height = 1, $style = null, $relative = false)
{
$geometry = new mxGeometry($x, $y, $width, $height);
$geometry->relative = $relative;
$vertex = new mxCell($value, $geometry, $style);
$vertex->setId($id);
$vertex->setVertex(true);
return $vertex;
}
/**
* Function: insertEdge
*
* Adds a new edge into the given parent using value as the user
* object and the given source and target as the terminals of the new edge.
* The id and style are used for the respective properties of the new
* , which is returned.
*
* Parameters:
*
* parent - that specifies the parent of the new edge.
* id - Optional string that defines the Id of the new edge.
* value - JavaScript object to be used as the user object.
* source - that defines the source of the edge.
* target - that defines the target of the edge.
* style - Optional string that defines the cell style.
*/
function insertEdge($parent, $id = null, $value = null,
$source = null, $target = null, $style = null)
{
if ($parent == null)
{
$parent = $this->getDefaultParent();
}
$edge = $this->createEdge($parent, $id, $value, $source, $target, $style);
// Appends the edge to the given parent and sets
// the edge terminals in a single transaction
$index = $this->model->getChildCount($parent);
$this->model->beginUpdate();
try
{
$edge = $this->model->add($parent, $edge, $index);
$this->model->setTerminal($edge, $source, true);
$this->model->setTerminal($edge, $target, false);
}
catch (Exception $e)
{
$this->model->endUpdate();
throw($e);
}
$this->model->endUpdate();
return $edge;
}
/**
* Function: createEdge
*
* Creates the edge to be used in . This implementation does
* not set the source and target of the edge, these are set when the edge
* is added to the model.
*/
function createEdge($parent, $id = null, $value = null,
$source = null, $target = null, $style = null)
{
$geometry = new mxGeometry();
$edge = new mxCell($value, $geometry, $style);
$edge->setId($id);
$edge->setEdge(true);
$edge->geometry->relative = true;
return $edge;
}
/**
* Function: getGraphBounds
*
* Returns the bounds of the visible graph. Shortcut to
* .
*/
function getGraphBounds()
{
return $this->getView()->getGraphBounds();
}
/**
* Function: getBoundingBox
*
* Returns the bounding box of the given cell including all connected edges
* if includeEdge is true.
*/
function getBoundingBox($cell, $includeEdges = false, $includeDescendants = false)
{
return $this->getCellBounds($cell, $includeEdges, $includeDescendants, true);
}
/**
* Function: getPaintBounds
*
* Returns the bounding box of the given cells and their descendants.
*/
function getPaintBounds($cells)
{
return $this->getBoundsForCells($cells, false, true, true);
}
/**
* Function: getBoundsForCells
*
* Returns the bounds for the given cells.
*/
function getBoundsForCells($cells, $includeEdges = false, $includeDescendants = false, $boundingBox = false)
{
$cellCount = sizeof($cells);
$result = null;
if ($cellCount > 0)
{
for ($i = 0; $i < $cellCount; $i++)
{
$bounds = $this->getCellBounds($cells[$i], $includeEdges,
$includeDescendants, $boundingBox);
if ($bounds != null)
{
if ($result == null)
{
$result = new mxRectangle($bounds->x, $bounds->y,
$bounds->width, $bounds->height);
}
else
{
$result->add($bounds);
}
}
}
}
return $result;
}
/**
* Function: getCellBounds
*
* Returns the bounds of the given cell including all connected edges
* if includeEdge is true.
*/
function getCellBounds($cell, $includeEdges = false, $includeDescendants = false, $boundingBox = false)
{
$cells = array($cell);
// Includes the connected edges
if ($includeEdges)
{
$edgeCount = $this->model->getEdgeCount($cell);
for ($i = 0; $i < $edgeCount; $i++)
{
array_push($cells, $this->model->getEdgeAt($cell, $i));
}
}
$result = $this->view->getBounds($cells, $boundingBox);
// Recursively includes the bounds of the children
if ($includeDescendants)
{
for ($i = 0; $i < sizeof($cells); $i++)
{
$childCount = $this->model->getChildCount($cells[$i]);
for ($j = 0; $j < $childCount; $j++)
{
$tmp = $this->getCellBounds($this->model->getChildAt($cells[$i], $j),
$includeEdges, true, $boundingBox);
if ($result != null)
{
$result->add($tmp);
}
else
{
$result = $tmp;
}
}
}
}
return $result;
}
/**
* Function: getConnectionConstraint
*
* Returns an that describes the given connection
* point. This result can then be passed to .
*
* Parameters:
*
* edge - that represents the edge.
* terminal - that represents the terminal.
* source - Boolean indicating if the terminal is the source or target.
*/
function getConnectionConstraint($edge, $terminal, $source)
{
$point = null;
$x = mxUtils::getValue($edge->style,
($source) ? mxConstants::$STYLE_EXIT_X :
mxConstants::$STYLE_ENTRY_X);
if (isset($x))
{
$y = mxUtils::getValue($edge->style,
(($source) ? mxConstants::$STYLE_EXIT_Y :
mxConstants::$STYLE_ENTRY_Y));
if (isset($y))
{
$point = new mxPoint($x, $y);
}
}
$perimeter = false;
if (isset($point))
{
$perimeter = mxUtils::getValue($edge->style, ($source) ?
mxConstants::$STYLE_EXIT_PERIMETER :
mxConstants::$STYLE_ENTRY_PERIMETER, true);
}
return new mxConnectionConstraint($point, $perimeter);
}
/**
* Function: getConnectionPoint
*
* Returns the nearest point in the list of absolute points or the center
* of the opposite terminal.
*
* Parameters:
*
* vertex - that represents the vertex.
* constraint - that represents the connection point
* constraint as returned by .
*/
function getConnectionPoint($vertex, $constraint)
{
$point = null;
if (isset($vertex) && isset($constraint->point))
{
$point = new mxPoint($vertex->x + $constraint->point->x * $vertex->width,
$vertex->y + $constraint->point->y * $vertex->height);
}
if (isset($point) && $constraint->perimeter)
{
$point = $this->view->getPerimeterPoint($vertex, $point, false);
}
return $point;
}
/**
* Function: findTreeRoots
*
* Returns all children in the given parent which do not have incoming
* edges. If the result is empty then the with the greatest difference
* between incoming and outgoing edges is returned.
*
* Parameters:
*
* parent - whose children should be checked.
* isolate - Optional boolean that specifies if edges should be ignored if
* the opposite end is not a child of the given parent cell. Default is
* false.
* invert - Optional boolean that specifies if outgoing or incoming edges
* should be counted for a tree root. If false then outgoing edges will be
* counted. Default is false.
*/
function findTreeRoots($parent, $isolate = false, $invert = false)
{
$roots = array();
if ($parent != null)
{
$model = $this->getModel();
$childCount = $model->getChildCount($parent);
$maxDiff = 0;
for ($i=0; $i<$childCount; $i++)
{
$cell = $model->getChildAt($parent, $i);
if ($this->model->isVertex($cell) &&
$this->isCellVisible($cell))
{
$edgeCount = $model->getEdgeCount($cell);
$fanOut = 0;
$fanIn = 0;
for ($j = 0; $j < $edgeCount; $j++)
{
$edge = $model->getEdgeAt($cell, $j);
if ($this->isCellVisible($edge))
{
$source = $this->view->getVisibleTerminal($edge, true);
$target = $this->view->getVisibleTerminal($edge, false);
if ($source !== $target)
{
if ($source === $cell && (!$isolate ||
$this->model->getParent(target) == $parent))
{
$fanOut++;
}
else if (!$isolate ||
$this->model->getParent(source) == $parent)
{
$fanIn++;
}
}
}
}
if (($invert && $fanOut == 0 && $fanIn > 0) ||
(!$invert && $fanIn == 0 && $fanOut > 0))
{
array_push($roots, $cell);
}
$diff = ($invert) ? $fanIn - $fanOut : $fanOut - $fanIn;
if ($diff > $maxDiff)
{
$maxDiff = $diff;
$best = $cell;
}
}
}
if (sizeof($roots) == 0 && $best != null)
{
array_push($roots, $best);
}
}
return $roots;
}
/**
* Function: createImage
*/
function createImage($clip = null, $background = null)
{
return mxGdCanvas::drawGraph($this, $clip, $background);
}
/**
* Function: drawGraph
*
* Draws the given cell onto the specified canvas.
*/
function drawGraph($canvas)
{
$this->drawCell($canvas, $this->model->getRoot());
}
/**
* Function: paintCell
*
* Draws the given cell onto the specified canvas.
*/
function drawCell($canvas, $cell)
{
$this->drawState($canvas, $this->view->getState($cell),
$this->getLabel($cell));
// Draws the children on top
$childCount = $cell->getChildCount();
for ($i = 0; $i < $childCount; $i++)
{
$child = $cell->getChildAt($i);
$this->drawCell($canvas, $child);
}
}
/**
* Function: paintState
*
* Draws the given cell and label onto the specified canvas. No
* children or descendants are painted.
*/
function drawState($canvas, $state, $label)
{
$cell = (isset($state)) ? $state->cell : null;
if ($cell != null && $cell !== $this->model->getRoot() &&
($this->model->isVertex($cell) || $this->model->isEdge($cell)))
{
$canvas->drawCell($state);
if ($label != null && $state->labelBounds != null)
{
$canvas->drawLabel($label, $state, false);
}
}
}
/**
* Function: graphModelChanged
*
* Called when the graph model has changed.
*/
function graphModelChanged($event)
{
$this->view->revalidate();
}
}
?>