
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1052740 13f79535-47bb-0310-9956-ffa450edef68
638 lines
18 KiB
JavaScript
638 lines
18 KiB
JavaScript
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
/**
|
|
* SVG and VML component rendering functions.
|
|
*/
|
|
|
|
var graph = new Object();
|
|
|
|
/**
|
|
* Detect browser VML support.
|
|
*/
|
|
graph.supportsVML = function() {
|
|
if (typeof graph.supportsVML.supported != 'undefined')
|
|
return graph.supportsVML.supported;
|
|
graph.supportsVML.supported = navigator.appName == 'Microsoft Internet Explorer';
|
|
return graph.supportsVML.supported;
|
|
};
|
|
|
|
/**
|
|
* Detect browser SVG support.
|
|
*/
|
|
graph.supportsSVG = function() {
|
|
if (typeof graph.supportsSVG.supported != 'undefined')
|
|
return graph.supportsSVG.supported;
|
|
graph.supportsSVG.supported = navigator.appName != 'Microsoft Internet Explorer';
|
|
return graph.supportsSVG.supported;
|
|
};
|
|
|
|
/**
|
|
* Basic colors
|
|
*/
|
|
graph.red = 'red';
|
|
graph.green = 'green';
|
|
graph.blue = 'blue';
|
|
graph.yellow = 'yellow';
|
|
graph.orange = '#ffa500';
|
|
graph.gray = 'gray'
|
|
|
|
/**
|
|
* Base path class.
|
|
*/
|
|
graph.BasePath = function() {
|
|
this.path = '';
|
|
this.x = 0;
|
|
this.y = 0;
|
|
|
|
this.pos = function(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
return this;
|
|
};
|
|
|
|
this.xpos = function() {
|
|
return this.x;
|
|
};
|
|
|
|
this.ypos = function() {
|
|
return this.y;
|
|
};
|
|
|
|
this.rmove = function(x, y) {
|
|
return this.move(this.x + x, this.y + y);
|
|
};
|
|
|
|
this.rline = function(x, y) {
|
|
return this.line(this.x + x, this.y + y);
|
|
};
|
|
|
|
this.rcurve = function(x1, y1, x, y) {
|
|
return this.curve(this.x + x1, this.y + y1, this.x + x1 + x, this.y + y1 + y);
|
|
};
|
|
|
|
this.str = function() {
|
|
return this.path;
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Rendering functions that work both with VML and SVG.
|
|
*/
|
|
var graph;
|
|
|
|
/**
|
|
* VML rendering.
|
|
*/
|
|
if (graph.supportsVML()) {
|
|
|
|
graph.vmlns='urn:schemas-microsoft-com:vml';
|
|
document.write('<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />');
|
|
|
|
/**
|
|
* Make a graph.
|
|
*/
|
|
graph.mkgraph = function() {
|
|
var div = document.createElement('div');
|
|
div.id = 'vmldiv';
|
|
document.body.appendChild(div);
|
|
|
|
var vmlg = document.createElement('v:group');
|
|
vmlg.style.width = 500;
|
|
vmlg.style.height = 500;
|
|
vmlg.coordsize = '500,500';
|
|
div.appendChild(vmlg);
|
|
|
|
graph.dragging = null;
|
|
|
|
function draggable(n) {
|
|
if (n == vmlg)
|
|
return null;
|
|
if (n.nodeName == 'group')
|
|
return n;
|
|
return draggable(n.parentNode);
|
|
}
|
|
|
|
vmlg.onmousedown = function() {
|
|
window.event.returnValue = false;
|
|
graph.dragging = draggable(window.event.srcElement);
|
|
if (graph.dragging == null)
|
|
return false;
|
|
graph.dragging.parentNode.appendChild(graph.dragging);
|
|
graph.dragX = window.event.clientX;
|
|
graph.dragY = window.event.clientY;
|
|
vmlg.setCapture();
|
|
return false;
|
|
};
|
|
|
|
vmlg.onmouseup = function() {
|
|
if (graph.dragging == null)
|
|
return false;
|
|
graph.dragging = null;
|
|
vmlg.releaseCapture();
|
|
return false;
|
|
};
|
|
|
|
vmlg.onmousemove = function() {
|
|
if (graph.dragging == null)
|
|
return false;
|
|
var origX = graph.dragging.coordorigin.X;
|
|
var origY = graph.dragging.coordorigin.Y;
|
|
var newX = origX - (window.event.clientX - graph.dragX);
|
|
var newY = origY - (window.event.clientY - graph.dragY);
|
|
graph.dragX = window.event.clientX;
|
|
graph.dragY = window.event.clientY;
|
|
graph.dragging.setAttribute('coordorigin', newX + ' ' + newY);
|
|
return false;
|
|
};
|
|
|
|
graph.textWidthDiv = document.createElement('span');
|
|
graph.textWidthDiv.style.visibility = 'hidden'
|
|
div.appendChild(graph.textWidthDiv);
|
|
|
|
return vmlg;
|
|
};
|
|
|
|
/**
|
|
* Make a shape path.
|
|
*/
|
|
graph.mkpath = function() {
|
|
function Path() {
|
|
this.BasePath = graph.BasePath;
|
|
this.BasePath();
|
|
|
|
this.move = function(x, y) {
|
|
this.path += 'M ' + x + ',' + y + ' ';
|
|
return this.pos(x, y);
|
|
};
|
|
|
|
this.line = function(x, y) {
|
|
this.path += 'L ' + x + ',' + y + ' ';
|
|
return this.pos(x, y);
|
|
};
|
|
|
|
this.curve = function(x1, y1, x, y) {
|
|
this.path += 'QB ' + x1 + ',' + y1 + ',' + x + ',' + y + ' ';
|
|
return this.pos(x, y);
|
|
};
|
|
|
|
this.end = function() {
|
|
this.path += 'X E';
|
|
return this;
|
|
};
|
|
}
|
|
|
|
return new Path();
|
|
};
|
|
|
|
/**
|
|
* Make a title element.
|
|
*/
|
|
graph.mktitle = function(t) {
|
|
var title = document.createElement('v:textbox');
|
|
title.style.left = '25';
|
|
title.style.top = '5';
|
|
title.style.position = 'absolute';
|
|
var tnode = document.createTextNode(t);
|
|
title.appendChild(tnode);
|
|
return title;
|
|
};
|
|
|
|
/**
|
|
* Return the width of a title.
|
|
*/
|
|
graph.titlewidth = function(t) {
|
|
graph.textWidthDiv.innerHTML = t;
|
|
var twidth = graph.textWidthDiv.offsetWidth;
|
|
graph.textWidthDiv.innerHTML = '';
|
|
return twidth;
|
|
};
|
|
|
|
/**
|
|
* Make a component shape.
|
|
*/
|
|
graph.mkcompshape = function(comp, cassoc) {
|
|
var name = scdl.name(comp);
|
|
var title = graph.mktitle(name);
|
|
|
|
var d = graph.mkcomppath(comp, cassoc).str();
|
|
|
|
var shape = document.createElement('v:shape');
|
|
shape.style.width = 500;
|
|
shape.style.height = 500;
|
|
shape.coordsize = '500,500';
|
|
shape.path = d;
|
|
shape.fillcolor = graph.color(comp);
|
|
shape.stroked = 'false';
|
|
|
|
var contour = document.createElement('v:shape');
|
|
contour.style.width = 500;
|
|
contour.style.height = 500;
|
|
contour.coordsize = '500,500';
|
|
contour.setAttribute('path', d);
|
|
contour.filled = 'false';
|
|
contour.strokecolor = graph.gray;
|
|
contour.strokeweight = '3';
|
|
contour.style.top = 1;
|
|
contour.style.left = 1;
|
|
var stroke = document.createElement('v:stroke');
|
|
stroke.opacity = '20%';
|
|
contour.appendChild(stroke);
|
|
|
|
var g = document.createElement('v:group');
|
|
g.style.width = 500;
|
|
g.style.height = 500;
|
|
g.coordsize = '500,500';
|
|
g.appendChild(shape);
|
|
g.appendChild(contour);
|
|
g.appendChild(title);
|
|
return g;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* SVG rendering.
|
|
*/
|
|
if (graph.supportsSVG()) {
|
|
|
|
graph.svgns='http://www.w3.org/2000/svg';
|
|
|
|
/**
|
|
* Make a graph.
|
|
*/
|
|
graph.mkgraph = function() {
|
|
var div = document.createElement('div');
|
|
div.id = 'svgdiv';
|
|
document.body.appendChild(div);
|
|
|
|
var svg = document.createElementNS(graph.svgns, 'svg');
|
|
svg.style.height = '100%';
|
|
svg.style.width = '100%';
|
|
div.appendChild(svg);
|
|
|
|
graph.dragging = null;
|
|
|
|
function draggable(n) {
|
|
if (n == svg)
|
|
return null;
|
|
if (n.nodeName == 'g')
|
|
return n;
|
|
return draggable(n.parentNode);
|
|
}
|
|
|
|
svg.onmousedown = function(e) {
|
|
if (e.preventDefault)
|
|
e.preventDefault();
|
|
else
|
|
e.returnValue= false;
|
|
graph.dragging = draggable(e.target);
|
|
if (graph.dragging == null)
|
|
return false;
|
|
graph.dragging.parentNode.appendChild(graph.dragging);
|
|
var pos = typeof e.touches != "undefined" ? e.touches[0] : e;
|
|
graph.dragX = pos.clientX;
|
|
graph.dragY = pos.clientY;
|
|
return false;
|
|
};
|
|
|
|
svg.ontouchstart = svg.onmousedown;
|
|
|
|
svg.onmouseup = function(e) {
|
|
if (graph.dragging == null)
|
|
return false;
|
|
graph.dragging = null;
|
|
return false;
|
|
};
|
|
|
|
svg.ontouchend = svg.onmouseup;
|
|
|
|
svg.onmousemove = function(e) {
|
|
if (graph.dragging == null)
|
|
return false;
|
|
var matrix = graph.dragging.getCTM();
|
|
var pos = typeof e.touches != "undefined" ? e.touches[0] : e;
|
|
var newX = Number(matrix.e) + (pos.clientX - graph.dragX);
|
|
var newY = Number(matrix.f) + (pos.clientY - graph.dragY);
|
|
graph.dragX = pos.clientX;
|
|
graph.dragY = pos.clientY;
|
|
graph.dragging.setAttribute('transform', 'translate(' + newX + ',' + newY + ')');
|
|
return false;
|
|
};
|
|
|
|
svg.ontouchmove = svg.onmousemove;
|
|
|
|
graph.textWidthSvg = document.createElementNS(graph.svgns, 'svg');
|
|
graph.textWidthSvg.style.visibility = 'hidden';
|
|
graph.textWidthSvg.style.height = '0px';
|
|
graph.textWidthSvg.style.width = '0px';
|
|
div.appendChild(graph.textWidthSvg);
|
|
|
|
return svg;
|
|
};
|
|
|
|
/**
|
|
* Make a shape path.
|
|
*/
|
|
graph.mkpath = function() {
|
|
function Path() {
|
|
this.BasePath = graph.BasePath;
|
|
this.BasePath();
|
|
|
|
this.move = function(x, y) {
|
|
this.path += 'M' + x + ',' + y + ' ';
|
|
return this.pos(x, y);
|
|
};
|
|
|
|
this.line = function(x, y) {
|
|
this.path += 'L' + x + ',' + y + ' ';
|
|
return this.pos(x, y);
|
|
};
|
|
|
|
this.curve = function(x1, y1, x, y) {
|
|
this.path += 'Q' + x1 + ',' + y1 + ' ' + x + ',' + y + ' ';
|
|
return this.pos(x, y);
|
|
};
|
|
|
|
this.end = function() {
|
|
this.path += 'Z';
|
|
return this;
|
|
};
|
|
}
|
|
|
|
return new Path();
|
|
};
|
|
|
|
/**
|
|
* Make a title element.
|
|
*/
|
|
graph.mktitle = function(t) {
|
|
var title = document.createElementNS(graph.svgns, 'text');
|
|
title.setAttribute('text-anchor', 'start');
|
|
title.setAttribute('x', 5);
|
|
title.setAttribute('y', 15);
|
|
title.appendChild(document.createTextNode(t));
|
|
graph.textWidthSvg.appendChild(title);
|
|
return title;
|
|
};
|
|
|
|
/**
|
|
* Return a width of a title.
|
|
*/
|
|
graph.titlewidth = function(t) {
|
|
var title = graph.mktitle(t);
|
|
var width = title.getBBox().width;
|
|
graph.textWidthSvg.removeChild(title);
|
|
return width;
|
|
};
|
|
|
|
/**
|
|
* Make a component shape.
|
|
*/
|
|
graph.mkcompshape = function(comp, cassoc) {
|
|
var name = scdl.name(comp);
|
|
var title = graph.mktitle(name);
|
|
|
|
var d = graph.mkcomppath(comp, cassoc).str();
|
|
|
|
var shape = document.createElementNS(graph.svgns, 'path');
|
|
shape.setAttribute('d', d);
|
|
shape.setAttribute('fill', graph.color(comp));
|
|
|
|
var contour = document.createElementNS(graph.svgns, 'path');
|
|
contour.setAttribute('d', d);
|
|
contour.setAttribute('fill', 'none');
|
|
contour.setAttribute('stroke', graph.gray);
|
|
contour.setAttribute('stroke-width', '4');
|
|
contour.setAttribute('stroke-opacity', '0.20');
|
|
contour.setAttribute('transform', 'translate(1,1)');
|
|
|
|
var g = document.createElementNS(graph.svgns, 'g');
|
|
g.appendChild(shape);
|
|
g.appendChild(contour);
|
|
g.appendChild(title);
|
|
return g;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Make a reference shape path, positioned to the right of a component shape.
|
|
*/
|
|
graph.mkrrefpath = function(ref, cassoc, path) {
|
|
var height = graph.refheight(ref, cassoc);
|
|
var ypos = path.ypos();
|
|
return path.rline(0,10).rline(0,10).rcurve(0,5,-5,0).rcurve(-5,0,0,-5).rcurve(0,-5,-5,0).rcurve(-5,0,0,5).rline(0,20).rcurve(0,5,5,0).rcurve(5,0,0,-5).rcurve(0,-5,5,0).rcurve(5,0,0,5).line(path.xpos(),ypos + height);
|
|
};
|
|
|
|
/**
|
|
* Make a reference shape path, positioned at the bottom of a component shape.
|
|
*/
|
|
graph.mkbrefpath = function(ref, cassoc, path) {
|
|
var width = graph.refwidth(ref, cassoc);
|
|
var xpos = path.xpos();
|
|
return path.line(xpos - width + 60,path.ypos()).rline(-10,0).rline(-10,0).rcurve(-5,0,0,-5).rcurve(0,-5,5,0).rcurve(5,0,0,-5).rcurve(0,-5,-5,0).rline(-20,0).rcurve(-5,0,0,5).rcurve(0,5,5,0).rcurve(5,0,0,5).rcurve(0,5,-5,0).line(xpos - width,path.ypos());
|
|
};
|
|
|
|
/**
|
|
* Make a service shape path, positioned to the left of a component shape.
|
|
*/
|
|
graph.mklsvcpath = function(svc, path) {
|
|
var height = 60;
|
|
var ypos = path.ypos();
|
|
return path.rline(0,-10).rline(0, -10).rcurve(0,-5,-5,0).rcurve(-5,0,0,5).rcurve(0,5,-5,0).rcurve(-5,0,0,-5).rline(0,-20).rcurve(0,-5,5,0).rcurve(5,0,0,5).rcurve(0,5,5,0).rcurve(5,0,0,-5).line(path.xpos(), ypos - height);
|
|
};
|
|
|
|
/**
|
|
* Make a service shape path, positioned at the top of a component shape.
|
|
*/
|
|
graph.mktsvcpath = function(svc, path) {
|
|
var width = 60;
|
|
var xpos = path.xpos();
|
|
return path.rline(10,0).rline(10,0).rcurve(5,0,0,-5).rcurve(0,-5,-5,0).rcurve(-5,0,0,-5).rcurve(0,-5,5,0).rline(20,0).rcurve(5,0,0,5).rcurve(0,5,-5,0).rcurve(-5,0,0,5).rcurve(0,5,5,0).line(xpos + width,path.ypos());
|
|
};
|
|
|
|
|
|
/**
|
|
* Return the services and references of a component.
|
|
*/
|
|
graph.tsvcs = function(comp) {
|
|
return filter(function(s) { return scdl.align(s) == 'top'; }, scdl.services(comp));
|
|
};
|
|
|
|
graph.lsvcs = function(comp) {
|
|
return filter(function(s) { var a = scdl.align(s); return a == null || a == 'left'; }, scdl.services(comp));
|
|
};
|
|
|
|
graph.brefs = function(comp) {
|
|
return filter(function(r) { return scdl.align(r) == 'bottom'; }, scdl.references(comp));
|
|
};
|
|
|
|
graph.rrefs = function(comp) {
|
|
return filter(function(r) { var a = scdl.align(r); return a == null || a == 'right'; }, scdl.references(comp));
|
|
};
|
|
|
|
/**
|
|
* Return the color of a component.
|
|
*/
|
|
graph.color = function(comp) {
|
|
var c = scdl.color(comp);
|
|
return c == null? graph.blue : c;
|
|
}
|
|
|
|
/**
|
|
* Return the height of a reference.
|
|
*/
|
|
graph.refheight = function(ref, cassoc) {
|
|
var target = assoc(scdl.target(ref), cassoc);
|
|
if (isNil(target))
|
|
return 60;
|
|
return graph.compheight(cadr(target), cassoc);
|
|
}
|
|
|
|
/**
|
|
* Return the total height of a list of references.
|
|
*/
|
|
graph.refsheight = function(refs, cassoc) {
|
|
if (isNil(refs))
|
|
return 0;
|
|
return graph.refheight(car(refs), cassoc) + graph.refsheight(cdr(refs), cassoc);
|
|
}
|
|
|
|
/**
|
|
* Return the height of a component.
|
|
*/
|
|
graph.compheight = function(comp, cassoc) {
|
|
var lsvcs = graph.lsvcs(comp);
|
|
var lsvcsh = Math.max(1, length(lsvcs)) * 60 + 20;
|
|
var rrefs = graph.rrefs(comp);
|
|
var rrefsh = graph.refsheight(rrefs, cassoc) + 20;
|
|
var height = Math.max(lsvcsh, rrefsh);
|
|
return height;
|
|
};
|
|
|
|
/**
|
|
* Return the width of a reference.
|
|
*/
|
|
graph.refwidth = function(ref, cassoc) {
|
|
var target = assoc(scdl.target(ref), cassoc);
|
|
if (isNil(target))
|
|
return 60;
|
|
return graph.compwidth(cadr(target), cassoc);
|
|
}
|
|
|
|
/**
|
|
* Return the total width of a list of references.
|
|
*/
|
|
graph.refswidth = function(refs, cassoc) {
|
|
if (isNil(refs))
|
|
return 0;
|
|
return graph.refwidth(car(refs), cassoc) + graph.refswidth(cdr(refs), cassoc);
|
|
}
|
|
|
|
/**
|
|
* Return the width of a component.
|
|
*/
|
|
graph.compwidth = function(comp, cassoc) {
|
|
var twidth = graph.titlewidth(scdl.name(comp)) + 20;
|
|
var tsvcs = graph.tsvcs(comp);
|
|
var tsvcsw = Math.max(1, length(tsvcs)) * 60 + 20;
|
|
var brefs = graph.brefs(comp);
|
|
var brefsw = graph.refswidth(brefs, cassoc) + 20;
|
|
var width = Math.max(twidth, Math.max(tsvcsw, brefsw));
|
|
return width;
|
|
};
|
|
|
|
/**
|
|
* Make a component shape path.
|
|
*/
|
|
graph.mkcomppath = function(comp, cassoc) {
|
|
var tsvcs = graph.tsvcs(comp);
|
|
var lsvcs = graph.lsvcs(comp);
|
|
var brefs = graph.brefs(comp);
|
|
var rrefs = graph.rrefs(comp);
|
|
|
|
var height = graph.compheight(comp, cassoc);
|
|
var width = graph.compwidth(comp, cassoc);
|
|
|
|
var path = graph.mkpath().move(10,0);
|
|
for (var s = 0; s < length(tsvcs); s++)
|
|
path = graph.mktsvcpath(tsvcs[s], path);
|
|
|
|
path = path.line(width - 10,path.ypos()).rcurve(10,0,0,10);
|
|
for (var r = 0; r < length(rrefs); r++)
|
|
path = graph.mkrrefpath(rrefs[r], cassoc, path);
|
|
|
|
var boffset = 10 + graph.refswidth(brefs, cassoc);
|
|
path = path.line(path.xpos(),height - 10).rcurve(0,10,-10,0).line(boffset, path.ypos());
|
|
for (var r = 0; r < length(brefs); r++)
|
|
path = graph.mkbrefpath(brefs[r], cassoc, path);
|
|
|
|
var loffset = 10 + (length(lsvcs) * 60);
|
|
path = path.line(10,path.ypos()).rcurve(-10,0,0,-10).line(path.xpos(), loffset);
|
|
for (var s = 0; s < length(lsvcs); s++)
|
|
path = graph.mklsvcpath(lsvcs[s], path);
|
|
|
|
path = path.line(0,10).rcurve(0,-10,10,0);
|
|
return path.end();
|
|
};
|
|
|
|
/**
|
|
* Render a component.
|
|
*/
|
|
graph.component = function(comp, cassoc) {
|
|
return graph.mkcompshape(comp, cassoc);
|
|
};
|
|
|
|
/**
|
|
* Render a composite.
|
|
*/
|
|
graph.composite = function(compos) {
|
|
var name = scdl.name(compos);
|
|
var comps = scdl.components(compos);
|
|
var cassoc = scdl.nameToElementAssoc(comps);
|
|
|
|
function renderReference(ref, cassoc) {
|
|
var target = assoc(scdl.target(ref), cassoc);
|
|
if (isNil(target))
|
|
return mklist();
|
|
return renderComponent(cadr(target), cassoc);
|
|
}
|
|
|
|
function renderReferences(refs, cassoc) {
|
|
if (isNil(refs))
|
|
return mklist();
|
|
var rref = renderReference(car(refs), cassoc);
|
|
if (isNil(rref))
|
|
return renderReferences(cdr(refs), cassoc);
|
|
return append(rref, renderReferences(cdr(refs), cassoc));
|
|
}
|
|
|
|
function renderComponent(comp, cassoc) {
|
|
return append(renderReferences(scdl.references(comp), cassoc), mklist(graph.component(comp, cassoc)));
|
|
}
|
|
|
|
function renderComponents(comps, cassoc) {
|
|
if (isNil(comps))
|
|
return mklist();
|
|
return append(renderComponent(car(comps), cassoc), renderComponents(cdr(comps), cassoc));
|
|
}
|
|
|
|
var rcomps = renderComponents(comps, cassoc);
|
|
return rcomps;
|
|
};
|
|
|