Drag and drop graph
// http://js-graph-it.sf.net
// License: GNU Library or Lesser General Public License (LGPL)
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>js-graph.it homepage</title>
<script type="text/javascript">
/*********************
* browser detection *
*********************/
var ie=document.all;
var nn6=document.getElementById&&!document.all;
/*****************
* drag and drop *
*****************/
var isdrag=false;
var mouseStartX, mouseStartY; // mouse position when drag starts
var elementStartX, elementStartY; // element position when drag starts
var elementToMove;
var blockToMove;
// an array containing bounds to be respected while dragging elements,
// these bounds are left, top, left + width, top + height of the parent element.
var bounds = new Array(4);
function movemouse(e)
{
if (isdrag)
{
var currentMouseX = nn6 ? e.clientX : event.clientX;
var currentMouseY = nn6 ? e.clientY : event.clientY;
var newElementX = elementStartX + currentMouseX - mouseStartX;
var newElementY = elementStartY + currentMouseY - mouseStartY;
// check bounds
// note: the "-1" and "+1" is to avoid borders overlap
if(newElementX < bounds[0])
newElementX = bounds[0] + 1;
if(newElementX + elementToMove.offsetWidth > bounds[2])
newElementX = bounds[2] - elementToMove.offsetWidth - 1;
if(newElementY < bounds[1])
newElementY = bounds[1] + 1;
if(newElementY + elementToMove.offsetHeight > bounds[3])
newElementY = bounds[3] - elementToMove.offsetHeight - 1;
// move element
elementToMove.style.left = newElementX + 'px';
elementToMove.style.top = newElementY + 'px';
// elementToMove.style.left = newElementX / elementToMove.parentNode.offsetWidth * 100 + '%';
// elementToMove.style.top = newElementY / elementToMove.parentNode.offsetHeight * 100 + '%';
elementToMove.style.right = null;
elementToMove.style.bottom = null;
if(blockToMove)
blockToMove.onMove();
return false;
}
}
/**
* finds the innermost draggable element starting from the one that generated the event "e"
* (i.e.: the html element under mouse pointer), then setup the document's onmousemove function to
* move the element around.
*/
function selectmouse(e)
{
var eventSource = nn6 ? e.target : event.srcElement;
while (eventSource != document.body && !hasClass(eventSource, "draggable"))
{
eventSource = nn6 ? eventSource.parentNode : eventSource.parentElement;
}
// if a draggable element was found, calculate its actual position
if (hasClass(eventSource, "draggable"))
{
isdrag = true;
elementToMove = eventSource;
// calculate start point
//elementStartX = calculateOffsetLeft(elementToMove);
//elementStartY = calculateOffsetTop(elementToMove);
elementStartX = elementToMove.offsetLeft;
elementStartY = elementToMove.offsetTop;
// calculate mouse start point
mouseStartX = nn6 ? e.clientX : event.clientX;
mouseStartY = nn6 ? e.clientY : event.clientY;
// calculate bounds as left, top, width, height of the parent element
if(elementToMove.parentNode.style.position == 'absolute')
{
bounds[0] = 0;
bounds[1] = 0;
}
else
{
bounds[0] = calculateOffsetLeft(elementToMove.parentNode);
bounds[1] = calculateOffsetTop(elementToMove.parentNode);
}
bounds[2] = bounds[0] + elementToMove.parentNode.offsetWidth;
bounds[3] = bounds[1] + elementToMove.parentNode.offsetHeight;
// either find the block related to the dragging element to call its onMove method
blockToMove = findBlock(eventSource.id);
document.onmousemove = movemouse;
return false;
}
}
document.onmousedown=selectmouse;
document.onmouseup=new Function("isdrag=false");
/*************
* Constants *
*************/
var AUTO = 0;
var HORIZONTAL = 1;
var VERTICAL = 2;
/**************
* Inspectors *
**************/
var inspectors = new Array();
/**
* The canvas class.
* This class is built on a div html element.
*/
function Canvas(htmlElement)
{
/*
* initialization
*/
this.id = htmlElement.id;
this.htmlElement = htmlElement;
this.blocks = new Array();
this.connectors = new Array();
this.initCanvas = function()
{
// inspect canvas children to identify first level blocks
this.findNestedBlocksAndConnectors(this.htmlElement);
// init connectors
var i;
for(i = 0; i < this.connectors.length; i++)
{
this.connectors[i].initConnector();
}
}
this.findNestedBlocksAndConnectors = function(node)
{
var children = node.childNodes;
var i;
var offsetLeft = calculateOffsetLeft(this.htmlElement);
var offsetTop = calculateOffsetTop(this.htmlElement);
for(i = 0; i < children.length; i++)
{
// move element in a "correct relative" position and set it size as fixed
if(getStyle(children[i], "position") == 'absolute')
{
children[i].style.left = children[i].offsetLeft + offsetLeft + "px";
children[i].style.top = children[i].offsetTop + offsetTop + "px";
children[i].style.width = children[i].offsetWidth;
children[i].style.height = children[i].offsetHeight;
}
if(isBlock(children[i]))
{
// block found initialize it
var newBlock = new Block(children[i], this);
newBlock.initBlock();
this.blocks.push(newBlock);
}
else if(isConnector(children[i]))
{
// connector found, just create it, source or destination blocks may not
// have been initialized yet
var newConnector = new Connector(children[i], this);
this.connectors.push(newConnector);
}
else
{
// continue searching nested elements
this.findNestedBlocksAndConnectors(children[i]);
}
}
}
/*
* methods
*/
this.print = function()
{
var output = '<ul><legend>canvas: ' + this.id + '</legend>';
var i;
for(i = 0; i < this.blocks.length; i++)
{
output += '<li>';
output += this.blocks[i].print();
output += '</li>';
}
output += '</ul>';
return output;
}
/*
* This function searches for a nested block with a given id
*/
this.findBlock = function(blockId)
{
var result;
var i;
for(i = 0; i < this.blocks.length && !result; i++)
{
result = this.blocks[i].findBlock(blockId);
}
return result;
}
this.toString = function()
{
return 'canvas: ' + this.id;
}
}
/*
* Block class
*/
function Block(htmlElement, canvas)
{
/*
* initialization
*/
this.canvas = canvas;
this.htmlElement = htmlElement;
this.id = htmlElement.id;
this.blocks = new Array();
this.moveListeners = new Array();
this.initBlock = function()
{
// inspect block children to identify nested blocks
var children = this.htmlElement.childNodes;
var i;
for(i = 0; i < children.length; i++)
{
if(isBlock(children[i]))
{
var innerBlock = new Block(children[i], this.canvas);
innerBlock.initBlock();
this.blocks.push(innerBlock);
this.moveListeners.push(innerBlock);
}
}
//this.htmlElement.onmousemove = new Function('if(isdrag) findBlock(\'' + this.id + '\').onMove();');
}
this.top = function()
{
return calculateOffsetTop(this.htmlElement);
}
this.left = function()
{
return calculateOffsetLeft(this.htmlElement);
}
this.width = function()
{
return this.htmlElement.offsetWidth;
}
this.height = function()
{
return this.htmlElement.offsetHeight;
}
/*
* methods
*/
this.print = function()
{
var output = 'block: ' + this.id;
if(this.blocks.length > 0)
{
output += '<ul>';
var i;
for(i = 0; i < this.blocks.length; i++)
{
output += '<li>';
output += this.blocks[i].print();
output += '</li>';
}
output += '</ul>';
}
return output;
}
/*
* This function searches for a nested block (or the block itself) with a given id
*/
this.findBlock = function(blockId)
{
if(this.id == blockId)
return this;
var result;
var i;
for(i = 0; i < this.blocks.length && !result; i++)
{
result = this.blocks[i].findBlock(blockId);
}
return result;
}
this.move = function(left, top)
{
this.htmlElement.style.left = left;
this.htmlElement.style.top = top;
this.onMove();
}
this.onMove = function()
{
var i;
// notify listeners
for(i = 0; i < this.moveListeners.length; i++)
{
this.moveListeners[i].onMove();
}
}
this.toString = function()
{
return 'block: ' + this.id;
}
}
/*
* Connector class.
* The init function takes two Block objects as arguments representing
* the source and destination of the connector
*/
function Connector(htmlElement, canvas)
{
this.htmlElement = htmlElement;
this.canvas = canvas;
this.source = null;
this.destination = null;
this.startX = null;
this.startY = null;
this.destX = null;
this.destY = null;
this.segment1 = null;
this.segment2 = null;
this.segment3 = null;
this.preferredOrientation = AUTO;
this.orientation = HORIZONTAL;
this.size = 1;
this.color = 'black';
this.moveListeners = new Array();
this.initConnector = function()
{
// detect the connector id
if(this.htmlElement.id)
this.id = this.htmlElement.id;
else
this.id = this.htmlElement.className;
// split the class name to get the ids of the source and destination blocks
var splitted = htmlElement.className.split(' ');
if(splitted.length < 3)
{
alert('Unable to create connector \'' + id + '\', class is not in the correct format: connector <sourceBlockId>, <destBlockId>');
return;
}
var connectorClass = splitted[0] + ' ' + splitted[1] + ' ' + splitted[2];
this.source = this.canvas.findBlock(splitted[1]);
if(!this.source)
{
alert('cannot find source block with id \'' + splitted[1] + '\'');
return;
}
this.destination = this.canvas.findBlock(splitted[2]);
if(!this.destination)
{
alert('cannot find destination block with id \'' + splitted[2] + '\'');
return;
}
// check preferred orientation
if(hasClass(this.htmlElement, 'vertical'))
this.preferredOrientation = VERTICAL;
else if(hasClass(this.htmlElement, 'horizontal'))
this.preferredOrientation = HORIZONTAL;
else
this.preferredOrientation = AUTO;
// build the segments
this.segment1 = document.createElement('div');
this.segment1.id = this.id + "_1";
this.canvas.htmlElement.appendChild(this.segment1);
this.segment1.style.position = 'absolute';
this.segment1.style.overflow = 'hidden';
if(!getStyle(this.segment1, 'background-color'))
this.segment1.style.backgroundColor = this.color;
this.segment1.className = connectorClass;
this.segment2 = document.createElement('div');
this.segment2.id = this.id + "_2";
this.canvas.htmlElement.appendChild(this.segment2);
this.segment2.className = connectorClass;
this.segment2.style.position = 'absolute';
this.segment2.style.overflow = 'hidden';
if(!getStyle(this.segment2, 'background-color'))
this.segment2.style.backgroundColor = this.color;
this.segment3 = document.createElement('div');
this.segment3.id = this.id + "_3";
this.canvas.htmlElement.appendChild(this.segment3);
this.segment3.style.position = 'absolute';
this.segment3.style.overflow = 'hidden';
if(!getStyle(this.segment3, 'background-color'))
this.segment3.style.backgroundColor = this.color;
this.segment3.className = connectorClass;
this.repaint();
this.source.moveListeners.push(this);
this.destination.moveListeners.push(this);
// call inspectors for this connector
var i;
for(i = 0; i < inspectors.length; i++)
{
inspectors[i].inspect(this);
}
// remove old html element
this.htmlElement.parentNode.removeChild(this.htmlElement);
}
/**
* Repaints the connector
*/
this.repaint = function()
{
var sourceLeft = this.source.left();
var sourceTop = this.source.top();
var sourceWidth = this.source.width();
var sourceHeight = this.source.height();
var destinationLeft = this.destination.left();
var destinationTop = this.destination.top();
var destinationWidth = this.destination.width();
var destinationHeight = this.destination.height();
if(this.preferredOrientation == HORIZONTAL)
{
// use horizontal orientation except if it is impossible
if((destinationLeft - sourceLeft - sourceWidth) *
(sourceLeft - destinationLeft - destinationWidth) > 0)
this.orientation = VERTICAL;
else
this.orientation = HORIZONTAL;
}
else if(this.preferredOrientation == VERTICAL)
{
// use vertical orientation except if it is impossible
if((destinationTop - sourceTop - sourceHeight) *
(sourceTop - destinationTop - destinationHeight) > 0)
this.orientation = HORIZONTAL;
else
this.orientation = VERTICAL;
}
else
{
// auto orientation: change current orientation if it is impossible to maintain
if(this.orientation == HORIZONTAL &&
(destinationLeft - sourceLeft - sourceWidth) *
(sourceLeft - destinationLeft - destinationWidth) > 0)
{
this.orientation = VERTICAL;
}
else if(this.orientation == VERTICAL &&
(destinationTop - sourceTop - sourceHeight) *
(sourceTop - destinationTop - destinationHeight) > 0)
{
this.orientation = HORIZONTAL;
}
}
if(this.orientation == HORIZONTAL)
{
// deduce which face to use on source and destination blocks
if(sourceLeft + sourceWidth / 2 < destinationLeft + destinationWidth / 2)
{
// use left side of the source block and right side of the destination block
this.startX = sourceLeft + sourceWidth;
this.destX = destinationLeft;
}
else
{
// use right side of the source block and left side of the destination block
this.startX = sourceLeft;
this.destX = destinationLeft + destinationWidth;
}
this.startY = sourceTop + sourceHeight / 2;
this.destY = destinationTop + destinationHeight /2;
// first horizontal segment positioning
this.segment1.style.left = Math.min(this.startX, (this.destX + this.startX) / 2) + 'px';
this.segment1.style.top = this.startY + 'px';
this.segment1.style.width = Math.abs((this.startX - this.destX) / 2) + this.size + 'px';
this.segment1.style.height = this.size + 'px';
// vertical segment positioning
this.segment2.style.left = ((this.startX + this.destX) /2) + 'px';
this.segment2.style.top = Math.min(this.startY, this.destY) + 'px';
this.segment2.style.width = this.size + 'px';
this.segment2.style.height = Math.abs(this.destY - this.startY) + 'px';
// second horizontal segment positioning
this.segment3.style.left = Math.min((this.startX + this.destX) /2, this.destX) + 'px';
this.segment3.style.top = this.destY + 'px';
this.segment3.style.width = Math.abs((this.destX - this.startX) / 2) + 'px';
this.segment3.style.height = this.size + 'px';
// label positioning
//this.htmlElement.style.left = this.startX + 'px';
//this.htmlElement.style.top = this.startY + this.size + 'px';
}
else
{
// deduce which face to use on source and destination blocks
if(sourceTop + sourceHeight / 2 < destinationTop + destinationHeight / 2)
{
// use bottom side of the sheightblock and top side of thtopestination block
this.startY = sourceTop + sourceHeight;
this.destY = destinationTop;
}
else
{
// use top side of the source block and bottom side of the destination block
this.startY = sourceTop;
this.destY = destinationTop + destinationHeight;
}
this.startX = sourceLeft + sourceWidth / 2;
this.destX = destinationLeft + destinationWidth / 2;
// first vertical segment positioning
this.segment1.style.left = this.startX + 'px';
this.segment1.style.top = Math.min(this.startY, (this.destY + this.startY)/2) + 'px';
this.segment1.style.width = this.size + 'px';
this.segment1.style.height = Math.abs((this.startY - this.destY) / 2) + this.size + 'px';
// horizontal segment positioning
this.segment2.style.left = Math.min(this.startX, this.destX) + 'px';
this.segment2.style.top = ((this.startY + this.destY) /2) + 'px';
this.segment2.style.width = Math.abs(this.destX - this.startX) + 'px';
this.segment2.style.height = this.size + 'px';
// second vertical segment positioning
this.segment3.style.left = this.destX + 'px';
this.segment3.style.top = Math.min(this.destY, (this.destY + this.startY) / 2) + 'px';
this.segment3.style.width = this.size + 'px';
this.segment3.style.height = Math.abs((this.destY - this.startY) / 2) + 'px';
// label positioning
//this.htmlElement.style.left = this.startX + 'px';
//this.htmlElement.style.top = this.startY + this.size + 'px';
}
}
this.onMove = function()
{
this.repaint();
// notify listeners
var i;
for(i = 0; i < this.moveListeners.length; i++)
this.moveListeners[i].onMove();
}
}
function ConnectorEnd(connector, htmlElement, segment)
{
this.connector = connector;
this.htmlElement = htmlElement;
this.connector.segment1.parentNode.appendChild(htmlElement);
// strip extension
this.src = this.htmlElement.src.substring(0, this.htmlElement.src.lastIndexOf('.'));
this.srcExtension = this.htmlElement.src.substring(this.htmlElement.src.lastIndexOf('.'));
this.orientation;
this.repaint = function()
{
this.htmlElement.style.position = 'absolute';
var orientation;
var left;
var top;
if(connector.orientation == HORIZONTAL)
{
left = segment.offsetLeft;
orientation = "l";
if(segment.offsetLeft == connector.segment2.offsetLeft)
{
left += segment.offsetWidth - this.htmlElement.offsetWidth;
var orientation = "r";
}
top = segment.offsetTop - (this.htmlElement.offsetHeight / 2);
}
else
{
top = segment.offsetTop;
orientation = "u";
if(segment.offsetTop == connector.segment2.offsetTop)
{
top += segment.offsetHeight - this.htmlElement.offsetHeight;
var orientation = "d";
}
left = segment.offsetLeft - (this.htmlElement.offsetWidth / 2);
}
this.htmlElement.style.left = Math.ceil(left) + "px";
this.htmlElement.style.top = Math.ceil(top) + "px";
if(this.htmlElement.tagName.toLowerCase() == "img" && this.orientation != orientation)
{
this.htmlElement.src = this.src + "_" + orientation + this.srcExtension;
}
this.orientation = orientation;
}
this.onMove = function()
{
this.repaint();
}
}
function SideConnectorLabel(connector, htmlElement, side)
{
this.connector = connector;
this.htmlElement = htmlElement;
this.connector.segment1.parentNode.appendChild(htmlElement);
if(side == 'source')
this.segment = connector.segment1;
else
this.segment = connector.segment3;
this.side = side;
this.repaint = function()
{
this.htmlElement.style.position = 'absolute';
var segmentOrientation;
if(this.segment.offsetWidth < this.segment.offsetHeight)
segmentOrientation = VERTICAL;
else
segmentOrientation = HORIZONTAL;
var left = this.segment.offsetLeft;
var top = this.segment.offsetTop;
if(segmentOrientation == VERTICAL)
{
if(this.segment.offsetTop == connector.segment2.offsetTop)
{
// put label on the bottom of the connector (segment goes downward)
top += this.segment.offsetHeight - this.htmlElement.offsetHeight;
}
}
else
{
if(this.segment.offsetLeft == connector.segment2.offsetLeft)
{
// anchor the label on its right side to avoid overlap with the block
left += this.segment.offsetWidth - this.htmlElement.offsetWidth;
}
if(this.segment.offsetTop < (this.side == 'source' ? connector.segment3.offsetTop : connector.segment1.offsetTop))
{
// put label over the connector rather than below
top -= this.htmlElement.offsetHeight;
}
}
this.htmlElement.style.left = Math.ceil(left) + "px";
this.htmlElement.style.top = Math.ceil(top) + "px";
}
this.onMove = function()
{
this.repaint();
}
}
function MiddleConnectorLabel(connector, htmlElement)
{
this.connector = connector;
this.htmlElement = htmlElement;
this.connector.segment2.parentNode.appendChild(htmlElement);
this.repaint = function()
{
this.htmlElement.style.position = 'absolute';
var segmentOrientation;
if(connector.segment2.offsetWidth < connector.segment2.offsetHeight)
segmentOrientation = VERTICAL;
else
segmentOrientation = HORIZONTAL;
var left;
var top;
if(segmentOrientation == VERTICAL)
{
// put label at middle height on right side of the connector
top = connector.segment2.offsetTop + (connector.segment2.offsetHeight - this.htmlElement.offsetHeight) / 2;
left = connector.segment2.offsetLeft;
}
else
{
// put connector below the connector at middle widths
top = connector.segment2.offsetTop;
left = connector.segment2.offsetLeft + (connector.segment2.offsetWidth - this.htmlElement.offsetWidth) / 2;;
}
this.htmlElement.style.left = Math.ceil(left) + "px";
this.htmlElement.style.top = Math.ceil(top) + "px";
}
this.onMove = function()
{
this.repaint();
}
}
/*
* Inspector classes
*/
function ConnectorEndsInspector()
{
this.inspect = function(connector)
{
var children = connector.htmlElement.childNodes;
var i;
for(i = 0; i < children.length; i++)
{
if(hasClass(children[i], "connector-end"))
{
var newElement = new ConnectorEnd(connector, children[i], connector.segment3);
newElement.repaint();
connector.moveListeners.push(newElement);
}
else if(hasClass(children[i], "connector-start"))
{
var newElement = new ConnectorEnd(connector, children[i], connector.segment1);
newElement.repaint();
connector.moveListeners.push(newElement);
}
}
}
}
function ConnectorLabelsInspector()
{
this.inspect = function(connector)
{
var children = connector.htmlElement.childNodes;
var i;
for(i = 0; i < children.length; i++)
{
if(hasClass(children[i], "source-label"))
{
var newElement = new SideConnectorLabel(connector, children[i], "source");
newElement.repaint();
connector.moveListeners.push(newElement);
}
else if(hasClass(children[i], "middle-label"))
{
var newElement = new MiddleConnectorLabel(connector, children[i]);
newElement.repaint();
connector.moveListeners.push(newElement);
}
else if(hasClass(children[i], "destination-label"))
{
var newElement = new SideConnectorLabel(connector, children[i], "destination");
newElement.repaint();
connector.moveListeners.push(newElement);
}
}
}
}
/*
* Inspector registration
*/
inspectors.push(new ConnectorEndsInspector());
inspectors.push(new ConnectorLabelsInspector());
/*
* an array containing all the canvases in document
*/
var canvases = new Array();
/*
* This function initializes the js_graph objects inspecting the html document
*/
function initPageObjects()
{
if(isCanvas(document.body))
{
var newCanvas = new Canvas(document.body);
newCanvas.initCanvas();
canvases.push(newCanvas);
}
else
{
var divs = document.getElementsByTagName('div');
var i;
for(i = 0; i < divs.length; i++)
{
if(isCanvas(divs[i]))
{
var newCanvas = new Canvas(divs[i]);
newCanvas.initCanvas();
canvases.push(newCanvas);
}
}
}
}
/*
* Utility functions
*/
function findCanvas(canvasId)
{
var i;
for(i = 0; i < canvases.length; i++)
if(canvases[i].id == canvasId)
return canvases[i];
return null;
}
function findBlock(blockId)
{
var i;
for(i = 0; i < canvases.length; i++)
{
var block = canvases[i].findBlock(blockId);
if(block)
return block;
}
return null;
}
/*
* This function determines whether a html element is to be considered a canvas
*/
function isBlock(htmlElement)
{
return hasClass(htmlElement, 'block');
}
/*
* This function determines whether a html element is to be considered a block
*/
function isCanvas(htmlElement)
{
return hasClass(htmlElement, 'canvas');
}
/*
* This function determines whether a html element is to be considered a connector
*/
function isConnector(htmlElement)
{
return htmlElement.className && htmlElement.className.match(new RegExp('connector .*'));
}
/*
* This function calculates the absolute 'top' value for a html node
*/
function calculateOffsetTop(obj)
{
var curtop = 0;
if (obj.offsetParent)
{
while (obj.offsetParent)
{
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
}
else if (obj.y)
curtop += obj.y;
return curtop;
}
/*
* This function calculates the absolute 'left' value for a html node
*/
function calculateOffsetLeft(obj)
{
var curleft = 0;
if (obj.offsetParent)
{
while (obj.offsetParent)
{
curleft += obj.offsetLeft;
obj = obj.offsetParent;
}
}
else if (obj.x)
curleft += obj.x;
return curleft;
}
function hasClass(element, className)
{
if(!element.className)
return false;
var classes = element.className.split(' ');
var i;
for(i = 0; i < classes.length; i++)
if(classes[i] == className)
return true;
return false;
}
/**
* This function retrieves the actual value of a style property even if it is set via css.
*/
function getStyle(node, styleProp)
{
// if not an element
if( node.nodeType != 1)
return;
var value;
if (node.currentStyle)
{
// ie case
styleProp = replaceDashWithCamelNotation(styleProp);
value = node.currentStyle[styleProp];
}
else if (window.getComputedStyle)
{
// mozilla case
value = document.defaultView.getComputedStyle(node, null).getPropertyValue(styleProp);
}
return value;
}
function replaceDashWithCamelNotation(value)
{
var pos = value.indexOf('-');
while(pos > 0 && value.length > pos + 1)
{
value = value.substring(0, pos) + value.substring(pos + 1, pos + 2).toUpperCase() + value.substring(pos + 2);
pos = value.indexOf('-');
}
return value;
}
</script>
<style rel="stylesheet" type="text/css">
.draggable
{
position: absolute;
cursor: move;
}
.connector
{
background-color: black;
}
.dock_point
{
height: 1px;
width: 1px;
overflow: hidden;
padding: 0px !important;
border: none !important;
margin: 0px;
position: absolute;
font-size: 1px;
visibility: hidden;
}
div.block
{
border: 1px solid #262A37;
background-color: #E0E8FF;
padding: 5px;
font-size: 11px;
}
html
{
padding: 0px;
margin: 0px;
}
body
{
font-family: verdana;
color: #33333F;
padding: 3px;
margin: 0px;
background-color: white;
}
h1
{
color: #FF7521;
margin: 0px;
}
h2
{
font-size: 15px;
margin: 0px;
}
.middle-label, .source-label, .destination-label
{
font-size: 11px;
font-weight: bold;
padding: 5px;
}
div.connector
{
background-color: #FF9900;
}
table.main_table
{
width: 100%;
border-collapse: separate;
}
td.menu
{
padding: 5px;
}
.menu ul
{
margin: 0px;
padding: 0px;
list-style-type: none;
list-style-position: outside;
}
.menu li
{
border: none;
padding: 0px;
font-size: 12px;
margin-bottom: 3px;
}
.menu li a
{
display: block;
border: 1px solid #262A37;
width: 100px;
color: #262A37;
text-decoration: none;
padding: 1px;
background-color: #E0E8FF;
}
.menu li a#active_menu
{
color: #FF9900;
border-color: #FF9900;
}
.menu li a:hover
{
color: #FF9900;
border-color: #FF9900;
}
</style>
</head>
<body onload="initPageObjects();">
<table class="main_table">
<tr>
<td style="vertical-align: top; padding: 0px;">
<div id="mainCanvas" class="canvas block" style="width: 100%; height: 400px; background-color: white; padding: 0px;">
<div id="title_block" class="block draggable" style="left: 30px; top: 30px;">
<h1>js-graph.it</h1>
</div>
<div id="subtitle_block" class="block draggable" style="left: 130px; top: 130px;">
<h2>a javascript library for graphs representation</h2>
</div>
<div class="connector title_block subtitle_block">
<img class="connector-end" src="arrow.gif"/>
</div>
<table class="block draggable" style="left: 550px; top: 50px; border-collapse: collapse; cursor: default;" id="sf_logo" cellpadding="0" cellspacing="0">
<tr>
<td style="border: 1px solid #262A37; cursor: pointer;">
<a href="http://www.java2s.com"><img src="http://www.java2s.com/style/logo.png" width="125" height="37" border="0" alt="SourceForge.net Logo" /></a></td>
<td></td>
</tr>
<tr>
<td></td>
<td style="border: 1px solid #262A37; cursor: move;">
<img src="move.gif"/></td>
</tr>
</table>
<div class="connector title_block sf_logo">
<label class="middle-label">hosted on</label>
<img class="connector-end" src="arrow.gif"/>
</div>
<div class="connector subtitle_block description1_block">
<img class="connector-end" src="arrow.gif"/>
<label class="middle-label">that</label>
</div>
<div id="description1_block" class="block draggable" style="left: 100px; top: 250px;">
allows you
</div>
<div class="connector description1_block description2_block">
<img class="connector-end" src="arrow.gif"/>
<label class="source-label">to</label>
<label class="middle-label">connect</label>
<label class="destination-label">your</label>
</div>
<div id="description2_block" class="block draggable" style="left: 350px; top: 300px;">
html elements
<div id="description2_out1" class="block dock_point" style="right: -2px; top: 20%; background-color: black;"></div>
<div id="description2_out2" class="block dock_point" style="right: -2px; top: 80%; background-color: black;"></div>
</div>
<div id="no_js_code_block" class="block draggable" style="left: 590px; top: 200px; width: 200px;">
using css classes to declare blocks, connectors, labels...<br/>
no JavaScript code required.
</div>
<div class="connector description2_out1 no_js_code_block horizontal">
<img class="connector-end" src="arrow.gif"/>
<label class="middle-label">how?</label>
</div>
<div id="drag_and_drop_block" class="block draggable" style="left: 600px; top: 340px; width: 250px;">
you can drag and drop anything around!<br/>
(but you already noticed, didn't you?)
</div>
<div class="connector description2_out2 drag_and_drop_block horizontal">
<img class="connector-end" src="arrow.gif"/>
<label class="middle-label">and...</label>
<label class="destination-label">...oh!</label>
</div>
</div>
</td>
</tr>
</table>
<div class="connector active_menu mainCanvas">
</div>
</body>
</html>
dragdrop.zip( 15 k)Related examples in the same category