4203daed2a
Former-commit-id: b51ac7209d
367 lines
10 KiB
JavaScript
367 lines
10 KiB
JavaScript
/**
|
|
* Explore plugin.
|
|
*/
|
|
Draw.loadPlugin(function(ui)
|
|
{
|
|
// Adds resource for action
|
|
mxResources.parse('exploreFromHere=Explore from here...');
|
|
|
|
// Max number of edges per page
|
|
var pageSize = 20;
|
|
|
|
var uiCreatePopupMenu = ui.menus.createPopupMenu;
|
|
ui.menus.createPopupMenu = function(menu, cell, evt)
|
|
{
|
|
uiCreatePopupMenu.apply(this, arguments);
|
|
|
|
var graph = ui.editor.graph;
|
|
|
|
if (graph.model.isVertex(graph.getSelectionCell()))
|
|
{
|
|
this.addMenuItems(menu, ['-', 'exploreFromHere'], null, evt);
|
|
}
|
|
};
|
|
|
|
//
|
|
// Main function
|
|
//
|
|
function exploreFromHere(selectionCell)
|
|
{
|
|
var sourceGraph = ui.editor.graph;
|
|
|
|
var container = document.createElement('div');
|
|
container.style.position = 'absolute';
|
|
container.style.display = 'block';
|
|
container.style.background = '#ffffff';
|
|
container.style.width = '100%';
|
|
container.style.height = '100%';
|
|
container.style.left = '0px';
|
|
container.style.top = '0px';
|
|
container.style.zIndex = 2;
|
|
|
|
var deleteImage = document.createElement('img');
|
|
deleteImage.setAttribute('src', IMAGE_PATH + '/delete.png');
|
|
deleteImage.style.position = 'absolute';
|
|
deleteImage.style.cursor = 'pointer';
|
|
deleteImage.style.right = '10px';
|
|
deleteImage.style.top = '10px';
|
|
container.appendChild(deleteImage);
|
|
|
|
var closeLabel = document.createElement('div');
|
|
closeLabel.style.position = 'absolute';
|
|
closeLabel.style.cursor = 'pointer';
|
|
closeLabel.style.right = '38px';
|
|
closeLabel.style.top = '14px';
|
|
closeLabel.style.textAlign = 'right';
|
|
closeLabel.style.verticalAlign = 'top';
|
|
mxUtils.write(closeLabel, mxResources.get('close'));
|
|
container.appendChild(closeLabel);
|
|
document.body.appendChild(container);
|
|
|
|
var keyHandler = function(evt)
|
|
{
|
|
if (evt.keyCode == 27)
|
|
{
|
|
deleteImage.click();
|
|
}
|
|
};
|
|
|
|
mxEvent.addListener(document, 'keydown', keyHandler);
|
|
|
|
// Global variable to make sure each cell in a response has
|
|
// a unique ID throughout the complete life of the program,
|
|
// in a real-life setup each cell should have an external
|
|
// ID on the business object or else the cell ID should be
|
|
// globally unique for the lifetime of the graph model.
|
|
var requestId = 0;
|
|
|
|
function main(container)
|
|
{
|
|
// Checks if browser is supported
|
|
if (!mxClient.isBrowserSupported())
|
|
{
|
|
// Displays an error message if the browser is
|
|
// not supported.
|
|
mxUtils.error('Browser is not supported!', 200, false);
|
|
}
|
|
else
|
|
{
|
|
// Creates the graph inside the given container
|
|
var graph = new Graph(container);
|
|
graph.keepEdgesInBackground = true;
|
|
graph.isCellResizable = function()
|
|
{
|
|
return false;
|
|
};
|
|
// Workaround to hide custom handles
|
|
graph.isCellRotatable = function()
|
|
{
|
|
return false;
|
|
};
|
|
|
|
// Shows hand cursor for all vertices
|
|
graph.getCursorForCell = function(cell)
|
|
{
|
|
if (this.model.isVertex(cell))
|
|
{
|
|
return 'pointer';
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
graph.getFoldingImage = function()
|
|
{
|
|
return null;
|
|
};
|
|
|
|
var closeHandler = function()
|
|
{
|
|
mxEvent.removeListener(document, 'keydown', keyHandler);
|
|
container.parentNode.removeChild(container);
|
|
|
|
// FIXME: Does not work
|
|
sourceGraph.scrollCellToVisible(selectionCell);
|
|
};
|
|
|
|
mxEvent.addListener(deleteImage, 'click', closeHandler);
|
|
mxEvent.addListener(closeLabel, 'click', closeHandler);
|
|
|
|
// Disables all built-in interactions
|
|
graph.setEnabled(false);
|
|
|
|
// Handles clicks on cells
|
|
graph.click = function(me)
|
|
{
|
|
var evt = me.getEvent();
|
|
var cell = me.getCell();
|
|
|
|
if (cell != null && cell != graph.rootCell)
|
|
{
|
|
load(graph, cell);
|
|
}
|
|
};
|
|
|
|
// Gets the default parent for inserting new cells. This
|
|
// is normally the first child of the root (ie. layer 0).
|
|
var parent = graph.getDefaultParent();
|
|
|
|
var cx = graph.container.scrollWidth / 2;
|
|
var cy = graph.container.scrollHeight / 3;
|
|
|
|
graph.model.beginUpdate();
|
|
var cell = graph.importCells([selectionCell])[0];
|
|
cell.sourceCellId = selectionCell.id;
|
|
cell.geometry.x = cx - cell.geometry.width / 2;
|
|
cell.geometry.y = cy - cell.geometry.height / 2;
|
|
graph.model.endUpdate();
|
|
|
|
// Animates the changes in the graph model
|
|
graph.getModel().addListener(mxEvent.CHANGE, function(sender, evt)
|
|
{
|
|
var changes = evt.getProperty('edit').changes;
|
|
var prev = mxText.prototype.enableBoundingBox;
|
|
mxText.prototype.enableBoundingBox = false;
|
|
graph.labelsVisible = false;
|
|
|
|
mxEffects.animateChanges(graph, changes, function()
|
|
{
|
|
mxText.prototype.prev = true;
|
|
graph.labelsVisible = true;
|
|
graph.refresh();
|
|
graph.tooltipHandler.hide();
|
|
});
|
|
});
|
|
|
|
load(graph, cell);
|
|
}
|
|
};
|
|
|
|
// Loads the links for the given cell into the given graph
|
|
// by requesting the respective data in the server-side
|
|
// (implemented for this demo using the server-function)
|
|
function load(graph, cell)
|
|
{
|
|
if (graph.getModel().isVertex(cell))
|
|
{
|
|
var cx = graph.container.scrollWidth / 2;
|
|
var cy = graph.container.scrollHeight / 3;
|
|
|
|
// Gets the default parent for inserting new cells. This
|
|
// is normally the first child of the root (ie. layer 0).
|
|
var parent = graph.getDefaultParent();
|
|
graph.rootCell = cell.referenceCell || cell;
|
|
|
|
// Adds cells to the model in a single step
|
|
graph.getModel().beginUpdate();
|
|
try
|
|
{
|
|
var cells = rootChanged(graph, cell);
|
|
|
|
// Removes all cells except the new root
|
|
for (var key in graph.getModel().cells)
|
|
{
|
|
var tmp = graph.getModel().getCell(key);
|
|
|
|
if (tmp != graph.rootCell && graph.getModel().isVertex(tmp))
|
|
{
|
|
graph.removeCells([tmp]);
|
|
}
|
|
}
|
|
|
|
// Merges the response model with the client model
|
|
//graph.getModel().mergeChildren(model.getRoot().getChildAt(0), parent);
|
|
graph.addCells(cells);
|
|
|
|
// Moves the given cell to the center
|
|
var geo = graph.getModel().getGeometry(graph.rootCell);
|
|
|
|
if (geo != null)
|
|
{
|
|
geo = geo.clone();
|
|
|
|
geo.x = cx - geo.width / 2;
|
|
geo.y = cy - geo.height / 3;
|
|
graph.getModel().setGeometry(graph.rootCell, geo);
|
|
}
|
|
|
|
// Creates a list of the new vertices, if there is more
|
|
// than the center vertex which might have existed
|
|
// previously, then this needs to be changed to analyze
|
|
// the target model before calling mergeChildren above
|
|
var vertices = [];
|
|
|
|
for (var key in graph.getModel().cells)
|
|
{
|
|
var tmp = graph.getModel().getCell(key);
|
|
|
|
if (tmp != graph.rootCell && graph.getModel().isVertex(tmp) &&
|
|
graph.getModel().getParent(tmp) == graph.getDefaultParent())
|
|
{
|
|
vertices.push(tmp);
|
|
|
|
// Changes the initial location "in-place"
|
|
// to get a nice animation effect from the
|
|
// center to the radius of the circle
|
|
var geo = graph.getModel().getGeometry(tmp);
|
|
|
|
if (geo != null)
|
|
{
|
|
geo.x = cx - geo.width / 2;
|
|
geo.y = cy - geo.height / 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Arranges the response in a circle
|
|
var cellCount = vertices.length;
|
|
var phi = 2 * Math.PI / cellCount;
|
|
var r = Math.min(graph.container.scrollWidth / 3 - 80,
|
|
graph.container.scrollHeight / 3 - 80);
|
|
|
|
for (var i = 0; i < cellCount; i++)
|
|
{
|
|
var geo = graph.getModel().getGeometry(vertices[i]);
|
|
|
|
if (geo != null)
|
|
{
|
|
geo = geo.clone();
|
|
geo.x += r * Math.sin(i * phi);
|
|
geo.y += r * Math.cos(i * phi);
|
|
|
|
graph.getModel().setGeometry(vertices[i], geo);
|
|
}
|
|
}
|
|
|
|
// Keeps parallel edges apart
|
|
var layout = new mxParallelEdgeLayout(graph);
|
|
layout.spacing = 60;
|
|
layout.execute(graph.getDefaultParent());
|
|
}
|
|
finally
|
|
{
|
|
// Updates the display
|
|
graph.getModel().endUpdate();
|
|
}
|
|
}
|
|
};
|
|
|
|
// Gets the edges from the source cell and adds the targets
|
|
function rootChanged(graph, cell)
|
|
{
|
|
// TODO: Keep existing cells, probably best via XML to redirect IDs
|
|
var realCell = cell.referenceCell || cell;
|
|
var sourceCell = sourceGraph.model.getCell(realCell.sourceCellId);
|
|
var edges = sourceGraph.getEdges(sourceCell, null, true, true, false, true);
|
|
var cells = edges;
|
|
|
|
// Paging by selecting a window in the edges array
|
|
if (cell.startIndex != null || (pageSize > 0 && edges.length > pageSize))
|
|
{
|
|
var start = cell.startIndex || 0;
|
|
|
|
cells = edges.slice(Math.max(0, start), Math.min(edges.length, start + pageSize));
|
|
}
|
|
|
|
cells = cells.concat(sourceGraph.getOpposites(cells, sourceCell));
|
|
var clones = graph.cloneCells(cells);
|
|
|
|
var edgeStyle = ';curved=1;noEdgeStyle=1;entryX=none;entryY=none;exitX=none;exitY=none;';
|
|
var btnStyle = 'fillColor=green;fontColor=white;strokeColor=green;';
|
|
|
|
for (var i = 0; i < cells.length; i++)
|
|
{
|
|
clones[i].sourceCellId = cells[i].id;
|
|
|
|
if (graph.model.isEdge(clones[i]))
|
|
{
|
|
// Removes waypoints, edge styles, constraints and centers the label
|
|
clones[i].geometry.x = 0;
|
|
clones[i].geometry.y = 0;
|
|
clones[i].geometry.points = null;
|
|
clones[i].setStyle(clones[i].getStyle() + edgeStyle);
|
|
clones[i].setTerminal(realCell, clones[i].getTerminal(true) == null);
|
|
}
|
|
}
|
|
|
|
if (cell.startIndex > 0)
|
|
{
|
|
var backCell = graph.createVertex(null, null, 'Back...', 0, 0, 80, 30, btnStyle);
|
|
backCell.referenceCell = realCell;
|
|
backCell.startIndex = Math.max(0, (cell.startIndex || 0) - pageSize);
|
|
clones.splice(0, 0, backCell);
|
|
}
|
|
|
|
if (edges.length > (cell.startIndex || 0) + pageSize)
|
|
{
|
|
var moreCell = graph.createVertex(null, null, 'More...', 0, 0, 80, 30, btnStyle);
|
|
moreCell.referenceCell = realCell;
|
|
moreCell.startIndex = (cell.startIndex || 0) + pageSize;
|
|
clones.splice(0, 0, moreCell);
|
|
}
|
|
|
|
return clones;
|
|
};
|
|
|
|
main(container);
|
|
};
|
|
|
|
// Adds action
|
|
ui.actions.addAction('exploreFromHere', function()
|
|
{
|
|
exploreFromHere(ui.editor.graph.getSelectionCell());
|
|
});
|
|
|
|
// Click handler for chromeless mode
|
|
if (ui.editor.chromeless)
|
|
{
|
|
ui.editor.graph.click = function(me)
|
|
{
|
|
if (ui.editor.graph.model.isVertex(me.getCell()) &&
|
|
ui.editor.graph.model.getEdgeCount(me.getCell()) > 0)
|
|
{
|
|
exploreFromHere(me.getCell());
|
|
}
|
|
};
|
|
}
|
|
});
|