<html>
<head>
</head>
<!-- warning : this is a prototype -->
<!-- license : the BSD license -->
<body>
<script>
var FluoCanvas = function() {
//
// draws centered text
//
function drawText (c, text, bwidth, bheight) {
c.save();
if (c.fluoVertical == false) {
c.translate(bwidth/2, bheight/2);
c.rotate(Math.PI/2);
}
c.mozTextStyle = "14px helvetica";
var width = c.mozMeasureText(text);
c.translate(-(width/2), 17);
c.mozDrawText(text);
c.translate(+(width/2), -17);
c.restore();
}
function drawArrow (c, length) {
var w = 4;
c.beginPath();
c.moveTo(0, 0);
c.lineTo(0, length);
c.stroke();
c.beginPath();
c.moveTo(0, length);
c.lineTo(-w, length-w*2);
c.lineTo(w, length-w*2);
c.lineTo(0, length);
c.fill();
}
function drawRoundedRect (c, width, height, radius) {
var w2 = width / 2;
c.beginPath();
c.moveTo(0, 0);
c.lineTo(w2 - radius, 0);
c.quadraticCurveTo(w2, 0, w2, radius);
c.lineTo(w2, height - radius);
c.quadraticCurveTo(w2, height, w2 - radius, height);
c.lineTo(-w2 + radius, height);
c.quadraticCurveTo(-w2, height, -w2, height - radius);
c.lineTo(-w2, radius);
c.quadraticCurveTo(-w2, 0, -w2 + radius, 0);
c.lineTo(0, 0);
c.stroke();
}
function drawQuadraticPath (c, x, y, radius) {
var xradius = radius;
if (x < 0) xradius = -radius;
var yradius = radius;
if (y < 0) yradius = -radius;
c.beginPath();
c.moveTo(0, 0);
c.lineTo(x - xradius, 0);
c.quadraticCurveTo(x, 0, x, yradius);
c.lineTo(x, y);
c.stroke();
}
function drawDiamond (c, height) {
var h = height / 2;
for (var i = 0; i < 2; i++) {
c.beginPath();
c.moveTo(0, 0);
c.lineTo(h, h);
c.lineTo(0, height);
c.lineTo(-h, h);
c.lineTo(0, 0);
if (i == 0) {
c.save();
c.fillStyle = 'rgb(255, 255, 255)';
c.fill();
c.restore();
}
else {
c.stroke();
}
}
}
function drawParaDiamond (c, height) {
drawDiamond(c, height);
c.save();
c.lineWidth = 2.5;
var l = height / 4;
c.beginPath();
c.moveTo(0, l); c.lineTo(0, l * 3);
c.stroke();
c.beginPath();
c.moveTo(-l, l * 2); c.lineTo(l, l * 2);
c.stroke();
c.restore();
}
return {
drawText: drawText,
drawArrow: drawArrow,
drawRoundedRect: drawRoundedRect,
drawQuadraticPath: drawQuadraticPath,
drawDiamond: drawDiamond,
drawParaDiamond: drawParaDiamond
};
}();
var FluoCan = function() {
//
// MISC METHODS
function childrenMax (c, exp, funcname) {
var max = 0;
for (var i = 0; i < exp[2].length; i++) {
var val = FluoCan[funcname](c, exp[2][i]);
if (val > max) max = val;
}
return max;
}
function childrenSum (c, exp, funcname) {
var sum = 0;
for (var i = 0; i < exp[2].length; i++) {
sum += FluoCan[funcname](c, exp[2][i]);
}
return sum;
}
function attributesMaxWidth (c, exp, title) {
var max = 0;
if (title) max = c.mozMeasureText(title);
for (var attname in exp[1]) {
var text = ""+attname+": "+exp[1][attname];
var l = c.mozMeasureText(text);
if (l > max) max = l;
}
return max;
}
function attributesCount (exp) {
var count = 0;
for (var k in exp[1]) { count++; }
return count;
}
function drawAttributes (c, exp, expname, width, height) {
if (expname) {
FluoCanvas.drawText(c, exp[0], width, height);
if (c.fluoVertical == false) c.translate(-20, 0);
else c.translate(0, 20);
}
for (var attname in exp[1]) {
FluoCanvas.drawText(c, attname+": "+exp[1][attname], width, height);
if (c.fluoVertical == false) c.translate(-20, 0);
else c.translate(0, 20);
}
}
//
// returns a copy of a handler, some kind of static inheritance
//
function copyHandler (h) {
var result = {};
for (var k in h) result[k] = h[k];
return result;
}
//
// EXPRESSION HANDLERS
var GenericHandler = {
render: function (c, exp) {
var width = getWidth(c, exp);
var height = getHeight(c, exp);
FluoCanvas.drawRoundedRect(c, width, height, 8);
c.save();
drawAttributes(c, exp, true, width, height);
c.restore();
},
getRealHeight: function (c, exp) {
return 7 + (1 + attributesCount(exp)) * 20;
},
getRealWidth: function (c, exp) {
return 10 + attributesMaxWidth(c, exp, exp[0]);
},
getHeight: function (c, exp) {
if (c.fluoVertical == false) return this.getRealWidth(c, exp);
return this.getRealHeight(c, exp);
},
getWidth: function (c, exp) {
if (c.fluoVertical == false) return this.getRealHeight(c, exp);
return this.getRealWidth(c, exp);
}
};
var GenericWithChildrenHandler = {
render: function (c, exp, expid) {
var width = this.getWidth(c, exp);
var height = this.getHeight(c, exp);
var attWidth = attributesMaxWidth(c, exp, exp[0]) + 7;
var attHeight = attributesCount(exp) * 20;
if (c.fluoVertical == false) {
var w = attWidth;
attWidth = attHeight;
attHeight = w;
}
FluoCanvas.drawRoundedRect(c, width, height, 8);
c.save();
c.translate(-width/2 + attWidth/2, 0);
if (c.fluoVertical == false) c.translate(attHeight/2, 0);
drawAttributes(c, exp, true, attWidth, attHeight);
c.restore();
c.save();
c.translate(width/2 - childrenMax(c, exp, 'getWidth')/2 - 3, 5);
for (var i = 0; i < exp[2].length; i++) {
var child = exp[2][i];
FluoCan.renderExpression(c, child, expid + '.' + i);
c.translate(0, 7 + FluoCan.getHeight(c, child));
}
c.restore();
},
getHeight: function (c, exp) {
return (exp[2].length + 1) * 7 + childrenSum(c, exp, 'getHeight');
},
getWidth: function (c, exp) {
return attributesMaxWidth(c, exp, exp[0]) + 14 + childrenMax(c, exp, 'getWidth');
}
};
var ProcessDefinitionHandler = {
render: function (c, exp, expid) {
},
getHeight: function (c, exp) {
},
getWidth: function (c, exp) {
}
};
var VerticalHandler = {
render: function (c, exp, expid) {
c.save();
var children = exp[2];
for (var i = 0; i < children.length; i++) {
var child = children[i];
FluoCan.renderExpression(c, child, expid + "." + i);
c.translate(0, FluoCan.getHeight(c, child));
if (i < children.length - 1) {
FluoCanvas.drawArrow(c, 20);
c.translate(0, 20);
}
}
c.restore();
},
getHeight: function (c, exp) {
return (exp[2].length - 1) * 20 + childrenSum(c, exp, 'getHeight');
},
getWidth: function (c, exp) {
return childrenMax(c, exp, 'getWidth');
}
};
var HorizontalHandler = {
render: function (c, exp, expid) {
var dist = this.computeDistribution(c, exp);
var childrenHeight = this.getChildrenHeight(c, exp);
this.renderHeader(c, exp, dist);
c.save();
c.translate(0, this.getHeaderHeight(c, exp));
for (var i=0; i < exp[2].length; i++) {
var child = exp[2][i];
c.save();
c.translate(dist[i], 0);
this.renderChild(c, child, expid + '.' + i, childrenHeight);
c.restore();
}
c.restore();
this.renderFooter(c, exp, dist, childrenHeight);
},
getHeaderHeight: function (c, exp) {
if (c.fluoVertical == false) return 23 + attributesMaxWidth(c, exp);
return 23 + attributesCount(exp) * 20;
},
getChildrenHeight: function (c, exp) {
return childrenMax(c, exp, 'getHeight');
},
getHeight: function (c, exp) {
return this.getHeaderHeight(c, exp) + this.getChildrenHeight(c, exp) + 10;
},
getWidth: function (c, exp) {
return (exp[2].length - 1) * 3 + childrenSum(c, exp, 'getWidth');
},
computeDistribution: function (c, exp) {
var totalWidth = this.getWidth(c, exp);
var offset = -totalWidth/2;
var dist = new Array(exp[2].length);
for (var i = 0; i < exp[2].length; i++) {
var cWidth = FluoCan.getWidth(c, exp[2][i]);
dist[i] = offset + cWidth / 2;
offset += (cWidth + 3);
}
return dist;
},
renderHeader: function (c, exp, distribution) {
var hheight = this.getHeaderHeight(c, exp) - 10;
c.save();
c.translate(0, 10);
FluoCanvas.drawQuadraticPath(
c, distribution[0], hheight, 8);
FluoCanvas.drawQuadraticPath(
c, distribution[distribution.length-1], hheight, 8);
for (var i = 1; i < distribution.length - 1; i++) {
c.beginPath();
c.moveTo(distribution[i], 0);
c.lineTo(distribution[i], hheight);
c.stroke();
}
c.restore();
this.renderHeaderSymbol(c);
this.renderHeaderLabel(c, exp);
},
renderHeaderSymbol: function (c) {
FluoCanvas.drawDiamond(c, 20);
},
renderHeaderLabel: function (c, exp) {
var width = attributesMaxWidth(c, exp);
var height = attributesCount(exp) * 20;
if (c.fluoVertical == false) {
var w = width;
width = height;
height = w;
}
c.save();
c.translate(0, 20);
c.save();
c.fillStyle = 'rgb(255, 255, 255)';
c.fillRect(-width/2, 0, width, height);
c.restore();
drawAttributes(c, exp, false, width, height);
c.restore();
},
renderChild: function (c, exp, expid, childrenHeight) {
var cheight = FluoCan.getHeight(c, exp);
FluoCan.renderExpression(c, exp, expid);
c.beginPath();
c.moveTo(0, cheight); c.lineTo(0, childrenHeight);
c.stroke();
},
renderFooter: function (c, exp, distribution) {
var childrenHeight = this.getChildrenHeight(c, exp);
c.save();
c.translate(
0, this.getHeaderHeight(c, exp) + this.getChildrenHeight(c, exp) + 10);
FluoCanvas.drawQuadraticPath(
c, distribution[0], -10, 8);
FluoCanvas.drawQuadraticPath(
c, distribution[distribution.length-1], -10, 8);
for (var i = 1; i < distribution.length - 1; i++) {
c.beginPath();
c.moveTo(distribution[i], 0);
c.lineTo(distribution[i], -10);
c.stroke();
}
c.restore();
},
renderFooterDiamond: function (c) {
}
};
var ConcurrenceHandler = copyHandler(HorizontalHandler);
ConcurrenceHandler.renderHeaderSymbol = function (c) {
FluoCanvas.drawParaDiamond(c, 20);
};
var ParticipantHandler = {
render: function (c, exp, expid) {
var width = getWidth(c, exp);
var height = getHeight(c, exp);
FluoCanvas.drawRoundedRect(c, width, height, 8);
FluoCanvas.drawText(c, exp[1]['ref'], width, height);
},
getHeight: function (c, exp) {
if (c.fluoVertical == false) return 80;
return 50;
},
getWidth: function (c, exp) {
if (c.fluoVertical == false) return 50;
return 80;
}
};
var HANDLERS = {
'process-definition': ProcessDefinitionHandler,
'sequence': VerticalHandler,
'concurrence': ConcurrenceHandler,
'if': HorizontalHandler
//'participant': ParticipantHandler
};
function renderExpression (context, exp, expid) {
return getHandler(exp).render(context, exp, expid);
}
//
// returns the raw height of an expression
//
function getHeight (c, exp) {
return getHandler(exp).getHeight(c, exp);
}
//
// return the raw width of an expression
//
function getWidth (c, exp) {
return getHandler(exp).getWidth(c, exp);
}
function getHandler (exp) {
var h = HANDLERS[exp[0]];
if (h != null) return h;
if (exp[2].length > 0) return GenericWithChildrenHandler;
return GenericHandler;
}
return {
newCan: newCan,
renderExpression: renderExpression,
getHeight: getHeight,
getWidth: getWidth
};
}();
//
// TESTING
//var flow =
// [ 'process-definition', {'name': 'pdef', 'revision': '1'}, [
// [ 'sequence', {}, [
// [ 'participant', {'ref': 'alpha'}, []],
// [ 'participant', {'ref': 'bravo'}, []]
// ]]
// ]];
var flow1 =
[ 'sequence', {}, [
[ 'participant', {'ref': 'first'}, []],
[ 'concurrence', {}, [
[ 'participant', {'ref': 'a'}, []],
[ 'sequence', {}, [
[ 'participant', {'ref': 'b一'}, []],
[ 'participant', {'ref': 'b二'}, []],
[ 'participant', {'ref': 'b三'}, []]]
],
[ 'concurrence', {'count': 1}, [
[ 'participant', {'ref': 'c_a'}, []],
[ 'participant', {'ref': 'c_b', 'timeout': '12d'}, []]]
]//,
//[ 'concurrence', {}, [
// [ 'participant', {'ref': 'd'}, []]]
//]]
]
],
[ 'participant', {'ref': '最後'}, []]]
];
var cfluo = FluoCan.newCan('fluo', 600, 400);
document.body.appendChild(cfluo);
var c = cfluo.getContext("2d");
c.scale(0.75, 0.75);
//c.beginPath();
//c.moveTo(1, 1);
//c.lineTo(1, 399);
//c.lineTo(599, 399);
//c.lineTo(599, 1);
//c.lineTo(1, 1);
//c.stroke();
c.translate(300, 1);
FluoCan.renderExpression(c, flow1, '0');
var cfluo = FluoCan.newCan('fluo', 600, 400);
document.body.appendChild(cfluo);
var c = cfluo.getContext("2d");
c.beginPath();
c.moveTo(1, 1);
c.lineTo(1, 399);
c.lineTo(599, 399);
c.lineTo(599, 1);
c.lineTo(1, 1);
c.stroke();
c.scale(0.75, 0.75);
c.translate(4, 200);
c.rotate(-Math.PI/2);
c.fluoVertical = false;
FluoCan.renderExpression(c, flow1, '0');
</script>
</body>
</html>