summaryrefslogtreecommitdiff
path: root/src/js/model
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/model')
-rw-r--r--src/js/model/mxCell.js806
-rw-r--r--src/js/model/mxCellPath.js163
-rw-r--r--src/js/model/mxGeometry.js277
-rw-r--r--src/js/model/mxGraphModel.js2622
4 files changed, 3868 insertions, 0 deletions
diff --git a/src/js/model/mxCell.js b/src/js/model/mxCell.js
new file mode 100644
index 0000000..cb5eb9f
--- /dev/null
+++ b/src/js/model/mxCell.js
@@ -0,0 +1,806 @@
+/**
+ * $Id: mxCell.js,v 1.36 2011-06-17 13:45:08 gaudenz Exp $
+ * Copyright (c) 2006-2010, JGraph Ltd
+ */
+/**
+ * Class: mxCell
+ *
+ * Cells are the elements of the graph model. They represent the state
+ * of the groups, vertices and edges in a graph.
+ *
+ * Custom attributes:
+ *
+ * For custom attributes we recommend using an XML node as the value of a cell.
+ * The following code can be used to create a cell with an XML node as the
+ * value:
+ *
+ * (code)
+ * var doc = mxUtils.createXmlDocument();
+ * var node = doc.createElement('MyNode')
+ * node.setAttribute('label', 'MyLabel');
+ * node.setAttribute('attribute1', 'value1');
+ * graph.insertVertex(graph.getDefaultParent(), null, node, 40, 40, 80, 30);
+ * (end)
+ *
+ * For the label to work, <mxGraph.convertValueToString> and
+ * <mxGraph.cellLabelChanged> should be overridden as follows:
+ *
+ * (code)
+ * graph.convertValueToString = function(cell)
+ * {
+ * if (mxUtils.isNode(cell.value))
+ * {
+ * return cell.getAttribute('label', '')
+ * }
+ * };
+ *
+ * var cellLabelChanged = graph.cellLabelChanged;
+ * graph.cellLabelChanged = function(cell, newValue, autoSize)
+ * {
+ * if (mxUtils.isNode(cell.value))
+ * {
+ * // Clones the value for correct undo/redo
+ * var elt = cell.value.cloneNode(true);
+ * elt.setAttribute('label', newValue);
+ * newValue = elt;
+ * }
+ *
+ * cellLabelChanged.apply(this, arguments);
+ * };
+ * (end)
+ *
+ * Callback: onInit
+ *
+ * Called from within the constructor.
+ *
+ * Constructor: mxCell
+ *
+ * Constructs a new cell to be used in a graph model.
+ * This method invokes <onInit> upon completion.
+ *
+ * Parameters:
+ *
+ * value - Optional object that represents the cell value.
+ * geometry - Optional <mxGeometry> that specifies the geometry.
+ * style - Optional formatted string that defines the style.
+ */
+function mxCell(value, geometry, style)
+{
+ this.value = value;
+ this.setGeometry(geometry);
+ this.setStyle(style);
+
+ if (this.onInit != null)
+ {
+ this.onInit();
+ }
+};
+
+/**
+ * Variable: id
+ *
+ * Holds the Id. Default is null.
+ */
+mxCell.prototype.id = null;
+
+/**
+ * Variable: value
+ *
+ * Holds the user object. Default is null.
+ */
+mxCell.prototype.value = null;
+
+/**
+ * Variable: geometry
+ *
+ * Holds the <mxGeometry>. Default is null.
+ */
+mxCell.prototype.geometry = null;
+
+/**
+ * Variable: style
+ *
+ * Holds the style as a string of the form [(stylename|key=value);]. Default is
+ * null.
+ */
+mxCell.prototype.style = null;
+
+/**
+ * Variable: vertex
+ *
+ * Specifies whether the cell is a vertex. Default is false.
+ */
+mxCell.prototype.vertex = false;
+
+/**
+ * Variable: edge
+ *
+ * Specifies whether the cell is an edge. Default is false.
+ */
+mxCell.prototype.edge = false;
+
+/**
+ * Variable: connectable
+ *
+ * Specifies whether the cell is connectable. Default is true.
+ */
+mxCell.prototype.connectable = true;
+
+/**
+ * Variable: visible
+ *
+ * Specifies whether the cell is visible. Default is true.
+ */
+mxCell.prototype.visible = true;
+
+/**
+ * Variable: collapsed
+ *
+ * Specifies whether the cell is collapsed. Default is false.
+ */
+mxCell.prototype.collapsed = false;
+
+/**
+ * Variable: parent
+ *
+ * Reference to the parent cell.
+ */
+mxCell.prototype.parent = null;
+
+/**
+ * Variable: source
+ *
+ * Reference to the source terminal.
+ */
+mxCell.prototype.source = null;
+
+/**
+ * Variable: target
+ *
+ * Reference to the target terminal.
+ */
+mxCell.prototype.target = null;
+
+/**
+ * Variable: children
+ *
+ * Holds the child cells.
+ */
+mxCell.prototype.children = null;
+
+/**
+ * Variable: edges
+ *
+ * Holds the edges.
+ */
+mxCell.prototype.edges = null;
+
+/**
+ * Variable: mxTransient
+ *
+ * List of members that should not be cloned inside <clone>. This field is
+ * passed to <mxUtils.clone> and is not made persistent in <mxCellCodec>.
+ * This is not a convention for all classes, it is only used in this class
+ * to mark transient fields since transient modifiers are not supported by
+ * the language.
+ */
+mxCell.prototype.mxTransient = ['id', 'value', 'parent', 'source',
+ 'target', 'children', 'edges'];
+
+/**
+ * Function: getId
+ *
+ * Returns the Id of the cell as a string.
+ */
+mxCell.prototype.getId = function()
+{
+ return this.id;
+};
+
+/**
+ * Function: setId
+ *
+ * Sets the Id of the cell to the given string.
+ */
+mxCell.prototype.setId = function(id)
+{
+ this.id = id;
+};
+
+/**
+ * Function: getValue
+ *
+ * Returns the user object of the cell. The user
+ * object is stored in <value>.
+ */
+mxCell.prototype.getValue = function()
+{
+ return this.value;
+};
+
+/**
+ * Function: setValue
+ *
+ * Sets the user object of the cell. The user object
+ * is stored in <value>.
+ */
+mxCell.prototype.setValue = function(value)
+{
+ this.value = value;
+};
+
+/**
+ * Function: valueChanged
+ *
+ * Changes the user object after an in-place edit
+ * and returns the previous value. This implementation
+ * replaces the user object with the given value and
+ * returns the old user object.
+ */
+mxCell.prototype.valueChanged = function(newValue)
+{
+ var previous = this.getValue();
+ this.setValue(newValue);
+
+ return previous;
+};
+
+/**
+ * Function: getGeometry
+ *
+ * Returns the <mxGeometry> that describes the <geometry>.
+ */
+mxCell.prototype.getGeometry = function()
+{
+ return this.geometry;
+};
+
+/**
+ * Function: setGeometry
+ *
+ * Sets the <mxGeometry> to be used as the <geometry>.
+ */
+mxCell.prototype.setGeometry = function(geometry)
+{
+ this.geometry = geometry;
+};
+
+/**
+ * Function: getStyle
+ *
+ * Returns a string that describes the <style>.
+ */
+mxCell.prototype.getStyle = function()
+{
+ return this.style;
+};
+
+/**
+ * Function: setStyle
+ *
+ * Sets the string to be used as the <style>.
+ */
+mxCell.prototype.setStyle = function(style)
+{
+ this.style = style;
+};
+
+/**
+ * Function: isVertex
+ *
+ * Returns true if the cell is a vertex.
+ */
+mxCell.prototype.isVertex = function()
+{
+ return this.vertex;
+};
+
+/**
+ * Function: setVertex
+ *
+ * Specifies if the cell is a vertex. This should only be assigned at
+ * construction of the cell and not be changed during its lifecycle.
+ *
+ * Parameters:
+ *
+ * vertex - Boolean that specifies if the cell is a vertex.
+ */
+mxCell.prototype.setVertex = function(vertex)
+{
+ this.vertex = vertex;
+};
+
+/**
+ * Function: isEdge
+ *
+ * Returns true if the cell is an edge.
+ */
+mxCell.prototype.isEdge = function()
+{
+ return this.edge;
+};
+
+/**
+ * Function: setEdge
+ *
+ * Specifies if the cell is an edge. This should only be assigned at
+ * construction of the cell and not be changed during its lifecycle.
+ *
+ * Parameters:
+ *
+ * edge - Boolean that specifies if the cell is an edge.
+ */
+mxCell.prototype.setEdge = function(edge)
+{
+ this.edge = edge;
+};
+
+/**
+ * Function: isConnectable
+ *
+ * Returns true if the cell is connectable.
+ */
+mxCell.prototype.isConnectable = function()
+{
+ return this.connectable;
+};
+
+/**
+ * Function: setConnectable
+ *
+ * Sets the connectable state.
+ *
+ * Parameters:
+ *
+ * connectable - Boolean that specifies the new connectable state.
+ */
+mxCell.prototype.setConnectable = function(connectable)
+{
+ this.connectable = connectable;
+};
+
+/**
+ * Function: isVisible
+ *
+ * Returns true if the cell is visibile.
+ */
+mxCell.prototype.isVisible = function()
+{
+ return this.visible;
+};
+
+/**
+ * Function: setVisible
+ *
+ * Specifies if the cell is visible.
+ *
+ * Parameters:
+ *
+ * visible - Boolean that specifies the new visible state.
+ */
+mxCell.prototype.setVisible = function(visible)
+{
+ this.visible = visible;
+};
+
+/**
+ * Function: isCollapsed
+ *
+ * Returns true if the cell is collapsed.
+ */
+mxCell.prototype.isCollapsed = function()
+{
+ return this.collapsed;
+};
+
+/**
+ * Function: setCollapsed
+ *
+ * Sets the collapsed state.
+ *
+ * Parameters:
+ *
+ * collapsed - Boolean that specifies the new collapsed state.
+ */
+mxCell.prototype.setCollapsed = function(collapsed)
+{
+ this.collapsed = collapsed;
+};
+
+/**
+ * Function: getParent
+ *
+ * Returns the cell's parent.
+ */
+mxCell.prototype.getParent = function()
+{
+ return this.parent;
+};
+
+/**
+ * Function: setParent
+ *
+ * Sets the parent cell.
+ *
+ * Parameters:
+ *
+ * parent - <mxCell> that represents the new parent.
+ */
+mxCell.prototype.setParent = function(parent)
+{
+ this.parent = parent;
+};
+
+/**
+ * Function: getTerminal
+ *
+ * Returns the source or target terminal.
+ *
+ * Parameters:
+ *
+ * source - Boolean that specifies if the source terminal should be
+ * returned.
+ */
+mxCell.prototype.getTerminal = function(source)
+{
+ return (source) ? this.source : this.target;
+};
+
+/**
+ * Function: setTerminal
+ *
+ * Sets the source or target terminal and returns the new terminal.
+ *
+ * Parameters:
+ *
+ * terminal - <mxCell> that represents the new source or target terminal.
+ * isSource - Boolean that specifies if the source or target terminal
+ * should be set.
+ */
+mxCell.prototype.setTerminal = function(terminal, isSource)
+{
+ if (isSource)
+ {
+ this.source = terminal;
+ }
+ else
+ {
+ this.target = terminal;
+ }
+
+ return terminal;
+};
+
+/**
+ * Function: getChildCount
+ *
+ * Returns the number of child cells.
+ */
+mxCell.prototype.getChildCount = function()
+{
+ return (this.children == null) ? 0 : this.children.length;
+};
+
+/**
+ * Function: getIndex
+ *
+ * Returns the index of the specified child in the child array.
+ *
+ * Parameters:
+ *
+ * child - Child whose index should be returned.
+ */
+mxCell.prototype.getIndex = function(child)
+{
+ return mxUtils.indexOf(this.children, child);
+};
+
+/**
+ * Function: getChildAt
+ *
+ * Returns the child at the specified index.
+ *
+ * Parameters:
+ *
+ * index - Integer that specifies the child to be returned.
+ */
+mxCell.prototype.getChildAt = function(index)
+{
+ return (this.children == null) ? null : this.children[index];
+};
+
+/**
+ * Function: insert
+ *
+ * Inserts the specified child into the child array at the specified index
+ * and updates the parent reference of the child. If not childIndex is
+ * specified then the child is appended to the child array. Returns the
+ * inserted child.
+ *
+ * Parameters:
+ *
+ * child - <mxCell> to be inserted or appended to the child array.
+ * index - Optional integer that specifies the index at which the child
+ * should be inserted into the child array.
+ */
+mxCell.prototype.insert = function(child, index)
+{
+ if (child != null)
+ {
+ if (index == null)
+ {
+ index = this.getChildCount();
+
+ if (child.getParent() == this)
+ {
+ index--;
+ }
+ }
+
+ child.removeFromParent();
+ child.setParent(this);
+
+ if (this.children == null)
+ {
+ this.children = [];
+ this.children.push(child);
+ }
+ else
+ {
+ this.children.splice(index, 0, child);
+ }
+ }
+
+ return child;
+};
+
+/**
+ * Function: remove
+ *
+ * Removes the child at the specified index from the child array and
+ * returns the child that was removed. Will remove the parent reference of
+ * the child.
+ *
+ * Parameters:
+ *
+ * index - Integer that specifies the index of the child to be
+ * removed.
+ */
+mxCell.prototype.remove = function(index)
+{
+ var child = null;
+
+ if (this.children != null && index >= 0)
+ {
+ child = this.getChildAt(index);
+
+ if (child != null)
+ {
+ this.children.splice(index, 1);
+ child.setParent(null);
+ }
+ }
+
+ return child;
+};
+
+/**
+ * Function: removeFromParent
+ *
+ * Removes the cell from its parent.
+ */
+mxCell.prototype.removeFromParent = function()
+{
+ if (this.parent != null)
+ {
+ var index = this.parent.getIndex(this);
+ this.parent.remove(index);
+ }
+};
+
+/**
+ * Function: getEdgeCount
+ *
+ * Returns the number of edges in the edge array.
+ */
+mxCell.prototype.getEdgeCount = function()
+{
+ return (this.edges == null) ? 0 : this.edges.length;
+};
+
+/**
+ * Function: getEdgeIndex
+ *
+ * Returns the index of the specified edge in <edges>.
+ *
+ * Parameters:
+ *
+ * edge - <mxCell> whose index in <edges> should be returned.
+ */
+mxCell.prototype.getEdgeIndex = function(edge)
+{
+ return mxUtils.indexOf(this.edges, edge);
+};
+
+/**
+ * Function: getEdgeAt
+ *
+ * Returns the edge at the specified index in <edges>.
+ *
+ * Parameters:
+ *
+ * index - Integer that specifies the index of the edge to be returned.
+ */
+mxCell.prototype.getEdgeAt = function(index)
+{
+ return (this.edges == null) ? null : this.edges[index];
+};
+
+/**
+ * Function: insertEdge
+ *
+ * Inserts the specified edge into the edge array and returns the edge.
+ * Will update the respective terminal reference of the edge.
+ *
+ * Parameters:
+ *
+ * edge - <mxCell> to be inserted into the edge array.
+ * isOutgoing - Boolean that specifies if the edge is outgoing.
+ */
+mxCell.prototype.insertEdge = function(edge, isOutgoing)
+{
+ if (edge != null)
+ {
+ edge.removeFromTerminal(isOutgoing);
+ edge.setTerminal(this, isOutgoing);
+
+ if (this.edges == null ||
+ edge.getTerminal(!isOutgoing) != this ||
+ mxUtils.indexOf(this.edges, edge) < 0)
+ {
+ if (this.edges == null)
+ {
+ this.edges = [];
+ }
+
+ this.edges.push(edge);
+ }
+ }
+
+ return edge;
+};
+
+/**
+ * Function: removeEdge
+ *
+ * Removes the specified edge from the edge array and returns the edge.
+ * Will remove the respective terminal reference from the edge.
+ *
+ * Parameters:
+ *
+ * edge - <mxCell> to be removed from the edge array.
+ * isOutgoing - Boolean that specifies if the edge is outgoing.
+ */
+mxCell.prototype.removeEdge = function(edge, isOutgoing)
+{
+ if (edge != null)
+ {
+ if (edge.getTerminal(!isOutgoing) != this &&
+ this.edges != null)
+ {
+ var index = this.getEdgeIndex(edge);
+
+ if (index >= 0)
+ {
+ this.edges.splice(index, 1);
+ }
+ }
+
+ edge.setTerminal(null, isOutgoing);
+ }
+
+ return edge;
+};
+
+/**
+ * Function: removeFromTerminal
+ *
+ * Removes the edge from its source or target terminal.
+ *
+ * Parameters:
+ *
+ * isSource - Boolean that specifies if the edge should be removed from its
+ * source or target terminal.
+ */
+mxCell.prototype.removeFromTerminal = function(isSource)
+{
+ var terminal = this.getTerminal(isSource);
+
+ if (terminal != null)
+ {
+ terminal.removeEdge(this, isSource);
+ }
+};
+
+/**
+ * Function: getAttribute
+ *
+ * Returns the specified attribute from the user object if it is an XML
+ * node.
+ *
+ * Parameters:
+ *
+ * name - Name of the attribute whose value should be returned.
+ * defaultValue - Optional default value to use if the attribute has no
+ * value.
+ */
+mxCell.prototype.getAttribute = function(name, defaultValue)
+{
+ var userObject = this.getValue();
+
+ var val = (userObject != null &&
+ userObject.nodeType == mxConstants.NODETYPE_ELEMENT) ?
+ userObject.getAttribute(name) : null;
+
+ return val || defaultValue;
+};
+
+/**
+ * Function: setAttribute
+ *
+ * Sets the specified attribute on the user object if it is an XML node.
+ *
+ * Parameters:
+ *
+ * name - Name of the attribute whose value should be set.
+ * value - New value of the attribute.
+ */
+mxCell.prototype.setAttribute = function(name, value)
+{
+ var userObject = this.getValue();
+
+ if (userObject != null &&
+ userObject.nodeType == mxConstants.NODETYPE_ELEMENT)
+ {
+ userObject.setAttribute(name, value);
+ }
+};
+
+/**
+ * Function: clone
+ *
+ * Returns a clone of the cell. Uses <cloneValue> to clone
+ * the user object. All fields in <mxTransient> are ignored
+ * during the cloning.
+ */
+mxCell.prototype.clone = function()
+{
+ var clone = mxUtils.clone(this, this.mxTransient);
+ clone.setValue(this.cloneValue());
+
+ return clone;
+};
+
+/**
+ * Function: cloneValue
+ *
+ * Returns a clone of the cell's user object.
+ */
+mxCell.prototype.cloneValue = function()
+{
+ var value = this.getValue();
+
+ if (value != null)
+ {
+ if (typeof(value.clone) == 'function')
+ {
+ value = value.clone();
+ }
+ else if (!isNaN(value.nodeType))
+ {
+ value = value.cloneNode(true);
+ }
+ }
+
+ return value;
+};
diff --git a/src/js/model/mxCellPath.js b/src/js/model/mxCellPath.js
new file mode 100644
index 0000000..71a379e
--- /dev/null
+++ b/src/js/model/mxCellPath.js
@@ -0,0 +1,163 @@
+/**
+ * $Id: mxCellPath.js,v 1.12 2010-01-02 09:45:15 gaudenz Exp $
+ * Copyright (c) 2006-2010, JGraph Ltd
+ */
+var mxCellPath =
+{
+
+ /**
+ * Class: mxCellPath
+ *
+ * Implements a mechanism for temporary cell Ids.
+ *
+ * Variable: PATH_SEPARATOR
+ *
+ * Defines the separator between the path components. Default is ".".
+ */
+ PATH_SEPARATOR: '.',
+
+ /**
+ * Function: create
+ *
+ * Creates the cell path for the given cell. The cell path is a
+ * concatenation of the indices of all ancestors on the (finite) path to
+ * the root, eg. "0.0.0.1".
+ *
+ * Parameters:
+ *
+ * cell - Cell whose path should be returned.
+ */
+ create: function(cell)
+ {
+ var result = '';
+
+ if (cell != null)
+ {
+ var parent = cell.getParent();
+
+ while (parent != null)
+ {
+ var index = parent.getIndex(cell);
+ result = index + mxCellPath.PATH_SEPARATOR + result;
+
+ cell = parent;
+ parent = cell.getParent();
+ }
+ }
+
+ // Removes trailing separator
+ var n = result.length;
+
+ if (n > 1)
+ {
+ result = result.substring(0, n - 1);
+ }
+
+ return result;
+ },
+
+ /**
+ * Function: getParentPath
+ *
+ * Returns the path for the parent of the cell represented by the given
+ * path. Returns null if the given path has no parent.
+ *
+ * Parameters:
+ *
+ * path - Path whose parent path should be returned.
+ */
+ getParentPath: function(path)
+ {
+ if (path != null)
+ {
+ var index = path.lastIndexOf(mxCellPath.PATH_SEPARATOR);
+
+ if (index >= 0)
+ {
+ return path.substring(0, index);
+ }
+ else if (path.length > 0)
+ {
+ return '';
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Function: resolve
+ *
+ * Returns the cell for the specified cell path using the given root as the
+ * root of the path.
+ *
+ * Parameters:
+ *
+ * root - Root cell of the path to be resolved.
+ * path - String that defines the path.
+ */
+ resolve: function(root, path)
+ {
+ var parent = root;
+
+ if (path != null)
+ {
+ var tokens = path.split(mxCellPath.PATH_SEPARATOR);
+
+ for (var i=0; i<tokens.length; i++)
+ {
+ parent = parent.getChildAt(parseInt(tokens[i]));
+ }
+ }
+
+ return parent;
+ },
+
+ /**
+ * Function: compare
+ *
+ * Compares the given cell paths and returns -1 if p1 is smaller, 0 if
+ * p1 is equal and 1 if p1 is greater than p2.
+ */
+ compare: function(p1, p2)
+ {
+ var min = Math.min(p1.length, p2.length);
+ var comp = 0;
+
+ for (var i = 0; i < min; i++)
+ {
+ if (p1[i] != p2[i])
+ {
+ if (p1[i].length == 0 ||
+ p2[i].length == 0)
+ {
+ comp = (p1[i] == p2[i]) ? 0 : ((p1[i] > p2[i]) ? 1 : -1);
+ }
+ else
+ {
+ var t1 = parseInt(p1[i]);
+ var t2 = parseInt(p2[i]);
+
+ comp = (t1 == t2) ? 0 : ((t1 > t2) ? 1 : -1);
+ }
+
+ break;
+ }
+ }
+
+ // Compares path length if both paths are equal to this point
+ if (comp == 0)
+ {
+ var t1 = p1.length;
+ var t2 = p2.length;
+
+ if (t1 != t2)
+ {
+ comp = (t1 > t2) ? 1 : -1;
+ }
+ }
+
+ return comp;
+ }
+
+};
diff --git a/src/js/model/mxGeometry.js b/src/js/model/mxGeometry.js
new file mode 100644
index 0000000..51a7d3b
--- /dev/null
+++ b/src/js/model/mxGeometry.js
@@ -0,0 +1,277 @@
+/**
+ * $Id: mxGeometry.js,v 1.26 2010-01-02 09:45:15 gaudenz Exp $
+ * Copyright (c) 2006-2010, JGraph Ltd
+ */
+/**
+ * Class: mxGeometry
+ *
+ * Extends <mxRectangle> to represent the geometry of a cell.
+ *
+ * For vertices, the geometry consists of the x- and y-location, and the width
+ * and height. For edges, the geometry consists of the optional terminal- and
+ * control points. The terminal points are only required if an edge is
+ * unconnected, and are stored in the sourcePoint> and <targetPoint>
+ * variables, respectively.
+ *
+ * Example:
+ *
+ * If an edge is unconnected, that is, it has no source or target terminal,
+ * then a geometry with terminal points for a new edge can be defined as
+ * follows.
+ *
+ * (code)
+ * geometry.setTerminalPoint(new mxPoint(x1, y1), true);
+ * geometry.points = [new mxPoint(x2, y2)];
+ * geometry.setTerminalPoint(new mxPoint(x3, y3), false);
+ * (end)
+ *
+ * Control points are used regardless of the connected state of an edge and may
+ * be ignored or interpreted differently depending on the edge's <mxEdgeStyle>.
+ *
+ * To disable automatic reset of control points after a cell has been moved or
+ * resized, the the <mxGraph.resizeEdgesOnMove> and
+ * <mxGraph.resetEdgesOnResize> may be used.
+ *
+ * Edge Labels:
+ *
+ * Using the x- and y-coordinates of a cell's geometry, it is possible to
+ * position the label on edges on a specific location on the actual edge shape
+ * as it appears on the screen. The x-coordinate of an edge's geometry is used
+ * to describe the distance from the center of the edge from -1 to 1 with 0
+ * being the center of the edge and the default value. The y-coordinate of an
+ * edge's geometry is used to describe the absolute, orthogonal distance in
+ * pixels from that point. In addition, the <mxGeometry.offset> is used as an
+ * absolute offset vector from the resulting point.
+ *
+ * This coordinate system is applied if <relative> is true, otherwise the
+ * offset defines the absolute vector from the edge's center point to the
+ * label.
+ *
+ * Ports:
+ *
+ * The term "port" refers to a relatively positioned, connectable child cell,
+ * which is used to specify the connection between the parent and another cell
+ * in the graph. Ports are typically modeled as vertices with relative
+ * geometries.
+ *
+ * Offsets:
+ *
+ * The <offset> field is interpreted in 3 different ways, depending on the cell
+ * and the geometry. For edges, the offset defines the absolute offset for the
+ * edge label. For relative geometries, the offset defines the absolute offset
+ * for the origin (top, left corner) of the vertex, otherwise the offset
+ * defines the absolute offset for the label inside the vertex or group.
+ *
+ * Constructor: mxGeometry
+ *
+ * Constructs a new object to describe the size and location of a vertex or
+ * the control points of an edge.
+ */
+function mxGeometry(x, y, width, height)
+{
+ mxRectangle.call(this, x, y, width, height);
+};
+
+/**
+ * Extends mxRectangle.
+ */
+mxGeometry.prototype = new mxRectangle();
+mxGeometry.prototype.constructor = mxGeometry;
+
+/**
+ * Variable: TRANSLATE_CONTROL_POINTS
+ *
+ * Global switch to translate the points in translate. Default is true.
+ */
+mxGeometry.prototype.TRANSLATE_CONTROL_POINTS = true;
+
+/**
+ * Variable: alternateBounds
+ *
+ * Stores alternate values for x, y, width and height in a rectangle. See
+ * <swap> to exchange the values. Default is null.
+ */
+mxGeometry.prototype.alternateBounds = null;
+
+/**
+ * Variable: sourcePoint
+ *
+ * Defines the source <mxPoint> of the edge. This is used if the
+ * corresponding edge does not have a source vertex. Otherwise it is
+ * ignored. Default is null.
+ */
+mxGeometry.prototype.sourcePoint = null;
+
+/**
+ * Variable: targetPoint
+ *
+ * Defines the target <mxPoint> of the edge. This is used if the
+ * corresponding edge does not have a target vertex. Otherwise it is
+ * ignored. Default is null.
+ */
+mxGeometry.prototype.targetPoint = null;
+
+/**
+ * Variable: points
+ *
+ * Array of <mxPoints> which specifies the control points along the edge.
+ * These points are the intermediate points on the edge, for the endpoints
+ * use <targetPoint> and <sourcePoint> or set the terminals of the edge to
+ * a non-null value. Default is null.
+ */
+mxGeometry.prototype.points = null;
+
+/**
+ * Variable: offset
+ *
+ * For edges, this holds the offset (in pixels) from the position defined
+ * by <x> and <y> on the edge. For relative geometries (for vertices), this
+ * defines the absolute offset from the point defined by the relative
+ * coordinates. For absolute geometries (for vertices), this defines the
+ * offset for the label. Default is null.
+ */
+mxGeometry.prototype.offset = null;
+
+/**
+ * Variable: relative
+ *
+ * Specifies if the coordinates in the geometry are to be interpreted as
+ * relative coordinates. For edges, this is used to define the location of
+ * the edge label relative to the edge as rendered on the display. For
+ * vertices, this specifies the relative location inside the bounds of the
+ * parent cell.
+ *
+ * If this is false, then the coordinates are relative to the origin of the
+ * parent cell or, for edges, the edge label position is relative to the
+ * center of the edge as rendered on screen.
+ *
+ * Default is false.
+ */
+mxGeometry.prototype.relative = false;
+
+/**
+ * Function: swap
+ *
+ * Swaps the x, y, width and height with the values stored in
+ * <alternateBounds> and puts the previous values into <alternateBounds> as
+ * a rectangle. This operation is carried-out in-place, that is, using the
+ * existing geometry instance. If this operation is called during a graph
+ * model transactional change, then the geometry should be cloned before
+ * calling this method and setting the geometry of the cell using
+ * <mxGraphModel.setGeometry>.
+ */
+mxGeometry.prototype.swap = function()
+{
+ if (this.alternateBounds != null)
+ {
+ var old = new mxRectangle(
+ this.x, this.y, this.width, this.height);
+
+ this.x = this.alternateBounds.x;
+ this.y = this.alternateBounds.y;
+ this.width = this.alternateBounds.width;
+ this.height = this.alternateBounds.height;
+
+ this.alternateBounds = old;
+ }
+};
+
+/**
+ * Function: getTerminalPoint
+ *
+ * Returns the <mxPoint> representing the source or target point of this
+ * edge. This is only used if the edge has no source or target vertex.
+ *
+ * Parameters:
+ *
+ * isSource - Boolean that specifies if the source or target point
+ * should be returned.
+ */
+mxGeometry.prototype.getTerminalPoint = function(isSource)
+{
+ return (isSource) ? this.sourcePoint : this.targetPoint;
+};
+
+/**
+ * Function: setTerminalPoint
+ *
+ * Sets the <sourcePoint> or <targetPoint> to the given <mxPoint> and
+ * returns the new point.
+ *
+ * Parameters:
+ *
+ * point - Point to be used as the new source or target point.
+ * isSource - Boolean that specifies if the source or target point
+ * should be set.
+ */
+mxGeometry.prototype.setTerminalPoint = function(point, isSource)
+{
+ if (isSource)
+ {
+ this.sourcePoint = point;
+ }
+ else
+ {
+ this.targetPoint = point;
+ }
+
+ return point;
+};
+
+/**
+ * Function: translate
+ *
+ * Translates the geometry by the specified amount. That is, <x> and <y>
+ * of the geometry, the <sourcePoint>, <targetPoint> and all elements of
+ * <points> are translated by the given amount. <x> and <y> are only
+ * translated if <relative> is false. If <TRANSLATE_CONTROL_POINTS> is
+ * false, then <points> are not modified by this function.
+ *
+ * Parameters:
+ *
+ * dx - Integer that specifies the x-coordinate of the translation.
+ * dy - Integer that specifies the y-coordinate of the translation.
+ */
+mxGeometry.prototype.translate = function(dx, dy)
+{
+ var clone = this.clone();
+
+ // Translates the geometry
+ if (!this.relative)
+ {
+ this.x += dx;
+ this.y += dy;
+ }
+
+ // Translates the source point
+ if (this.sourcePoint != null)
+ {
+ this.sourcePoint.x += dx;
+ this.sourcePoint.y += dy;
+ }
+
+ // Translates the target point
+ if (this.targetPoint != null)
+ {
+ this.targetPoint.x += dx;
+ this.targetPoint.y += dy;
+ }
+
+ // Translate the control points
+ if (this.TRANSLATE_CONTROL_POINTS &&
+ this.points != null)
+ {
+ var count = this.points.length;
+
+ for (var i = 0; i < count; i++)
+ {
+ var pt = this.points[i];
+
+ if (pt != null)
+ {
+ pt.x += dx;
+ pt.y += dy;
+ }
+ }
+ }
+};
diff --git a/src/js/model/mxGraphModel.js b/src/js/model/mxGraphModel.js
new file mode 100644
index 0000000..c65c0e1
--- /dev/null
+++ b/src/js/model/mxGraphModel.js
@@ -0,0 +1,2622 @@
+/**
+ * $Id: mxGraphModel.js,v 1.125 2012-04-16 10:48:43 david Exp $
+ * Copyright (c) 2006-2010, JGraph Ltd
+ */
+/**
+ * Class: mxGraphModel
+ *
+ * Extends <mxEventSource> to implement a graph model. The graph model acts as
+ * a wrapper around the cells which are in charge of storing the actual graph
+ * datastructure. The model acts as a transactional wrapper with event
+ * notification for all changes, whereas the cells contain the atomic
+ * operations for updating the actual datastructure.
+ *
+ * Layers:
+ *
+ * The cell hierarchy in the model must have a top-level root cell which
+ * contains the layers (typically one default layer), which in turn contain the
+ * top-level cells of the layers. This means each cell is contained in a layer.
+ * If no layers are required, then all new cells should be added to the default
+ * layer.
+ *
+ * Layers are useful for hiding and showing groups of cells, or for placing
+ * groups of cells on top of other cells in the display. To identify a layer,
+ * the <isLayer> function is used. It returns true if the parent of the given
+ * cell is the root of the model.
+ *
+ * Encoding the model:
+ *
+ * To encode a graph model, use the following code:
+ *
+ * (code)
+ * var enc = new mxCodec();
+ * var node = enc.encode(graph.getModel());
+ * (end)
+ *
+ * This will create an XML node that contains all the model information.
+ *
+ * Encoding and decoding changes:
+ *
+ * For the encoding of changes, a graph model listener is required that encodes
+ * each change from the given array of changes.
+ *
+ * (code)
+ * model.addListener(mxEvent.CHANGE, function(sender, evt)
+ * {
+ * var changes = evt.getProperty('edit').changes;
+ * var nodes = [];
+ * var codec = new mxCodec();
+ *
+ * for (var i = 0; i < changes.length; i++)
+ * {
+ * nodes.push(codec.encode(changes[i]));
+ * }
+ * // do something with the nodes
+ * });
+ * (end)
+ *
+ * For the decoding and execution of changes, the codec needs a lookup function
+ * that allows it to resolve cell IDs as follows:
+ *
+ * (code)
+ * var codec = new mxCodec();
+ * codec.lookup = function(id)
+ * {
+ * return model.getCell(id);
+ * }
+ * (end)
+ *
+ * For each encoded change (represented by a node), the following code can be
+ * used to carry out the decoding and create a change object.
+ *
+ * (code)
+ * var changes = [];
+ * var change = codec.decode(node);
+ * change.model = model;
+ * change.execute();
+ * changes.push(change);
+ * (end)
+ *
+ * The changes can then be dispatched using the model as follows.
+ *
+ * (code)
+ * var edit = new mxUndoableEdit(model, false);
+ * edit.changes = changes;
+ *
+ * edit.notify = function()
+ * {
+ * edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
+ * 'edit', edit, 'changes', edit.changes));
+ * edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
+ * 'edit', edit, 'changes', edit.changes));
+ * }
+ *
+ * model.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
+ * model.fireEvent(new mxEventObject(mxEvent.CHANGE,
+ * 'edit', edit, 'changes', changes));
+ * (end)
+ *
+ * Event: mxEvent.CHANGE
+ *
+ * Fires when an undoable edit is dispatched. The <code>edit</code> property
+ * contains the <mxUndoableEdit>. The <code>changes</code> property contains
+ * the array of atomic changes inside the undoable edit. The changes property
+ * is <strong>deprecated</strong>, please use edit.changes instead.
+ *
+ * Example:
+ *
+ * For finding newly inserted cells, the following code can be used:
+ *
+ * (code)
+ * graph.model.addListener(mxEvent.CHANGE, function(sender, evt)
+ * {
+ * var changes = evt.getProperty('edit').changes;
+ *
+ * for (var i = 0; i < changes.length; i++)
+ * {
+ * var change = changes[i];
+ *
+ * if (change instanceof mxChildChange &&
+ * change.change.previous == null)
+ * {
+ * graph.startEditingAtCell(change.child);
+ * break;
+ * }
+ * }
+ * });
+ * (end)
+ *
+ *
+ * Event: mxEvent.NOTIFY
+ *
+ * Same as <mxEvent.CHANGE>, this event can be used for classes that need to
+ * implement a sync mechanism between this model and, say, a remote model. In
+ * such a setup, only local changes should trigger a notify event and all
+ * changes should trigger a change event.
+ *
+ * Event: mxEvent.EXECUTE
+ *
+ * Fires between begin- and endUpdate and after an atomic change was executed
+ * in the model. The <code>change</code> property contains the atomic change
+ * that was executed.
+ *
+ * Event: mxEvent.BEGIN_UPDATE
+ *
+ * Fires after the <updateLevel> was incremented in <beginUpdate>. This event
+ * contains no properties.
+ *
+ * Event: mxEvent.END_UPDATE
+ *
+ * Fires after the <updateLevel> was decreased in <endUpdate> but before any
+ * notification or change dispatching. The <code>edit</code> property contains
+ * the <currentEdit>.
+ *
+ * Event: mxEvent.BEFORE_UNDO
+ *
+ * Fires before the change is dispatched after the update level has reached 0
+ * in <endUpdate>. The <code>edit</code> property contains the <curreneEdit>.
+ *
+ * Event: mxEvent.UNDO
+ *
+ * Fires after the change was dispatched in <endUpdate>. The <code>edit</code>
+ * property contains the <currentEdit>.
+ *
+ * Constructor: mxGraphModel
+ *
+ * Constructs a new graph model. If no root is specified then a new root
+ * <mxCell> with a default layer is created.
+ *
+ * Parameters:
+ *
+ * root - <mxCell> that represents the root cell.
+ */
+function mxGraphModel(root)
+{
+ this.currentEdit = this.createUndoableEdit();
+
+ if (root != null)
+ {
+ this.setRoot(root);
+ }
+ else
+ {
+ this.clear();
+ }
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxGraphModel.prototype = new mxEventSource();
+mxGraphModel.prototype.constructor = mxGraphModel;
+
+/**
+ * Variable: root
+ *
+ * Holds the root cell, which in turn contains the cells that represent the
+ * layers of the diagram as child cells. That is, the actual elements of the
+ * diagram are supposed to live in the third generation of cells and below.
+ */
+mxGraphModel.prototype.root = null;
+
+/**
+ * Variable: cells
+ *
+ * Maps from Ids to cells.
+ */
+mxGraphModel.prototype.cells = null;
+
+/**
+ * Variable: maintainEdgeParent
+ *
+ * Specifies if edges should automatically be moved into the nearest common
+ * ancestor of their terminals. Default is true.
+ */
+mxGraphModel.prototype.maintainEdgeParent = true;
+
+/**
+ * Variable: createIds
+ *
+ * Specifies if the model should automatically create Ids for new cells.
+ * Default is true.
+ */
+mxGraphModel.prototype.createIds = true;
+
+/**
+ * Variable: prefix
+ *
+ * Defines the prefix of new Ids. Default is an empty string.
+ */
+mxGraphModel.prototype.prefix = '';
+
+/**
+ * Variable: postfix
+ *
+ * Defines the postfix of new Ids. Default is an empty string.
+ */
+mxGraphModel.prototype.postfix = '';
+
+/**
+ * Variable: nextId
+ *
+ * Specifies the next Id to be created. Initial value is 0.
+ */
+mxGraphModel.prototype.nextId = 0;
+
+/**
+ * Variable: currentEdit
+ *
+ * Holds the changes for the current transaction. If the transaction is
+ * closed then a new object is created for this variable using
+ * <createUndoableEdit>.
+ */
+mxGraphModel.prototype.currentEdit = null;
+
+/**
+ * Variable: updateLevel
+ *
+ * Counter for the depth of nested transactions. Each call to <beginUpdate>
+ * will increment this number and each call to <endUpdate> will decrement
+ * it. When the counter reaches 0, the transaction is closed and the
+ * respective events are fired. Initial value is 0.
+ */
+mxGraphModel.prototype.updateLevel = 0;
+
+/**
+ * Variable: endingUpdate
+ *
+ * True if the program flow is currently inside endUpdate.
+ */
+mxGraphModel.prototype.endingUpdate = false;
+
+/**
+ * Function: clear
+ *
+ * Sets a new root using <createRoot>.
+ */
+mxGraphModel.prototype.clear = function()
+{
+ this.setRoot(this.createRoot());
+};
+
+/**
+ * Function: isCreateIds
+ *
+ * Returns <createIds>.
+ */
+mxGraphModel.prototype.isCreateIds = function()
+{
+ return this.createIds;
+};
+
+/**
+ * Function: setCreateIds
+ *
+ * Sets <createIds>.
+ */
+mxGraphModel.prototype.setCreateIds = function(value)
+{
+ this.createIds = value;
+};
+
+/**
+ * Function: createRoot
+ *
+ * Creates a new root cell with a default layer (child 0).
+ */
+mxGraphModel.prototype.createRoot = function()
+{
+ var cell = new mxCell();
+ cell.insert(new mxCell());
+
+ return cell;
+};
+
+/**
+ * Function: getCell
+ *
+ * Returns the <mxCell> for the specified Id or null if no cell can be
+ * found for the given Id.
+ *
+ * Parameters:
+ *
+ * id - A string representing the Id of the cell.
+ */
+mxGraphModel.prototype.getCell = function(id)
+{
+ return (this.cells != null) ? this.cells[id] : null;
+};
+
+/**
+ * Function: filterCells
+ *
+ * Returns the cells from the given array where the fiven filter function
+ * returns true.
+ */
+mxGraphModel.prototype.filterCells = function(cells, filter)
+{
+ var result = null;
+
+ if (cells != null)
+ {
+ result = [];
+
+ for (var i = 0; i < cells.length; i++)
+ {
+ if (filter(cells[i]))
+ {
+ result.push(cells[i]);
+ }
+ }
+ }
+
+ return result;
+};
+
+/**
+ * Function: getDescendants
+ *
+ * Returns all descendants of the given cell and the cell itself in an array.
+ *
+ * Parameters:
+ *
+ * parent - <mxCell> whose descendants should be returned.
+ */
+mxGraphModel.prototype.getDescendants = function(parent)
+{
+ return this.filterDescendants(null, parent);
+};
+
+/**
+ * Function: filterDescendants
+ *
+ * Visits all cells recursively and applies the specified filter function
+ * to each cell. If the function returns true then the cell is added
+ * to the resulting array. The parent and result paramters are optional.
+ * If parent is not specified then the recursion starts at <root>.
+ *
+ * Example:
+ * The following example extracts all vertices from a given model:
+ * (code)
+ * var filter = function(cell)
+ * {
+ * return model.isVertex(cell);
+ * }
+ * var vertices = model.filterDescendants(filter);
+ * (code)
+ *
+ * Parameters:
+ *
+ * filter - JavaScript function that takes an <mxCell> as an argument
+ * and returns a boolean.
+ * parent - Optional <mxCell> that is used as the root of the recursion.
+ */
+mxGraphModel.prototype.filterDescendants = function(filter, parent)
+{
+ // Creates a new array for storing the result
+ var result = [];
+
+ // Recursion starts at the root of the model
+ parent = parent || this.getRoot();
+
+ // Checks if the filter returns true for the cell
+ // and adds it to the result array
+ if (filter == null || filter(parent))
+ {
+ result.push(parent);
+ }
+
+ // Visits the children of the cell
+ var childCount = this.getChildCount(parent);
+
+ for (var i = 0; i < childCount; i++)
+ {
+ var child = this.getChildAt(parent, i);
+ result = result.concat(this.filterDescendants(filter, child));
+ }
+
+ return result;
+};
+
+/**
+ * Function: getRoot
+ *
+ * Returns the root of the model or the topmost parent of the given cell.
+ *
+ * Parameters:
+ *
+ * cell - Optional <mxCell> that specifies the child.
+ */
+mxGraphModel.prototype.getRoot = function(cell)
+{
+ var root = cell || this.root;
+
+ if (cell != null)
+ {
+ while (cell != null)
+ {
+ root = cell;
+ cell = this.getParent(cell);
+ }
+ }
+
+ return root;
+};
+
+/**
+ * Function: setRoot
+ *
+ * Sets the <root> of the model using <mxRootChange> and adds the change to
+ * the current transaction. This resets all datastructures in the model and
+ * is the preferred way of clearing an existing model. Returns the new
+ * root.
+ *
+ * Example:
+ *
+ * (code)
+ * var root = new mxCell();
+ * root.insert(new mxCell());
+ * model.setRoot(root);
+ * (end)
+ *
+ * Parameters:
+ *
+ * root - <mxCell> that specifies the new root.
+ */
+mxGraphModel.prototype.setRoot = function(root)
+{
+ this.execute(new mxRootChange(this, root));
+
+ return root;
+};
+
+/**
+ * Function: rootChanged
+ *
+ * Inner callback to change the root of the model and update the internal
+ * datastructures, such as <cells> and <nextId>. Returns the previous root.
+ *
+ * Parameters:
+ *
+ * root - <mxCell> that specifies the new root.
+ */
+mxGraphModel.prototype.rootChanged = function(root)
+{
+ var oldRoot = this.root;
+ this.root = root;
+
+ // Resets counters and datastructures
+ this.nextId = 0;
+ this.cells = null;
+ this.cellAdded(root);
+
+ return oldRoot;
+};
+
+/**
+ * Function: isRoot
+ *
+ * Returns true if the given cell is the root of the model and a non-null
+ * value.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that represents the possible root.
+ */
+mxGraphModel.prototype.isRoot = function(cell)
+{
+ return cell != null && this.root == cell;
+};
+
+/**
+ * Function: isLayer
+ *
+ * Returns true if <isRoot> returns true for the parent of the given cell.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that represents the possible layer.
+ */
+mxGraphModel.prototype.isLayer = function(cell)
+{
+ return this.isRoot(this.getParent(cell));
+};
+
+/**
+ * Function: isAncestor
+ *
+ * Returns true if the given parent is an ancestor of the given child.
+ *
+ * Parameters:
+ *
+ * parent - <mxCell> that specifies the parent.
+ * child - <mxCell> that specifies the child.
+ */
+mxGraphModel.prototype.isAncestor = function(parent, child)
+{
+ while (child != null && child != parent)
+ {
+ child = this.getParent(child);
+ }
+
+ return child == parent;
+};
+
+/**
+ * Function: contains
+ *
+ * Returns true if the model contains the given <mxCell>.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that specifies the cell.
+ */
+mxGraphModel.prototype.contains = function(cell)
+{
+ return this.isAncestor(this.root, cell);
+};
+
+/**
+ * Function: getParent
+ *
+ * Returns the parent of the given cell.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose parent should be returned.
+ */
+mxGraphModel.prototype.getParent = function(cell)
+{
+ return (cell != null) ? cell.getParent() : null;
+};
+
+/**
+ * Function: add
+ *
+ * Adds the specified child to the parent at the given index using
+ * <mxChildChange> and adds the change to the current transaction. If no
+ * index is specified then the child is appended to the parent's array of
+ * children. Returns the inserted child.
+ *
+ * Parameters:
+ *
+ * parent - <mxCell> that specifies the parent to contain the child.
+ * child - <mxCell> that specifies the child to be inserted.
+ * index - Optional integer that specifies the index of the child.
+ */
+mxGraphModel.prototype.add = function(parent, child, index)
+{
+ if (child != parent && parent != null && child != null)
+ {
+ // Appends the child if no index was specified
+ if (index == null)
+ {
+ index = this.getChildCount(parent);
+ }
+
+ var parentChanged = parent != this.getParent(child);
+ this.execute(new mxChildChange(this, parent, child, index));
+
+ // Maintains the edges parents by moving the edges
+ // into the nearest common ancestor of its
+ // terminals
+ if (this.maintainEdgeParent && parentChanged)
+ {
+ this.updateEdgeParents(child);
+ }
+ }
+
+ return child;
+};
+
+/**
+ * Function: cellAdded
+ *
+ * Inner callback to update <cells> when a cell has been added. This
+ * implementation resolves collisions by creating new Ids. To change the
+ * ID of a cell after it was inserted into the model, use the following
+ * code:
+ *
+ * (code
+ * delete model.cells[cell.getId()];
+ * cell.setId(newId);
+ * model.cells[cell.getId()] = cell;
+ * (end)
+ *
+ * If the change of the ID should be part of the command history, then the
+ * cell should be removed from the model and a clone with the new ID should
+ * be reinserted into the model instead.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that specifies the cell that has been added.
+ */
+mxGraphModel.prototype.cellAdded = function(cell)
+{
+ if (cell != null)
+ {
+ // Creates an Id for the cell if not Id exists
+ if (cell.getId() == null && this.createIds)
+ {
+ cell.setId(this.createId(cell));
+ }
+
+ if (cell.getId() != null)
+ {
+ var collision = this.getCell(cell.getId());
+
+ if (collision != cell)
+ {
+ // Creates new Id for the cell
+ // as long as there is a collision
+ while (collision != null)
+ {
+ cell.setId(this.createId(cell));
+ collision = this.getCell(cell.getId());
+ }
+
+ // Lazily creates the cells dictionary
+ if (this.cells == null)
+ {
+ this.cells = new Object();
+ }
+
+ this.cells[cell.getId()] = cell;
+ }
+ }
+
+ // Makes sure IDs of deleted cells are not reused
+ if (mxUtils.isNumeric(cell.getId()))
+ {
+ this.nextId = Math.max(this.nextId, cell.getId());
+ }
+
+ // Recursively processes child cells
+ var childCount = this.getChildCount(cell);
+
+ for (var i=0; i<childCount; i++)
+ {
+ this.cellAdded(this.getChildAt(cell, i));
+ }
+ }
+};
+
+/**
+ * Function: createId
+ *
+ * Hook method to create an Id for the specified cell. This implementation
+ * concatenates <prefix>, id and <postfix> to create the Id and increments
+ * <nextId>. The cell is ignored by this implementation, but can be used in
+ * overridden methods to prefix the Ids with eg. the cell type.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> to create the Id for.
+ */
+mxGraphModel.prototype.createId = function(cell)
+{
+ var id = this.nextId;
+ this.nextId++;
+
+ return this.prefix + id + this.postfix;
+};
+
+/**
+ * Function: updateEdgeParents
+ *
+ * Updates the parent for all edges that are connected to cell or one of
+ * its descendants using <updateEdgeParent>.
+ */
+mxGraphModel.prototype.updateEdgeParents = function(cell, root)
+{
+ // Gets the topmost node of the hierarchy
+ root = root || this.getRoot(cell);
+
+ // Updates edges on children first
+ var childCount = this.getChildCount(cell);
+
+ for (var i = 0; i < childCount; i++)
+ {
+ var child = this.getChildAt(cell, i);
+ this.updateEdgeParents(child, root);
+ }
+
+ // Updates the parents of all connected edges
+ var edgeCount = this.getEdgeCount(cell);
+ var edges = [];
+
+ for (var i = 0; i < edgeCount; i++)
+ {
+ edges.push(this.getEdgeAt(cell, i));
+ }
+
+ for (var i = 0; i < edges.length; i++)
+ {
+ var edge = edges[i];
+
+ // Updates edge parent if edge and child have
+ // a common root node (does not need to be the
+ // model root node)
+ if (this.isAncestor(root, edge))
+ {
+ this.updateEdgeParent(edge, root);
+ }
+ }
+};
+
+/**
+ * Function: updateEdgeParent
+ *
+ * Inner callback to update the parent of the specified <mxCell> to the
+ * nearest-common-ancestor of its two terminals.
+ *
+ * Parameters:
+ *
+ * edge - <mxCell> that specifies the edge.
+ * root - <mxCell> that represents the current root of the model.
+ */
+mxGraphModel.prototype.updateEdgeParent = function(edge, root)
+{
+ var source = this.getTerminal(edge, true);
+ var target = this.getTerminal(edge, false);
+ var cell = null;
+
+ // Uses the first non-relative descendants of the source terminal
+ while (source != null && !this.isEdge(source) &&
+ source.geometry != null && source.geometry.relative)
+ {
+ source = this.getParent(source);
+ }
+
+ // Uses the first non-relative descendants of the target terminal
+ while (target != null && !this.isEdge(target) &&
+ target.geometry != null && target.geometry.relative)
+ {
+ target = this.getParent(target);
+ }
+
+ if (this.isAncestor(root, source) && this.isAncestor(root, target))
+ {
+ if (source == target)
+ {
+ cell = this.getParent(source);
+ }
+ else
+ {
+ cell = this.getNearestCommonAncestor(source, target);
+ }
+
+ if (cell != null && (this.getParent(cell) != this.root ||
+ this.isAncestor(cell, edge)) && this.getParent(edge) != cell)
+ {
+ var geo = this.getGeometry(edge);
+
+ if (geo != null)
+ {
+ var origin1 = this.getOrigin(this.getParent(edge));
+ var origin2 = this.getOrigin(cell);
+
+ var dx = origin2.x - origin1.x;
+ var dy = origin2.y - origin1.y;
+
+ geo = geo.clone();
+ geo.translate(-dx, -dy);
+ this.setGeometry(edge, geo);
+ }
+
+ this.add(cell, edge, this.getChildCount(cell));
+ }
+ }
+};
+
+/**
+ * Function: getOrigin
+ *
+ * Returns the absolute, accumulated origin for the children inside the
+ * given parent as an <mxPoint>.
+ */
+mxGraphModel.prototype.getOrigin = function(cell)
+{
+ var result = null;
+
+ if (cell != null)
+ {
+ result = this.getOrigin(this.getParent(cell));
+
+ if (!this.isEdge(cell))
+ {
+ var geo = this.getGeometry(cell);
+
+ if (geo != null)
+ {
+ result.x += geo.x;
+ result.y += geo.y;
+ }
+ }
+ }
+ else
+ {
+ result = new mxPoint();
+ }
+
+ return result;
+};
+
+/**
+ * Function: getNearestCommonAncestor
+ *
+ * Returns the nearest common ancestor for the specified cells.
+ *
+ * Parameters:
+ *
+ * cell1 - <mxCell> that specifies the first cell in the tree.
+ * cell2 - <mxCell> that specifies the second cell in the tree.
+ */
+mxGraphModel.prototype.getNearestCommonAncestor = function(cell1, cell2)
+{
+ if (cell1 != null && cell2 != null)
+ {
+ // Creates the cell path for the second cell
+ var path = mxCellPath.create(cell2);
+
+ if (path != null && path.length > 0)
+ {
+ // Bubbles through the ancestors of the first
+ // cell to find the nearest common ancestor.
+ var cell = cell1;
+ var current = mxCellPath.create(cell);
+
+ // Inverts arguments
+ if (path.length < current.length)
+ {
+ cell = cell2;
+ var tmp = current;
+ current = path;
+ path = tmp;
+ }
+
+ while (cell != null)
+ {
+ var parent = this.getParent(cell);
+
+ // Checks if the cell path is equal to the beginning of the given cell path
+ if (path.indexOf(current + mxCellPath.PATH_SEPARATOR) == 0 && parent != null)
+ {
+ return cell;
+ }
+
+ current = mxCellPath.getParentPath(current);
+ cell = parent;
+ }
+ }
+ }
+
+ return null;
+};
+
+/**
+ * Function: remove
+ *
+ * Removes the specified cell from the model using <mxChildChange> and adds
+ * the change to the current transaction. This operation will remove the
+ * cell and all of its children from the model. Returns the removed cell.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that should be removed.
+ */
+mxGraphModel.prototype.remove = function(cell)
+{
+ if (cell == this.root)
+ {
+ this.setRoot(null);
+ }
+ else if (this.getParent(cell) != null)
+ {
+ this.execute(new mxChildChange(this, null, cell));
+ }
+
+ return cell;
+};
+
+/**
+ * Function: cellRemoved
+ *
+ * Inner callback to update <cells> when a cell has been removed.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that specifies the cell that has been removed.
+ */
+mxGraphModel.prototype.cellRemoved = function(cell)
+{
+ if (cell != null && this.cells != null)
+ {
+ // Recursively processes child cells
+ var childCount = this.getChildCount(cell);
+
+ for (var i = childCount - 1; i >= 0; i--)
+ {
+ this.cellRemoved(this.getChildAt(cell, i));
+ }
+
+ // Removes the dictionary entry for the cell
+ if (this.cells != null && cell.getId() != null)
+ {
+ delete this.cells[cell.getId()];
+ }
+ }
+};
+
+/**
+ * Function: parentForCellChanged
+ *
+ * Inner callback to update the parent of a cell using <mxCell.insert>
+ * on the parent and return the previous parent.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> to update the parent for.
+ * parent - <mxCell> that specifies the new parent of the cell.
+ * index - Optional integer that defines the index of the child
+ * in the parent's child array.
+ */
+mxGraphModel.prototype.parentForCellChanged = function(cell, parent, index)
+{
+ var previous = this.getParent(cell);
+
+ if (parent != null)
+ {
+ if (parent != previous || previous.getIndex(cell) != index)
+ {
+ parent.insert(cell, index);
+ }
+ }
+ else if (previous != null)
+ {
+ var oldIndex = previous.getIndex(cell);
+ previous.remove(oldIndex);
+ }
+
+ // Checks if the previous parent was already in the
+ // model and avoids calling cellAdded if it was.
+ if (!this.contains(previous) && parent != null)
+ {
+ this.cellAdded(cell);
+ }
+ else if (parent == null)
+ {
+ this.cellRemoved(cell);
+ }
+
+ return previous;
+};
+
+/**
+ * Function: getChildCount
+ *
+ * Returns the number of children in the given cell.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose number of children should be returned.
+ */
+mxGraphModel.prototype.getChildCount = function(cell)
+{
+ return (cell != null) ? cell.getChildCount() : 0;
+};
+
+/**
+ * Function: getChildAt
+ *
+ * Returns the child of the given <mxCell> at the given index.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that represents the parent.
+ * index - Integer that specifies the index of the child to be returned.
+ */
+mxGraphModel.prototype.getChildAt = function(cell, index)
+{
+ return (cell != null) ? cell.getChildAt(index) : null;
+};
+
+/**
+ * Function: getChildren
+ *
+ * Returns all children of the given <mxCell> as an array of <mxCells>. The
+ * return value should be only be read.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> the represents the parent.
+ */
+mxGraphModel.prototype.getChildren = function(cell)
+{
+ return (cell != null) ? cell.children : null;
+};
+
+/**
+ * Function: getChildVertices
+ *
+ * Returns the child vertices of the given parent.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose child vertices should be returned.
+ */
+mxGraphModel.prototype.getChildVertices = function(parent)
+{
+ return this.getChildCells(parent, true, false);
+};
+
+/**
+ * Function: getChildEdges
+ *
+ * Returns the child edges of the given parent.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose child edges should be returned.
+ */
+mxGraphModel.prototype.getChildEdges = function(parent)
+{
+ return this.getChildCells(parent, false, true);
+};
+
+/**
+ * Function: getChildCells
+ *
+ * Returns the children of the given cell that are vertices and/or edges
+ * depending on the arguments.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> the represents the parent.
+ * vertices - Boolean indicating if child vertices should be returned.
+ * Default is false.
+ * edges - Boolean indicating if child edges should be returned.
+ * Default is false.
+ */
+mxGraphModel.prototype.getChildCells = function(parent, vertices, edges)
+{
+ vertices = (vertices != null) ? vertices : false;
+ edges = (edges != null) ? edges : false;
+
+ var childCount = this.getChildCount(parent);
+ var result = [];
+
+ for (var i = 0; i < childCount; i++)
+ {
+ var child = this.getChildAt(parent, i);
+
+ if ((!edges && !vertices) || (edges && this.isEdge(child)) ||
+ (vertices && this.isVertex(child)))
+ {
+ result.push(child);
+ }
+ }
+
+ return result;
+};
+
+/**
+ * Function: getTerminal
+ *
+ * Returns the source or target <mxCell> of the given edge depending on the
+ * value of the boolean parameter.
+ *
+ * Parameters:
+ *
+ * edge - <mxCell> that specifies the edge.
+ * isSource - Boolean indicating which end of the edge should be returned.
+ */
+mxGraphModel.prototype.getTerminal = function(edge, isSource)
+{
+ return (edge != null) ? edge.getTerminal(isSource) : null;
+};
+
+/**
+ * Function: setTerminal
+ *
+ * Sets the source or target terminal of the given <mxCell> using
+ * <mxTerminalChange> and adds the change to the current transaction.
+ * This implementation updates the parent of the edge using <updateEdgeParent>
+ * if required.
+ *
+ * Parameters:
+ *
+ * edge - <mxCell> that specifies the edge.
+ * terminal - <mxCell> that specifies the new terminal.
+ * isSource - Boolean indicating if the terminal is the new source or
+ * target terminal of the edge.
+ */
+mxGraphModel.prototype.setTerminal = function(edge, terminal, isSource)
+{
+ var terminalChanged = terminal != this.getTerminal(edge, isSource);
+ this.execute(new mxTerminalChange(this, edge, terminal, isSource));
+
+ if (this.maintainEdgeParent && terminalChanged)
+ {
+ this.updateEdgeParent(edge, this.getRoot());
+ }
+
+ return terminal;
+};
+
+/**
+ * Function: setTerminals
+ *
+ * Sets the source and target <mxCell> of the given <mxCell> in a single
+ * transaction using <setTerminal> for each end of the edge.
+ *
+ * Parameters:
+ *
+ * edge - <mxCell> that specifies the edge.
+ * source - <mxCell> that specifies the new source terminal.
+ * target - <mxCell> that specifies the new target terminal.
+ */
+mxGraphModel.prototype.setTerminals = function(edge, source, target)
+{
+ this.beginUpdate();
+ try
+ {
+ this.setTerminal(edge, source, true);
+ this.setTerminal(edge, target, false);
+ }
+ finally
+ {
+ this.endUpdate();
+ }
+};
+
+/**
+ * Function: terminalForCellChanged
+ *
+ * Inner helper function to update the terminal of the edge using
+ * <mxCell.insertEdge> and return the previous terminal.
+ *
+ * Parameters:
+ *
+ * edge - <mxCell> that specifies the edge to be updated.
+ * terminal - <mxCell> that specifies the new terminal.
+ * isSource - Boolean indicating if the terminal is the new source or
+ * target terminal of the edge.
+ */
+mxGraphModel.prototype.terminalForCellChanged = function(edge, terminal, isSource)
+{
+ var previous = this.getTerminal(edge, isSource);
+
+ if (terminal != null)
+ {
+ terminal.insertEdge(edge, isSource);
+ }
+ else if (previous != null)
+ {
+ previous.removeEdge(edge, isSource);
+ }
+
+ return previous;
+};
+
+/**
+ * Function: getEdgeCount
+ *
+ * Returns the number of distinct edges connected to the given cell.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that represents the vertex.
+ */
+mxGraphModel.prototype.getEdgeCount = function(cell)
+{
+ return (cell != null) ? cell.getEdgeCount() : 0;
+};
+
+/**
+ * Function: getEdgeAt
+ *
+ * Returns the edge of cell at the given index.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that specifies the vertex.
+ * index - Integer that specifies the index of the edge
+ * to return.
+ */
+mxGraphModel.prototype.getEdgeAt = function(cell, index)
+{
+ return (cell != null) ? cell.getEdgeAt(index) : null;
+};
+
+/**
+ * Function: getDirectedEdgeCount
+ *
+ * Returns the number of incoming or outgoing edges, ignoring the given
+ * edge.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose edge count should be returned.
+ * outgoing - Boolean that specifies if the number of outgoing or
+ * incoming edges should be returned.
+ * ignoredEdge - <mxCell> that represents an edge to be ignored.
+ */
+mxGraphModel.prototype.getDirectedEdgeCount = function(cell, outgoing, ignoredEdge)
+{
+ var count = 0;
+ var edgeCount = this.getEdgeCount(cell);
+
+ for (var i = 0; i < edgeCount; i++)
+ {
+ var edge = this.getEdgeAt(cell, i);
+
+ if (edge != ignoredEdge && this.getTerminal(edge, outgoing) == cell)
+ {
+ count++;
+ }
+ }
+
+ return count;
+};
+
+/**
+ * Function: getConnections
+ *
+ * Returns all edges of the given cell without loops.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose edges should be returned.
+ *
+ */
+mxGraphModel.prototype.getConnections = function(cell)
+{
+ return this.getEdges(cell, true, true, false);
+};
+
+/**
+ * Function: getIncomingEdges
+ *
+ * Returns the incoming edges of the given cell without loops.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose incoming edges should be returned.
+ *
+ */
+mxGraphModel.prototype.getIncomingEdges = function(cell)
+{
+ return this.getEdges(cell, true, false, false);
+};
+
+/**
+ * Function: getOutgoingEdges
+ *
+ * Returns the outgoing edges of the given cell without loops.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose outgoing edges should be returned.
+ *
+ */
+mxGraphModel.prototype.getOutgoingEdges = function(cell)
+{
+ return this.getEdges(cell, false, true, false);
+};
+
+/**
+ * Function: getEdges
+ *
+ * Returns all distinct edges connected to this cell as a new array of
+ * <mxCells>. If at least one of incoming or outgoing is true, then loops
+ * are ignored, otherwise if both are false, then all edges connected to
+ * the given cell are returned including loops.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that specifies the cell.
+ * incoming - Optional boolean that specifies if incoming edges should be
+ * returned. Default is true.
+ * outgoing - Optional boolean that specifies if outgoing edges should be
+ * returned. Default is true.
+ * includeLoops - Optional boolean that specifies if loops should be returned.
+ * Default is true.
+ */
+mxGraphModel.prototype.getEdges = function(cell, incoming, outgoing, includeLoops)
+{
+ incoming = (incoming != null) ? incoming : true;
+ outgoing = (outgoing != null) ? outgoing : true;
+ includeLoops = (includeLoops != null) ? includeLoops : true;
+
+ var edgeCount = this.getEdgeCount(cell);
+ var result = [];
+
+ for (var i = 0; i < edgeCount; i++)
+ {
+ var edge = this.getEdgeAt(cell, i);
+ var source = this.getTerminal(edge, true);
+ var target = this.getTerminal(edge, false);
+
+ if ((includeLoops && source == target) || ((source != target) && ((incoming && target == cell) ||
+ (outgoing && source == cell))))
+ {
+ result.push(edge);
+ }
+ }
+
+ return result;
+};
+
+/**
+ * Function: getEdgesBetween
+ *
+ * Returns all edges between the given source and target pair. If directed
+ * is true, then only edges from the source to the target are returned,
+ * otherwise, all edges between the two cells are returned.
+ *
+ * Parameters:
+ *
+ * source - <mxCell> that defines the source terminal of the edge to be
+ * returned.
+ * target - <mxCell> that defines the target terminal of the edge to be
+ * returned.
+ * directed - Optional boolean that specifies if the direction of the
+ * edge should be taken into account. Default is false.
+ */
+mxGraphModel.prototype.getEdgesBetween = function(source, target, directed)
+{
+ directed = (directed != null) ? directed : false;
+
+ var tmp1 = this.getEdgeCount(source);
+ var tmp2 = this.getEdgeCount(target);
+
+ // Assumes the source has less connected edges
+ var terminal = source;
+ var edgeCount = tmp1;
+
+ // Uses the smaller array of connected edges
+ // for searching the edge
+ if (tmp2 < tmp1)
+ {
+ edgeCount = tmp2;
+ terminal = target;
+ }
+
+ var result = [];
+
+ // Checks if the edge is connected to the correct
+ // cell and returns the first match
+ for (var i = 0; i < edgeCount; i++)
+ {
+ var edge = this.getEdgeAt(terminal, i);
+ var src = this.getTerminal(edge, true);
+ var trg = this.getTerminal(edge, false);
+ var directedMatch = (src == source) && (trg == target);
+ var oppositeMatch = (trg == source) && (src == target);
+
+ if (directedMatch || (!directed && oppositeMatch))
+ {
+ result.push(edge);
+ }
+ }
+
+ return result;
+};
+
+/**
+ * Function: getOpposites
+ *
+ * Returns all opposite vertices wrt terminal for the given edges, only
+ * returning sources and/or targets as specified. The result is returned
+ * as an array of <mxCells>.
+ *
+ * Parameters:
+ *
+ * edges - Array of <mxCells> that contain the edges to be examined.
+ * terminal - <mxCell> that specifies the known end of the edges.
+ * sources - Boolean that specifies if source terminals should be contained
+ * in the result. Default is true.
+ * targets - Boolean that specifies if target terminals should be contained
+ * in the result. Default is true.
+ */
+mxGraphModel.prototype.getOpposites = function(edges, terminal, sources, targets)
+{
+ sources = (sources != null) ? sources : true;
+ targets = (targets != null) ? targets : true;
+
+ var terminals = [];
+
+ if (edges != null)
+ {
+ for (var i = 0; i < edges.length; i++)
+ {
+ var source = this.getTerminal(edges[i], true);
+ var target = this.getTerminal(edges[i], false);
+
+ // Checks if the terminal is the source of
+ // the edge and if the target should be
+ // stored in the result
+ if (source == terminal && target != null && target != terminal && targets)
+ {
+ terminals.push(target);
+ }
+
+ // Checks if the terminal is the taget of
+ // the edge and if the source should be
+ // stored in the result
+ else if (target == terminal && source != null && source != terminal && sources)
+ {
+ terminals.push(source);
+ }
+ }
+ }
+
+ return terminals;
+};
+
+/**
+ * Function: getTopmostCells
+ *
+ * Returns the topmost cells of the hierarchy in an array that contains no
+ * descendants for each <mxCell> that it contains. Duplicates should be
+ * removed in the cells array to improve performance.
+ *
+ * Parameters:
+ *
+ * cells - Array of <mxCells> whose topmost ancestors should be returned.
+ */
+mxGraphModel.prototype.getTopmostCells = function(cells)
+{
+ var tmp = [];
+
+ for (var i = 0; i < cells.length; i++)
+ {
+ var cell = cells[i];
+ var topmost = true;
+ var parent = this.getParent(cell);
+
+ while (parent != null)
+ {
+ if (mxUtils.indexOf(cells, parent) >= 0)
+ {
+ topmost = false;
+ break;
+ }
+
+ parent = this.getParent(parent);
+ }
+
+ if (topmost)
+ {
+ tmp.push(cell);
+ }
+ }
+
+ return tmp;
+};
+
+/**
+ * Function: isVertex
+ *
+ * Returns true if the given cell is a vertex.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that represents the possible vertex.
+ */
+mxGraphModel.prototype.isVertex = function(cell)
+{
+ return (cell != null) ? cell.isVertex() : false;
+};
+
+/**
+ * Function: isEdge
+ *
+ * Returns true if the given cell is an edge.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that represents the possible edge.
+ */
+mxGraphModel.prototype.isEdge = function(cell)
+{
+ return (cell != null) ? cell.isEdge() : false;
+};
+
+/**
+ * Function: isConnectable
+ *
+ * Returns true if the given <mxCell> is connectable. If <edgesConnectable>
+ * is false, then this function returns false for all edges else it returns
+ * the return value of <mxCell.isConnectable>.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose connectable state should be returned.
+ */
+mxGraphModel.prototype.isConnectable = function(cell)
+{
+ return (cell != null) ? cell.isConnectable() : false;
+};
+
+/**
+ * Function: getValue
+ *
+ * Returns the user object of the given <mxCell> using <mxCell.getValue>.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose user object should be returned.
+ */
+mxGraphModel.prototype.getValue = function(cell)
+{
+ return (cell != null) ? cell.getValue() : null;
+};
+
+/**
+ * Function: setValue
+ *
+ * Sets the user object of then given <mxCell> using <mxValueChange>
+ * and adds the change to the current transaction.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose user object should be changed.
+ * value - Object that defines the new user object.
+ */
+mxGraphModel.prototype.setValue = function(cell, value)
+{
+ this.execute(new mxValueChange(this, cell, value));
+
+ return value;
+};
+
+/**
+ * Function: valueForCellChanged
+ *
+ * Inner callback to update the user object of the given <mxCell>
+ * using <mxCell.valueChanged> and return the previous value,
+ * that is, the return value of <mxCell.valueChanged>.
+ *
+ * To change a specific attribute in an XML node, the following code can be
+ * used.
+ *
+ * (code)
+ * graph.getModel().valueForCellChanged = function(cell, value)
+ * {
+ * var previous = cell.value.getAttribute('label');
+ * cell.value.setAttribute('label', value);
+ *
+ * return previous;
+ * };
+ * (end)
+ */
+mxGraphModel.prototype.valueForCellChanged = function(cell, value)
+{
+ return cell.valueChanged(value);
+};
+
+/**
+ * Function: getGeometry
+ *
+ * Returns the <mxGeometry> of the given <mxCell>.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose geometry should be returned.
+ */
+mxGraphModel.prototype.getGeometry = function(cell, geometry)
+{
+ return (cell != null) ? cell.getGeometry() : null;
+};
+
+/**
+ * Function: setGeometry
+ *
+ * Sets the <mxGeometry> of the given <mxCell>. The actual update
+ * of the cell is carried out in <geometryForCellChanged>. The
+ * <mxGeometryChange> action is used to encapsulate the change.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose geometry should be changed.
+ * geometry - <mxGeometry> that defines the new geometry.
+ */
+mxGraphModel.prototype.setGeometry = function(cell, geometry)
+{
+ if (geometry != this.getGeometry(cell))
+ {
+ this.execute(new mxGeometryChange(this, cell, geometry));
+ }
+
+ return geometry;
+};
+
+/**
+ * Function: geometryForCellChanged
+ *
+ * Inner callback to update the <mxGeometry> of the given <mxCell> using
+ * <mxCell.setGeometry> and return the previous <mxGeometry>.
+ */
+mxGraphModel.prototype.geometryForCellChanged = function(cell, geometry)
+{
+ var previous = this.getGeometry(cell);
+ cell.setGeometry(geometry);
+
+ return previous;
+};
+
+/**
+ * Function: getStyle
+ *
+ * Returns the style of the given <mxCell>.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose style should be returned.
+ */
+mxGraphModel.prototype.getStyle = function(cell)
+{
+ return (cell != null) ? cell.getStyle() : null;
+};
+
+/**
+ * Function: setStyle
+ *
+ * Sets the style of the given <mxCell> using <mxStyleChange> and
+ * adds the change to the current transaction.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose style should be changed.
+ * style - String of the form [stylename;|key=value;] to specify
+ * the new cell style.
+ */
+mxGraphModel.prototype.setStyle = function(cell, style)
+{
+ if (style != this.getStyle(cell))
+ {
+ this.execute(new mxStyleChange(this, cell, style));
+ }
+
+ return style;
+};
+
+/**
+ * Function: styleForCellChanged
+ *
+ * Inner callback to update the style of the given <mxCell>
+ * using <mxCell.setStyle> and return the previous style.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that specifies the cell to be updated.
+ * style - String of the form [stylename;|key=value;] to specify
+ * the new cell style.
+ */
+mxGraphModel.prototype.styleForCellChanged = function(cell, style)
+{
+ var previous = this.getStyle(cell);
+ cell.setStyle(style);
+
+ return previous;
+};
+
+/**
+ * Function: isCollapsed
+ *
+ * Returns true if the given <mxCell> is collapsed.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose collapsed state should be returned.
+ */
+mxGraphModel.prototype.isCollapsed = function(cell)
+{
+ return (cell != null) ? cell.isCollapsed() : false;
+};
+
+/**
+ * Function: setCollapsed
+ *
+ * Sets the collapsed state of the given <mxCell> using <mxCollapseChange>
+ * and adds the change to the current transaction.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose collapsed state should be changed.
+ * collapsed - Boolean that specifies the new collpased state.
+ */
+mxGraphModel.prototype.setCollapsed = function(cell, collapsed)
+{
+ if (collapsed != this.isCollapsed(cell))
+ {
+ this.execute(new mxCollapseChange(this, cell, collapsed));
+ }
+
+ return collapsed;
+};
+
+/**
+ * Function: collapsedStateForCellChanged
+ *
+ * Inner callback to update the collapsed state of the
+ * given <mxCell> using <mxCell.setCollapsed> and return
+ * the previous collapsed state.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that specifies the cell to be updated.
+ * collapsed - Boolean that specifies the new collpased state.
+ */
+mxGraphModel.prototype.collapsedStateForCellChanged = function(cell, collapsed)
+{
+ var previous = this.isCollapsed(cell);
+ cell.setCollapsed(collapsed);
+
+ return previous;
+};
+
+/**
+ * Function: isVisible
+ *
+ * Returns true if the given <mxCell> is visible.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose visible state should be returned.
+ */
+mxGraphModel.prototype.isVisible = function(cell)
+{
+ return (cell != null) ? cell.isVisible() : false;
+};
+
+/**
+ * Function: setVisible
+ *
+ * Sets the visible state of the given <mxCell> using <mxVisibleChange> and
+ * adds the change to the current transaction.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> whose visible state should be changed.
+ * visible - Boolean that specifies the new visible state.
+ */
+mxGraphModel.prototype.setVisible = function(cell, visible)
+{
+ if (visible != this.isVisible(cell))
+ {
+ this.execute(new mxVisibleChange(this, cell, visible));
+ }
+
+ return visible;
+};
+
+/**
+ * Function: visibleStateForCellChanged
+ *
+ * Inner callback to update the visible state of the
+ * given <mxCell> using <mxCell.setCollapsed> and return
+ * the previous visible state.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> that specifies the cell to be updated.
+ * visible - Boolean that specifies the new visible state.
+ */
+mxGraphModel.prototype.visibleStateForCellChanged = function(cell, visible)
+{
+ var previous = this.isVisible(cell);
+ cell.setVisible(visible);
+
+ return previous;
+};
+
+/**
+ * Function: execute
+ *
+ * Executes the given edit and fires events if required. The edit object
+ * requires an execute function which is invoked. The edit is added to the
+ * <currentEdit> between <beginUpdate> and <endUpdate> calls, so that
+ * events will be fired if this execute is an individual transaction, that
+ * is, if no previous <beginUpdate> calls have been made without calling
+ * <endUpdate>. This implementation fires an <execute> event before
+ * executing the given change.
+ *
+ * Parameters:
+ *
+ * change - Object that described the change.
+ */
+mxGraphModel.prototype.execute = function(change)
+{
+ change.execute();
+ this.beginUpdate();
+ this.currentEdit.add(change);
+ this.fireEvent(new mxEventObject(mxEvent.EXECUTE, 'change', change));
+ this.endUpdate();
+};
+
+/**
+ * Function: beginUpdate
+ *
+ * Increments the <updateLevel> by one. The event notification
+ * is queued until <updateLevel> reaches 0 by use of
+ * <endUpdate>.
+ *
+ * All changes on <mxGraphModel> are transactional,
+ * that is, they are executed in a single undoable change
+ * on the model (without transaction isolation).
+ * Therefore, if you want to combine any
+ * number of changes into a single undoable change,
+ * you should group any two or more API calls that
+ * modify the graph model between <beginUpdate>
+ * and <endUpdate> calls as shown here:
+ *
+ * (code)
+ * var model = graph.getModel();
+ * var parent = graph.getDefaultParent();
+ * var index = model.getChildCount(parent);
+ * model.beginUpdate();
+ * try
+ * {
+ * model.add(parent, v1, index);
+ * model.add(parent, v2, index+1);
+ * }
+ * finally
+ * {
+ * model.endUpdate();
+ * }
+ * (end)
+ *
+ * Of course there is a shortcut for appending a
+ * sequence of cells into the default parent:
+ *
+ * (code)
+ * graph.addCells([v1, v2]).
+ * (end)
+ */
+mxGraphModel.prototype.beginUpdate = function()
+{
+ this.updateLevel++;
+ this.fireEvent(new mxEventObject(mxEvent.BEGIN_UPDATE));
+};
+
+/**
+ * Function: endUpdate
+ *
+ * Decrements the <updateLevel> by one and fires an <undo>
+ * event if the <updateLevel> reaches 0. This function
+ * indirectly fires a <change> event by invoking the notify
+ * function on the <currentEdit> und then creates a new
+ * <currentEdit> using <createUndoableEdit>.
+ *
+ * The <undo> event is fired only once per edit, whereas
+ * the <change> event is fired whenever the notify
+ * function is invoked, that is, on undo and redo of
+ * the edit.
+ */
+mxGraphModel.prototype.endUpdate = function()
+{
+ this.updateLevel--;
+
+ if (!this.endingUpdate)
+ {
+ this.endingUpdate = this.updateLevel == 0;
+ this.fireEvent(new mxEventObject(mxEvent.END_UPDATE, 'edit', this.currentEdit));
+
+ try
+ {
+ if (this.endingUpdate && !this.currentEdit.isEmpty())
+ {
+ this.fireEvent(new mxEventObject(mxEvent.BEFORE_UNDO, 'edit', this.currentEdit));
+ var tmp = this.currentEdit;
+ this.currentEdit = this.createUndoableEdit();
+ tmp.notify();
+ this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', tmp));
+ }
+ }
+ finally
+ {
+ this.endingUpdate = false;
+ }
+ }
+};
+
+/**
+ * Function: createUndoableEdit
+ *
+ * Creates a new <mxUndoableEdit> that implements the
+ * notify function to fire a <change> and <notify> event
+ * through the <mxUndoableEdit>'s source.
+ */
+mxGraphModel.prototype.createUndoableEdit = function()
+{
+ var edit = new mxUndoableEdit(this, true);
+
+ edit.notify = function()
+ {
+ // LATER: Remove changes property (deprecated)
+ edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
+ 'edit', edit, 'changes', edit.changes));
+ edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
+ 'edit', edit, 'changes', edit.changes));
+ };
+
+ return edit;
+};
+
+/**
+ * Function: mergeChildren
+ *
+ * Merges the children of the given cell into the given target cell inside
+ * this model. All cells are cloned unless there is a corresponding cell in
+ * the model with the same id, in which case the source cell is ignored and
+ * all edges are connected to the corresponding cell in this model. Edges
+ * are considered to have no identity and are always cloned unless the
+ * cloneAllEdges flag is set to false, in which case edges with the same
+ * id in the target model are reconnected to reflect the terminals of the
+ * source edges.
+ */
+mxGraphModel.prototype.mergeChildren = function(from, to, cloneAllEdges)
+{
+ cloneAllEdges = (cloneAllEdges != null) ? cloneAllEdges : true;
+
+ this.beginUpdate();
+ try
+ {
+ var mapping = new Object();
+ this.mergeChildrenImpl(from, to, cloneAllEdges, mapping);
+
+ // Post-processes all edges in the mapping and
+ // reconnects the terminals to the corresponding
+ // cells in the target model
+ for (var key in mapping)
+ {
+ var cell = mapping[key];
+ var terminal = this.getTerminal(cell, true);
+
+ if (terminal != null)
+ {
+ terminal = mapping[mxCellPath.create(terminal)];
+ this.setTerminal(cell, terminal, true);
+ }
+
+ terminal = this.getTerminal(cell, false);
+
+ if (terminal != null)
+ {
+ terminal = mapping[mxCellPath.create(terminal)];
+ this.setTerminal(cell, terminal, false);
+ }
+ }
+ }
+ finally
+ {
+ this.endUpdate();
+ }
+};
+
+/**
+ * Function: mergeChildren
+ *
+ * Clones the children of the source cell into the given target cell in
+ * this model and adds an entry to the mapping that maps from the source
+ * cell to the target cell with the same id or the clone of the source cell
+ * that was inserted into this model.
+ */
+mxGraphModel.prototype.mergeChildrenImpl = function(from, to, cloneAllEdges, mapping)
+{
+ this.beginUpdate();
+ try
+ {
+ var childCount = from.getChildCount();
+
+ for (var i = 0; i < childCount; i++)
+ {
+ var cell = from.getChildAt(i);
+
+ if (typeof(cell.getId) == 'function')
+ {
+ var id = cell.getId();
+ var target = (id != null && (!this.isEdge(cell) || !cloneAllEdges)) ?
+ this.getCell(id) : null;
+
+ // Clones and adds the child if no cell exists for the id
+ if (target == null)
+ {
+ var clone = cell.clone();
+ clone.setId(id);
+
+ // Sets the terminals from the original cell to the clone
+ // because the lookup uses strings not cells in JS
+ clone.setTerminal(cell.getTerminal(true), true);
+ clone.setTerminal(cell.getTerminal(false), false);
+
+ // Do *NOT* use model.add as this will move the edge away
+ // from the parent in updateEdgeParent if maintainEdgeParent
+ // is enabled in the target model
+ target = to.insert(clone);
+ this.cellAdded(target);
+ }
+
+ // Stores the mapping for later reconnecting edges
+ mapping[mxCellPath.create(cell)] = target;
+
+ // Recurses
+ this.mergeChildrenImpl(cell, target, cloneAllEdges, mapping);
+ }
+ }
+ }
+ finally
+ {
+ this.endUpdate();
+ }
+};
+
+/**
+ * Function: getParents
+ *
+ * Returns an array that represents the set (no duplicates) of all parents
+ * for the given array of cells.
+ *
+ * Parameters:
+ *
+ * cells - Array of cells whose parents should be returned.
+ */
+mxGraphModel.prototype.getParents = function(cells)
+{
+ var parents = [];
+
+ if (cells != null)
+ {
+ var hash = new Object();
+
+ for (var i = 0; i < cells.length; i++)
+ {
+ var parent = this.getParent(cells[i]);
+
+ if (parent != null)
+ {
+ var id = mxCellPath.create(parent);
+
+ if (hash[id] == null)
+ {
+ hash[id] = parent;
+ parents.push(parent);
+ }
+ }
+ }
+ }
+
+ return parents;
+};
+
+//
+// Cell Cloning
+//
+
+/**
+ * Function: cloneCell
+ *
+ * Returns a deep clone of the given <mxCell> (including
+ * the children) which is created using <cloneCells>.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> to be cloned.
+ */
+mxGraphModel.prototype.cloneCell = function(cell)
+{
+ if (cell != null)
+ {
+ return this.cloneCells([cell], true)[0];
+ }
+
+ return null;
+};
+
+/**
+ * Function: cloneCells
+ *
+ * Returns an array of clones for the given array of <mxCells>.
+ * Depending on the value of includeChildren, a deep clone is created for
+ * each cell. Connections are restored based if the corresponding
+ * cell is contained in the passed in array.
+ *
+ * Parameters:
+ *
+ * cells - Array of <mxCell> to be cloned.
+ * includeChildren - Boolean indicating if the cells should be cloned
+ * with all descendants.
+ */
+mxGraphModel.prototype.cloneCells = function(cells, includeChildren)
+{
+ var mapping = new Object();
+ var clones = [];
+
+ for (var i = 0; i < cells.length; i++)
+ {
+ if (cells[i] != null)
+ {
+ clones.push(this.cloneCellImpl(cells[i], mapping, includeChildren));
+ }
+ else
+ {
+ clones.push(null);
+ }
+ }
+
+ for (var i = 0; i < clones.length; i++)
+ {
+ if (clones[i] != null)
+ {
+ this.restoreClone(clones[i], cells[i], mapping);
+ }
+ }
+
+ return clones;
+};
+
+/**
+ * Function: cloneCellImpl
+ *
+ * Inner helper method for cloning cells recursively.
+ */
+mxGraphModel.prototype.cloneCellImpl = function(cell, mapping, includeChildren)
+{
+ var clone = this.cellCloned(cell);
+
+ // Stores the clone in the lookup under the
+ // cell path for the original cell
+ mapping[mxObjectIdentity.get(cell)] = clone;
+
+ if (includeChildren)
+ {
+ var childCount = this.getChildCount(cell);
+
+ for (var i = 0; i < childCount; i++)
+ {
+ var cloneChild = this.cloneCellImpl(
+ this.getChildAt(cell, i), mapping, true);
+ clone.insert(cloneChild);
+ }
+ }
+
+ return clone;
+};
+
+/**
+ * Function: cellCloned
+ *
+ * Hook for cloning the cell. This returns cell.clone() or
+ * any possible exceptions.
+ */
+mxGraphModel.prototype.cellCloned = function(cell)
+{
+ return cell.clone();
+};
+
+/**
+ * Function: restoreClone
+ *
+ * Inner helper method for restoring the connections in
+ * a network of cloned cells.
+ */
+mxGraphModel.prototype.restoreClone = function(clone, cell, mapping)
+{
+ var source = this.getTerminal(cell, true);
+
+ if (source != null)
+ {
+ var tmp = mapping[mxObjectIdentity.get(source)];
+
+ if (tmp != null)
+ {
+ tmp.insertEdge(clone, true);
+ }
+ }
+
+ var target = this.getTerminal(cell, false);
+
+ if (target != null)
+ {
+ var tmp = mapping[mxObjectIdentity.get(target)];
+
+ if (tmp != null)
+ {
+ tmp.insertEdge(clone, false);
+ }
+ }
+
+ var childCount = this.getChildCount(clone);
+
+ for (var i = 0; i < childCount; i++)
+ {
+ this.restoreClone(this.getChildAt(clone, i),
+ this.getChildAt(cell, i), mapping);
+ }
+};
+
+//
+// Atomic changes
+//
+
+/**
+ * Class: mxRootChange
+ *
+ * Action to change the root in a model.
+ *
+ * Constructor: mxRootChange
+ *
+ * Constructs a change of the root in the
+ * specified model.
+ */
+function mxRootChange(model, root)
+{
+ this.model = model;
+ this.root = root;
+ this.previous = root;
+};
+
+/**
+ * Function: execute
+ *
+ * Carries out a change of the root using
+ * <mxGraphModel.rootChanged>.
+ */
+mxRootChange.prototype.execute = function()
+{
+ this.root = this.previous;
+ this.previous = this.model.rootChanged(this.previous);
+};
+
+/**
+ * Class: mxChildChange
+ *
+ * Action to add or remove a child in a model.
+ *
+ * Constructor: mxChildChange
+ *
+ * Constructs a change of a child in the
+ * specified model.
+ */
+function mxChildChange(model, parent, child, index)
+{
+ this.model = model;
+ this.parent = parent;
+ this.previous = parent;
+ this.child = child;
+ this.index = index;
+ this.previousIndex = index;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the parent of <child> using
+ * <mxGraphModel.parentForCellChanged> and
+ * removes or restores the cell's
+ * connections.
+ */
+mxChildChange.prototype.execute = function()
+{
+ var tmp = this.model.getParent(this.child);
+ var tmp2 = (tmp != null) ? tmp.getIndex(this.child) : 0;
+
+ if (this.previous == null)
+ {
+ this.connect(this.child, false);
+ }
+
+ tmp = this.model.parentForCellChanged(
+ this.child, this.previous, this.previousIndex);
+
+ if (this.previous != null)
+ {
+ this.connect(this.child, true);
+ }
+
+ this.parent = this.previous;
+ this.previous = tmp;
+ this.index = this.previousIndex;
+ this.previousIndex = tmp2;
+};
+
+/**
+ * Function: disconnect
+ *
+ * Disconnects the given cell recursively from its
+ * terminals and stores the previous terminal in the
+ * cell's terminals.
+ */
+mxChildChange.prototype.connect = function(cell, isConnect)
+{
+ isConnect = (isConnect != null) ? isConnect : true;
+
+ var source = cell.getTerminal(true);
+ var target = cell.getTerminal(false);
+
+ if (source != null)
+ {
+ if (isConnect)
+ {
+ this.model.terminalForCellChanged(cell, source, true);
+ }
+ else
+ {
+ this.model.terminalForCellChanged(cell, null, true);
+ }
+ }
+
+ if (target != null)
+ {
+ if (isConnect)
+ {
+ this.model.terminalForCellChanged(cell, target, false);
+ }
+ else
+ {
+ this.model.terminalForCellChanged(cell, null, false);
+ }
+ }
+
+ cell.setTerminal(source, true);
+ cell.setTerminal(target, false);
+
+ var childCount = this.model.getChildCount(cell);
+
+ for (var i=0; i<childCount; i++)
+ {
+ this.connect(this.model.getChildAt(cell, i), isConnect);
+ }
+};
+
+/**
+ * Class: mxTerminalChange
+ *
+ * Action to change a terminal in a model.
+ *
+ * Constructor: mxTerminalChange
+ *
+ * Constructs a change of a terminal in the
+ * specified model.
+ */
+function mxTerminalChange(model, cell, terminal, source)
+{
+ this.model = model;
+ this.cell = cell;
+ this.terminal = terminal;
+ this.previous = terminal;
+ this.source = source;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the terminal of <cell> to <previous> using
+ * <mxGraphModel.terminalForCellChanged>.
+ */
+mxTerminalChange.prototype.execute = function()
+{
+ this.terminal = this.previous;
+ this.previous = this.model.terminalForCellChanged(
+ this.cell, this.previous, this.source);
+};
+
+/**
+ * Class: mxValueChange
+ *
+ * Action to change a user object in a model.
+ *
+ * Constructor: mxValueChange
+ *
+ * Constructs a change of a user object in the
+ * specified model.
+ */
+function mxValueChange(model, cell, value)
+{
+ this.model = model;
+ this.cell = cell;
+ this.value = value;
+ this.previous = value;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the value of <cell> to <previous> using
+ * <mxGraphModel.valueForCellChanged>.
+ */
+mxValueChange.prototype.execute = function()
+{
+ this.value = this.previous;
+ this.previous = this.model.valueForCellChanged(
+ this.cell, this.previous);
+};
+
+/**
+ * Class: mxStyleChange
+ *
+ * Action to change a cell's style in a model.
+ *
+ * Constructor: mxStyleChange
+ *
+ * Constructs a change of a style in the
+ * specified model.
+ */
+function mxStyleChange(model, cell, style)
+{
+ this.model = model;
+ this.cell = cell;
+ this.style = style;
+ this.previous = style;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the style of <cell> to <previous> using
+ * <mxGraphModel.styleForCellChanged>.
+ */
+mxStyleChange.prototype.execute = function()
+{
+ this.style = this.previous;
+ this.previous = this.model.styleForCellChanged(
+ this.cell, this.previous);
+};
+
+/**
+ * Class: mxGeometryChange
+ *
+ * Action to change a cell's geometry in a model.
+ *
+ * Constructor: mxGeometryChange
+ *
+ * Constructs a change of a geometry in the
+ * specified model.
+ */
+function mxGeometryChange(model, cell, geometry)
+{
+ this.model = model;
+ this.cell = cell;
+ this.geometry = geometry;
+ this.previous = geometry;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the geometry of <cell> ro <previous> using
+ * <mxGraphModel.geometryForCellChanged>.
+ */
+mxGeometryChange.prototype.execute = function()
+{
+ this.geometry = this.previous;
+ this.previous = this.model.geometryForCellChanged(
+ this.cell, this.previous);
+};
+
+/**
+ * Class: mxCollapseChange
+ *
+ * Action to change a cell's collapsed state in a model.
+ *
+ * Constructor: mxCollapseChange
+ *
+ * Constructs a change of a collapsed state in the
+ * specified model.
+ */
+function mxCollapseChange(model, cell, collapsed)
+{
+ this.model = model;
+ this.cell = cell;
+ this.collapsed = collapsed;
+ this.previous = collapsed;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the collapsed state of <cell> to <previous> using
+ * <mxGraphModel.collapsedStateForCellChanged>.
+ */
+mxCollapseChange.prototype.execute = function()
+{
+ this.collapsed = this.previous;
+ this.previous = this.model.collapsedStateForCellChanged(
+ this.cell, this.previous);
+};
+
+/**
+ * Class: mxVisibleChange
+ *
+ * Action to change a cell's visible state in a model.
+ *
+ * Constructor: mxVisibleChange
+ *
+ * Constructs a change of a visible state in the
+ * specified model.
+ */
+function mxVisibleChange(model, cell, visible)
+{
+ this.model = model;
+ this.cell = cell;
+ this.visible = visible;
+ this.previous = visible;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the visible state of <cell> to <previous> using
+ * <mxGraphModel.visibleStateForCellChanged>.
+ */
+mxVisibleChange.prototype.execute = function()
+{
+ this.visible = this.previous;
+ this.previous = this.model.visibleStateForCellChanged(
+ this.cell, this.previous);
+};
+
+/**
+ * Class: mxCellAttributeChange
+ *
+ * Action to change the attribute of a cell's user object.
+ * There is no method on the graph model that uses this
+ * action. To use the action, you can use the code shown
+ * in the example below.
+ *
+ * Example:
+ *
+ * To change the attributeName in the cell's user object
+ * to attributeValue, use the following code:
+ *
+ * (code)
+ * model.beginUpdate();
+ * try
+ * {
+ * var edit = new mxCellAttributeChange(
+ * cell, attributeName, attributeValue);
+ * model.execute(edit);
+ * }
+ * finally
+ * {
+ * model.endUpdate();
+ * }
+ * (end)
+ *
+ * Constructor: mxCellAttributeChange
+ *
+ * Constructs a change of a attribute of the DOM node
+ * stored as the value of the given <mxCell>.
+ */
+function mxCellAttributeChange(cell, attribute, value)
+{
+ this.cell = cell;
+ this.attribute = attribute;
+ this.value = value;
+ this.previous = value;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the attribute of the cell's user object by
+ * using <mxCell.setAttribute>.
+ */
+mxCellAttributeChange.prototype.execute = function()
+{
+ var tmp = this.cell.getAttribute(this.attribute);
+
+ if (this.previous == null)
+ {
+ this.cell.value.removeAttribute(this.attribute);
+ }
+ else
+ {
+ this.cell.setAttribute(this.attribute, this.previous);
+ }
+
+ this.previous = tmp;
+};