Skip to content

Instantly share code, notes, and snippets.

@takahashihideki-git
Last active January 31, 2018 21:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save takahashihideki-git/cd2b2f8ce5df75fe94ea to your computer and use it in GitHub Desktop.
Save takahashihideki-git/cd2b2f8ce5df75fe94ea to your computer and use it in GitHub Desktop.
Textwell Action "Diagram"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Diagram</title>
<style>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
-webkit-user-select:none;
}
.container {
position: relative;
margin: 0 auto;
border: 1px solid #ccc;
}
.container .handlerLayer {
position: absolute;
top: 0;
height: 0;
width: 100%;
height: 100%;
}
.container .handler {
position: absolute;
background-color: rgba( 200, 200, 200, 0 );
cursor: move;
}
.container .handler.dragging {
background-color: rgba( 200, 200, 200, 0 );
}
.container .message {
position: absolute;
top: 0;
left: 0;
width: 100%;
box-sizing: border-box;
padding: 0.5em;
background-color: rgba( 0, 0, 0, 0.5 );
color: #eee;
text-align: center;
font-size: 200%;
}
.container img {
width: 100%;
}
.button {
width: 10em;
font-size: 100%;
margin: 1em auto;
padding: 0.5em 1em;
font-family: sans-serif;
text-align: center;
color: #2168FF;
border: 1px solid #2168FF;
border-radius: 20px;
cursor: pointer;
}
.container .resizer {
position: absolute;
bottom: 0;
right: 0;
width: 80px;
height: 80px;
cursor: pointer;
}
.transformer {
position: fixed;
top: 0;
width: 100%;
font-size: 100%;
overflow-y: auto;
opacity: 0;
transition: opacity 0.2s linear 0;
}
.transformer-pannel {
width: 90%;
margin: 0 auto;
padding: 1em 0.5em;
background-color: rgba( 0, 0, 0, 0.8 );
border-radius: 0.5em;
}
.transformer th,
.transformer td {
padding: 0.5em;
}
.transformer th {
padding-right: 1em;
vertical-align: top;
text-align: right;
font-weight: bold;
color: #fff;
}
.transformer .setting-size th {
line-height: 2em;
}
.transformer ul {
margin: 0;
padding: 0;
list-style-type: none;
}
.transformer li {
margin: 0 0.5em 0.5em 0;
padding: 0 0.5em;
display: inline-block;
color: #666;
border: 2px solid #666;
border-radius: 1em;
line-height: 1.4;
cursor: pointer;
}
.transformer li.selected {
color: #fff;
border: 2px solid #fff;
}
.transformer .colors li {
opacity: 0.3;
padding: 0;
width: 1.4em;
height: 1.4em;
border: 2px solid #000;
}
.transformer .colors li.selected {
opacity: 1;
border: 2px solid #fff;
}
.transformer form {
position: relative;
}
.transformer label {
position: absolute;
top: 0;
display: block;
height: 1em;
color: #ccc;
}
.transformer label[for=transrator-width] {
left: 0.4em;
}
.transformer label[for=transrator-height] {
left: 5.7em;
}
.transformer input {
width: 3em;
height: 1em;
padding-left: 1.5em;
border: 1px solid #666;
border-radius: 1em;
font-size: 100%;
}
.transformer .button {
width: auto;
margin-left: 0;
}
@media (max-device-width: 480px) {
.button {
font-size: 400%;
}
.transformer {
position: absolute;
top: 20px;
font-size: 300%;
}
.transformer .button {
padding: 1em;
font-size: 100%;
}
}
</style>
</head>
<body>
<!-- for dev -->
<textarea style="display:none">
1.Responsive
Canvas
2.Handler
3.Shape
4.Connection
5.Transformer
6.Button
7.Parser
3->1.
4->1.draw
4->3.connect
2->3.delegate
3->5.use
6->1.control
1.style.(15,282,326,141,roundedRect,blue,auto)
2.style.(822,135,264,96,roundedRect,blue,auto)
3.style.(454,307,236,96,roundedRect,blue,auto)
4.style.(326,688,324,96,roundedRect,blue,auto)
5.style.(783,509,348,96,roundedRect,blue,auto)
6.style.(57,38,244,96,roundedRect,blue,auto)
7.style.(48,865,242,96,roundedRect,gray,auto)
</textarea>
<!-- for dev -->
<div class="container">
<canvas class="canvas"></canvas>
<div class="handlerLayer"></div>
<img sytle="display:none">
<div class="message" style="display:none">Converted. You can save the diagram as a file.</div>
<div class="resizer"><canvas></canvas></div>
</div>
<div class="button button-imagify">Convert to PNG</div>
<div class="transformer" style="display:none">
<div class="transformer-pannel">
<table>
<tr class="setting setting-color">
<th>Color</th>
<td>
<ul class="colors">
<li class="blue"></li>
<li class="green"></li>
<li class="purple"></li>
<li class="red"></li>
<li class="gray"></li>
<li class="indigo"></li>
<li class="lightGreen"></li>
<li class="deepPurple"></li>
<li class="pink"></li>
<li class="blueGray"></li>
<li class="lightBlue"></li>
<li class="teal"></li>
<li class="brown"></li>
<li class="orange"></li>
<li class="yellow"></li>
<li class="cyan"></li>
<li class="lime"></li>
<li class="amber"></li>
<li class="deepOrange"></li>
<li class="white"></li>
</ul>
</td>
</tr>
<tr class="setting setting-shape">
<th>Shape</th>
<td>
<ul class="shapes">
<li class="rect">Rectangle</li>
<li class="roundedRect">Rounded Rectangle</li>
<li class="circle">Circle</li>
<li class="ellipse">Ellipse</li>
<li class="transparent">Transparent</li>
</ul>
</td>
</tr>
<tr class="setting setting-size">
<th>Size</th>
<td>
<form>
<label for="transrator-width">w</label><input id="transrator-width" class="width" type="text" value="">
<label for="transrator-height">h</label><input id="transrator-height" class="height" type="text" value="">
</form>
</td>
</tr>
<tr class="setting setting-connector">
<th>Connector</th>
<td>
<ul class="connectors">
<li class="auto">auto</li>
<li class="top">top</li>
<li class="right">right</li>
<li class="bottom">bottom</li>
<li class="left">left</li>
</ul>
</td>
</tr>
</table>
<div class="button">OK</div>
</div>
</div>
<script>
const FONTSIZE = 32;
const LINEHEIGHT = 1.4;
const COLORS = {
blue:
{ color: "rgba(33,150,243,1)",
alpha: "rgba(33,150,243,0.2)" },
green:
{ color: "rgba(76,175,80,1)",
alpha: "rgba(76,175,80,0.2)" },
purple:
{ color: "rgba(156,39,176,1)",
alpha: "rgba(156,39,176,0.2)" },
red:
{ color: "rgba(244,67,54,1)",
alpha: "rgba(244,67,54,0.2)" },
gray:
{ color: "rgba(158,158,158,1)",
alpha: "rgba(158,158,158,0.2)" },
indigo:
{ color: "rgba(63,81,181,1)",
alpha: "rgba(63,81,181,0.2)" },
lightGreen:
{ color: "rgba(139,195,74,1)",
alpha: "rgba(139,195,74,0.2)" },
deepPurple:
{ color: "rgba(103,58,183,1)",
alpha: "rgba(103,58,183,0.2)" },
pink:
{ color: "rgba(233,30,99,1)",
alpha: "rgba(233,30,99,0.2)" },
blueGray:
{ color: "rgba(96,125,139,1)",
alpha: "rgba(96,125,139,0.2)" },
lightBlue:
{ color: "rgba(3,169,244,1)",
alpha: "rgba(3,169,244,0.2)" },
teal:
{ color: "rgba(0,150,136,1)",
alpha: "rgba(0,150,136,0.2)" },
brown:
{ color: "rgba(121,85,72,1)",
alpha: "rgba(121,85,72,0.2)" },
orange:
{ color: "rgba(255,152,0,1)",
alpha: "rgba(255,152,0,0.2)" },
yellow:
{ color: "rgba(255,235,59,1)",
alpha: "rgba(255,235,59,0.2)" },
cyan:
{ color: "rgba(0,188,212,1)",
alpha: "rgba(0,188,212,0.2)" },
lime:
{ color: "rgba(205,220,57,1)",
alpha: "rgba(205,220,57,0.2)" },
amber:
{ color: "rgba(255,193,7,1)",
alpha: "rgba(255,193,7,0.2)" },
deepOrange:
{ color: "rgba(255,87,34,1)",
alpha: "rgba(255,87,34,0.2)" },
white:
{ color: "rgba(255,255,255,1)",
alpha: "rgba(255,255,255,0.2)" },
darkGray:
{
color: "rgba( 50, 50, 50, 1 )",
alpha: "rgba( 50, 50, 50, 0.5 )" },
black:
{
color: "rgba( 0, 0, 0, 1 )",
alpha: "rgba( 0, 0, 0, 0.5 )" }
};
// pointing events
const TOUCHABLE = "ontouchstart" in document;
const POINTING = {
move: TOUCHABLE ? "touchmove" : "mousemove",
down: TOUCHABLE ? "touchstart" : "mousedown",
up: TOUCHABLE ? "touchend" : "mouseup"
}
/**
ResponsiveCanvas Class
*/
var ResponsiveCanvas = function ( args ) {
// get base
this.container = args.container;
this.canvas = this.container.querySelector( ".canvas" );
this.context = this.canvas.getContext( '2d' );
// set assets
this.image = this.container.querySelector( "img" );
this.message = this.container.querySelector( ".message" );
this.resizer = this.container.querySelector( ".resizer" );
this.setResizerIcon();
this.shapes = new Array();
this.connections = new Array();
this.handlerLayer = this.container.querySelector( ".handlerLayer" );
// set size
var width = window.innerWidth * 0.9;
var height = width;
if ( args.width ) {
width = args.width;
}
if ( args.height ) {
height = args.height;
}
this.resize( width, height );
var canvas = this;
this.handlerLayer.addEventListener( POINTING.move, function( event ) {
event.preventDefault();
}, true );
this.resizer.addEventListener( POINTING.down, function ( event ) {
event.preventDefault();
var e = TOUCHABLE ? event.changedTouches[ 0 ] : event;
canvas.resizing = true;
canvas.lastResizerPos = {
x: e.screenX,
y: e.screenY
}
}, true );
document.querySelector( "body" ).addEventListener( POINTING.up, function ( event ) {
canvas.resizing = false;
canvas.lastResizerPos = false;
}, true );
document.querySelector( "body" ).addEventListener( POINTING.move, function ( event ) {
var e = TOUCHABLE ? event.changedTouches[ 0 ] : event;
if ( canvas.resizing && canvas.lastResizerPos ) {
var distanceX = e.screenX - canvas.lastResizerPos.x;
var distanceY = e.screenY - canvas.lastResizerPos.y;
// threshold
if ( Math.abs( distanceX ) > canvas.resizingMax || Math.abs( distanceY ) > canvas.resizinMax ) {
return;
}
canvas.resize( canvas.width + distanceX, canvas.height + distanceY );
canvas.lastResizerPos = {
x: e.screenX,
y: e.screenY
}
}
}, true );
}
ResponsiveCanvas.prototype = {
container: null,
canvas: null,
context: null,
width: 0,
height: 0,
handlerLayer: null,
shapes: null,
connections: null,
image: null,
message: null,
resizing: false,
lastResizerPos: null,
resizingMax: 100,
autoAlignMark: {
x: 0,
line: 0,
lineY: [0],
marginX: 20,
marginY: 100,
},
setResizerIcon: function () {
var width = this.resizer.clientWidth;
var height = this.resizer.clientHeight;
var canvas = this.resizer.querySelector( "canvas" );
canvas.setAttribute( "width", width );
canvas.setAttribute( "height", height );
var context = canvas.getContext( '2d' );
var start = width / 2;
var span = start / 3;
context.strokeStyle = COLORS[ "gray" ].color;
context.beginPath();
context.moveTo( width, start );
context.lineTo( start, height );
context.moveTo( width, start + span );
context.lineTo( start + span, height );
context.moveTo( width, start + span * 2 );
context.lineTo( start + span * 2, height );
context.stroke();
},
resize: function ( width, height ) {
this.width = width;
this.height = height;
this.container.style.width = this.width + "px";
this.container.style.height = this.height + "px";
this.container.style.marginTop = this.width / 18 + "px";
this.canvas.setAttribute( "width", this.width );
this.canvas.setAttribute( "height", this.height );
this.refresh();
},
appendShape: function ( shape ) {
this.shapes.push( shape );
// auto align x
var x = this.autoAlignMark.x + this.autoAlignMark.marginX;
if ( x + shape.width > this.width ) {
//line feed
this.autoAlignMark.x = 0;
this.autoAlignMark.line++;
x = this.autoAlignMark.x + this.autoAlignMark.marginX
}
// set x to shape
shape.x = x;
//for next x
this.autoAlignMark.x = x + shape.width;
// auto align y
var y = this.autoAlignMark.lineY[ this.autoAlignMark.line ] + this.autoAlignMark.marginY;
// set y to shape
shape.y = y;
// resize canvas
if ( y + shape.height > this.height ) {
this.resize( this.width, y + shape.height + this.autoAlignMark.marginY )
}
// for next y
if ( ! this.autoAlignMark.lineY[ this.autoAlignMark.line + 1 ] ) {
this.autoAlignMark.lineY[ this.autoAlignMark.line + 1 ] = y + shape.height;
}
else if ( y + shape.height > this.autoAlignMark.lineY[ this.autoAlignMark.line + 1 ] ) {
this.autoAlignMark.lineY[ this.autoAlignMark.line + 1 ] = y + shape.height;
}
},
appendConnection: function ( connection ) {
this.connections.push( connection );
},
refresh: function () {
if ( this.connections.length || this.shapes.length ) {
this.context.clearRect( 0, 0, this.width, this.height );
}
if ( this.connections.length ) {
for ( var i = 0; i < this.connections.length; i++ ) {
this.connections[ i ].refresh();
}
}
if ( this.shapes.length ) {
for ( var i = 0; i < this.shapes.length; i++ ) {
this.shapes[ i ].refresh();
}
}
},
imagify: function () {
this.image.setAttribute( "src", this.canvas.toDataURL() )
this.canvas.style.display = "none";
this.handlerLayer.style.display = "none";
this.resizer.style.display = "none";
this.message.style.display = "block";
this.image.style.display = "inline-block";
},
unimagify: function () {
this.message.style.display = "none";
this.image.style.display = "none";
this.canvas.style.display = "block";
this.handlerLayer.style.display = "block";
this.resizer.style.display = "block";
},
getIndexOfShape: function ( shape ) {
for ( var i = 0; i < this.shapes.length; i++ ) {
if ( shape == this.shapes[ i ] ) {
break;
}
}
return i;
}
};
/**
Handler Class
*/
var Handler = function ( args ) {
this.canvas = args.canvas;
this.shape = args.shape;
this.x = args.x;
this.y = args.y;
this.width = args.width;
this.height = args.height;
this.initialize();
}
Handler.prototype = {
canvas: null,
shape: null,
element: null,
x: 0,
y: 0,
width: 0,
height: 0,
lastPos: null,
dragging: false,
draggingMax: 100,
holding: false,
holdingTime: 1000,
holdingTimer: null,
initialize: function () {
this.element = document.createElement( "div" );
this.element.style.top = this.y + "px";
this.element.style.left = this.x + "px";
this.element.style.width = this.width + "px";
this.element.style.height = this.height + "px";
this.element.setAttribute( "class", "handler" );
this.canvas.handlerLayer.appendChild( this.element );
var handler = this;
this.element.addEventListener( POINTING.down, function ( event ) {
var e = TOUCHABLE ? event.touches[ 0 ] : event;
handler.lastPos = {
x: e.screenX,
y: e.screenY
}
var classes = handler.element.getAttribute( "class" );
handler.element.setAttribute( "class", classes + " dragging" );
handler.dragging = true;
handler.holding = true;
handler.holdingTimer = setTimeout( function () {
if ( handler.holding ) {
handler.holding = false;
handler.dragging = false;
handler.shape.transform();
}
}, handler.holdingTime )
} );
this.element.addEventListener( POINTING.move, function ( event ) {
handler.holding = false;
if ( handler.holdingTimer ) {
clearTimeout( handler.holdingTimer );
}
if ( ! handler.dragging ) {
return;
}
event.preventDefault();
var e = TOUCHABLE ? event.changedTouches[ 0 ] : event;
var x = e.screenX;
var y = e.screenY;
var distanceX = x - handler.lastPos.x;
var distanceY = y - handler.lastPos.y;
// threshold
if ( Math.abs( distanceX ) > handler.draggingMax || Math.abs( distanceY ) > handler.draggingMax ) {
return;
}
var newX = handler.element.offsetLeft + distanceX;
var newY = handler.element.offsetTop + distanceY;
if ( newX < 0 ) {
newX = 0;
}
if ( newX + handler.width > handler.canvas.width ) {
newX = handler.canvas.width - handler.width;
}
if ( newY < 0 ) {
newY = 0;
}
if ( newY + handler.height > handler.canvas.height ) {
newY = handler.canvas.height - handler.height;
}
handler.lastPos = {
x: x,
y: y
}
handler.move( newX, newY );
}, true );
this.element.addEventListener( POINTING.up, function ( event ) {
var classes = handler.element.getAttribute( "class" );
handler.element.setAttribute( "class", classes.replace( / dragging/, "" ) );
handler.dragging = false;
handler.holding = false;
if ( handler.holdingTimer ) {
clearTimeout( handler.holdingTimer );
}
} );
},
move: function ( x, y ) {
this.element.style.left = x + "px";
this.element.style.top = y + "px";
this.shape.move( x, y );
},
setSize: function ( width, height ) {
this.width = width;
this.height = height;
this.element.style.width = this.width + "px";
this.element.style.height = this.height + "px";
}
};
/**
Shape Class
*/
var Shape = function ( args ) {
this.canvas = args.canvas;
this.transformer = args.transformer;
this.drawer = args.drawer;
if ( args.text ) {
this.text = args.text;
var sizeByText = this.getSizeByText();
this.width = sizeByText.width;
this.height = sizeByText.height;
}
if ( this.drawer == "circle" ) {
this.squarize();
}
// set size by arguments
if ( args.width ) {
this.width = args.width;
}
if ( args.height ) {
this.height = args.height;
}
this.color = args.color;
this.canvas.appendShape( this );
}
Shape.prototype = {
canvas: null,
transformer: null,
width: 100,
height: 100,
x: 0,
y: 0,
handler: null,
text: "",
fontSize: FONTSIZE,
lineHeight: LINEHEIGHT,
textBoxWidth: 0,
textBoxHeight: 0,
color: "",
connector: "auto",
drawer: "",
drawers: {
transparent: function () {
return;
},
rect: function ( shape ) {
var ctx = shape.canvas.context;
var x = shape.x;
var y = shape.y;
var width = shape.width;
var height = shape.height;
var color = shape.color;
var radius = 50;
ctx.strokeStyle = COLORS[ "darkGray" ].color;
ctx.beginPath();
ctx.moveTo( x, y );
ctx.lineTo( x + width, y );
ctx.lineTo( x + width, y + height );
ctx.lineTo( x, y + height );
ctx.closePath();
if ( color && COLORS[ color ] ) {
ctx.fillStyle = COLORS[ color ].alpha;
ctx.fill();
}
ctx.stroke();
},
roundedRect: function ( shape ) {
var ctx = shape.canvas.context;
var x = shape.x;
var y = shape.y;
var width = shape.width;
var height = shape.height;
var color = shape.color;
var radius = 30;
ctx.strokeStyle = COLORS[ "darkGray" ].color;
ctx.beginPath();
ctx.moveTo( x + radius, y );
ctx.lineTo( x + width - radius, y );
ctx.arc( x + width - radius, y + radius, radius, Math.PI*1.5, 0, false );
ctx.lineTo( x + width, y + height - radius );
ctx.arc( x + width - radius, y + height - radius, radius, 0, Math.PI*0.5, false );
ctx.lineTo( x + radius, y + height );
ctx.arc( x + radius, y + height - radius, radius, Math.PI*0.5, Math.PI, false );
ctx.lineTo( x, y + radius );
ctx.arc( x + radius, y + radius, radius, Math.PI, Math.PI*1.5, false );
ctx.closePath();
if ( color && COLORS[ color ] ) {
ctx.fillStyle = COLORS[ color ].alpha;
ctx.fill();
}
ctx.stroke();
},
circle: function ( shape ) {
shape.drawers[ "ellipse" ]( shape );
},
ellipse: function ( shape ) {
var x = shape.x + shape.width / 2;
var y = shape.y + shape.height / 2;
var ctx = shape.canvas.context;
ctx.strokeStyle = COLORS[ "darkGray" ].color;
var axisRatio = shape.width / shape.height;
ctx.moveTo( x, y );
ctx.beginPath();
ctx.save();
ctx.scale( axisRatio, 1 );
ctx.arc(
x * ( 1 / axisRatio ),
y,
shape.width * ( 1 / axisRatio ) / 2,
0,
Math.PI*2,
false
);
ctx.closePath();
if ( shape.color && COLORS[ shape.color ] ) {
ctx.fillStyle = COLORS[ shape.color ].alpha;
ctx.fill();
}
ctx.restore();
ctx.stroke();
}
},
setHandler: function () {
this.handler = new Handler( {
canvas: this.canvas,
shape: this,
x: this.x,
y: this.y,
width: this.width,
height: this.height
} );
},
getFontStyle: function () {
return "bold " + this.fontSize + "px sans-serif";
},
getSizeByText: function () {
// set size by text
var ctx = this.canvas.context;
ctx.font = this.getFontStyle();
var lines = this.text.split( /\n/ );
var lineWidth = 0;
for ( var i = 0; i < lines.length; i++ ) {
lineWidth = ( ctx.measureText( lines[ i ] ) ).width;
if ( lineWidth > this.textBoxWidth ) {
this.textBoxWidth = lineWidth;
}
}
this.textBoxHeight = this.fontSize;
for ( var i = 1; i < lines.length; i++ ) {
this.textBoxHeight += this.fontSize * this.lineHeight
}
// margin-top,bottom: fontSize; margin-left,right: fontSize * 2
return {
width: Math.round( this.textBoxWidth + FONTSIZE * 4 ),
height: Math.round( this.textBoxHeight + FONTSIZE * 2 )
};
},
draw: function ( x, y ) {
if ( x ) {
this.x = x;
}
if ( y ) {
this.y = y;
}
this.drawers[ this.drawer ]( this );
this.writeText();
if ( ! this.handler ) {
this.setHandler();
}
},
writeText: function () {
if ( this.text ) {
var ctx = this.canvas.context;
ctx.font = this.getFontStyle();
ctx.fillStyle = "rgb( 0, 0, 0 )";
var lines = this.text.split( /\n/ );
var fontBaseLineRatio = 0.8;
// margin-top,bottom: fontSize; margin-left,right: fontSize * 2
var x = this.x;
var y = this.y + FONTSIZE * fontBaseLineRatio;
var maxWidth = this.width;
if ( this.textBoxWidth < this.width ) {
x = this.x + ( this.width - this.textBoxWidth ) / 2;
}
if ( this.textBoxHeight < this.height ) {
y = this.y + this.fontSize * fontBaseLineRatio + ( this.height - this.textBoxHeight ) / 2;
}
for ( var i = 0; i < lines.length; i++ ) {
ctx.fillText( lines[ i ], x, y + this.fontSize * this.lineHeight * i, maxWidth );
}
}
},
move: function ( x, y ) {
this.x = x;
this.y = y;
this.canvas.refresh();
},
refresh: function () {
this.draw( this.x, this.y );
},
resize: function ( width, height ) {
this.setSize( width, height );
this.canvas.refresh();
},
setSize: function ( width, height ) {
this.width = width;
this.height = height;
if ( this.handler ) {
this.handler.setSize( this.width, this.height );
}
},
setConnector: function ( type ) {
this.connector = type; // top, right, bottom, left
this.canvas.refresh();
},
getConnector: function () {
var connectors = new Object();
connectors[ "top" ] = { x: this.x + this.width / 2, y: this.y },
connectors[ "right" ] = { x: this.x + this.width, y: this.y + this.height / 2 },
connectors[ "bottom" ] = { x: this.x + this.width / 2, y: this.y + this.height },
connectors[ "left" ] = { x: this.x, y: this.y + this.height / 2 }
if ( this.connector == "auto" ) {
return [
connectors[ "top" ], connectors[ "right" ], connectors[ "bottom" ], connectors[ "left" ]
];
}
else {
return [ connectors[ this.connector ] ];
}
},
changeColor: function ( color ) {
this.color = color;
this.canvas.refresh();
},
changeDrawer: function ( drawer ) {
if ( drawer == "circle" ) {
this.squarize();
}
if ( this.drawer == "circle" ) {
var sizeByText = this.getSizeByText();
this.setSize( sizeByText.width, sizeByText.height );
}
this.drawer = drawer;
this.canvas.refresh();
},
transform: function () {
this.transformer.open( this );
},
squarize: function () {
if ( this.width != this.height ) {
if ( this.width < this.height ) {
this.setSize( this.height, this.height );
}
else {
this.setSize( this.width, this.width );
}
}
}
};
/**
Connection Class
*/
var Connection = function ( args ) {
this.canvas = args.canvas;
this.canvas.appendConnection( this );
this.a = args.a;
this.b = args.b;
this.drawer = this.drawers[ args.drawer ];
this.text = args.text;
if ( this.text ) {
var lines = this.text.split( /\n/ );
var ctx = this.canvas.context;
this.textBoxHeight = this.fontSize;
for ( var i = 1; i < lines.length; i++ ) {
this.textBoxHeight += this.fontSize * this.lineHeight
}
this.textBoxWidth = 0;
var lineWidth = 0;
for ( var i = 0; i < lines.length; i++ ) {
lineWidth = ( ctx.measureText( lines[ i ] ) ).width;
if ( lineWidth > this.textBoxWidth ) {
this.textBoxWidth = lineWidth;
}
}
}
this.arrow = args.arrow;
}
Connection.prototype = {
canvas: null,
a: null,
b: null,
start: null,
end: null,
text: "",
fontSize: FONTSIZE,
lineHeight: LINEHEIGHT,
textBoxWidth: 0,
textBoxHeight: 0,
arrow: false,
drawer: null,
drawers: {
line: function ( connection ) {
var ctx = connection.canvas.context;
var start = connection.start;
var end = connection.end;
ctx.strokeStyle = COLORS[ "gray" ].color;
ctx.beginPath();
ctx.moveTo( start.x, start.y );
ctx.lineTo( end.x, end.y );
ctx.stroke();
if ( connection.arrow ) {
// make arrow http://www.dbp-consulting.com/tutorials/canvas/CanvasArrow.html
// arrow param
var d = 15;
var angle = 100;
// calculate the angle of the line
var lineangle = Math.atan2( end.y - start.y, end.x - start.x );
// h is the line length of a side of the arrow head
var h = Math.abs( d / Math.cos( angle ) );
// get arrow top serif
var angle1 = lineangle + Math.PI + angle;
var topx = end.x + Math.cos( angle1 ) * h;
var topy = end.y + Math.sin( angle1 ) * h;
// get arrow bottom serif
var angle2 = lineangle + Math.PI - angle;
var botx = end.x + Math.cos( angle2 ) * h;
var boty = end.y + Math.sin( angle2 ) * h;
// draw arrow
ctx.beginPath();
ctx.moveTo( end.x, end.y );
ctx.lineTo( topx, topy );
ctx.lineTo( botx, boty );
ctx.closePath();
ctx.fillStyle = COLORS[ "black" ].alpha;
ctx.fill();
}
}
},
getFontStyle: function () {
return this.fontSize + "px sans-serif";
},
getDistance: function ( pos1, pos2 ) {
return Math.sqrt( Math.pow( pos1.x - pos2.x, 2 ) + Math.pow( pos1.y - pos2.y, 2 ) )
},
sortConnections: function ( con1, con2 ) {
return con1.distance - con2.distance;
},
draw: function () {
var connections = new Array();
var con1 = this.a.getConnector();
var con2 = this.b.getConnector();
for ( var i = 0; i < con1.length; i++ ) {
for ( var j = 0; j < con2.length; j++ ) {
connections.push( {
con1: con1[ i ],
con2: con2[ j ],
distance: this.getDistance( con1[ i ], con2[ j ] )
} );
}
}
connections.sort( this.sortConnections );
this.start = connections[ 0 ].con1;
this.end = connections[ 0 ].con2;
this.drawer( this );
this.writeText();
},
writeText: function () {
if ( this.text ) {
var ctx = this.canvas.context;
ctx.font = this.getFontStyle();
ctx.fillStyle = COLORS[ "black" ].color;
var lines = this.text.split( /\n/ );
var x = this.start.x < this.end.x ? this.start.x : this.end.x;
var y = this.start.y < this.end.y ? this.start.y : this.end.y;
var width = Math.abs( this.start.x - this.end.x );
var height = Math.abs( this.start.y - this.end.y );
var center = {
x: x + width / 2,
y: y + height / 2
}
var x = center.x - this.textBoxWidth / 2;
var y = center.y;
if ( this.textBoxHeight < height ) {
y = center.y + this.fontSize - this.textBoxHeight / 2;
}
for ( var i = 0; i < lines.length; i++ ) {
ctx.fillText( lines[ i ], x, y + this.fontSize * this.lineHeight * i );
}
}
},
refresh: function () {
this.draw();
}
};
/**
Button Class
*/
var ImagifyButton = function ( args ) {
this.element = args.element;
this.canvas = args.canvas;
var button = this;
this.element.addEventListener( POINTING.down, function () {
button.element.style.backgroundColor = "#eee";
}, false );
this.element.addEventListener( POINTING.up, function () {
button.element.style.backgroundColor = "#fff";
button.execute();
}, false );
}
ImagifyButton.prototype = {
element: null,
canvas: null,
canImagify: true,
execute: function () {
if ( this.canImagify ) {
this.canvas.imagify();
this.element.innerHTML = "Edit"
this.canImagify = false;
}
else {
this.canvas.unimagify();
this.element.innerHTML = "Convert to PNG"
this.canImagify = true;
}
}
}
/**
Transformer Class
*/
var Transformer = function ( args ) {
this.container = args.container;
var transformer = this;
// set form
var colors = this.container.querySelectorAll( ".colors li" );
for ( var i = 0; i < colors.length; i++ ) {
colors[ i ].style.backgroundColor = COLORS[ colors[ i ].getAttribute( "class" ) ].color;
}
var settings = this.container.querySelectorAll( ".setting" );
( function () {
for ( var i = 0; i < settings.length; i++ ) { ( function () {
var section = settings[ i ].querySelector( "ul" );
if ( section ) {
var options = settings[ i ].querySelectorAll( "li" );
for ( var j = 0; j < options.length; j++ ) { ( function () {
var option = options[ j ];
option.addEventListener( POINTING.down, function () {
transformer.pushingButton = true;
} );
option.addEventListener( POINTING.move, function () {
transformer.pushingButton = false;
} );
option.addEventListener( POINTING.up, function () {
if ( transformer.pushingButton ) {
transformer.select( section, options, option );
}
} );
} )() }
}
} )() }
} )();
var inputs = this.container.querySelectorAll( "input" );
( function () {
for ( var i = 0; i < inputs.length; i++ ) { ( function () {
var input = inputs[ i ];
input.addEventListener( "keyup", function () {
transformer.input( inputs, input );
} );
} )() }
} )();
var button = this.container.querySelector( ".button" );
button.addEventListener( POINTING.down, function () {
button.style.backgroundColor = "#eee";
transformer.pushingButton = true;
}, false );
button.addEventListener( POINTING.up, function () {
if ( transformer.pushingButton ) {
button.style.backgroundColor = "transparent";
transformer.pushingButton = false;
transformer.close();
}
}, false );
}
Transformer.prototype = {
container: null,
shape: null,
pushingButton: false,
open: function ( shape ) {
this.shape = shape;
this.initialize();
this.setColor();
this.setShape();
this.setSize();
this.setConnector();
this.container.style.display = "block";
var transformer = this;
setTimeout( function () {
transformer.container.style.opacity = "1";
}, 100 );
},
initialize: function () {
var selecteds = this.container.querySelectorAll( "li.selected" );
for ( var i = 0; i < selecteds.length; i++ ) {
selecteds[ i ].setAttribute( "class", selecteds[ i ].getAttribute( "class" ).replace( / selected/, "" ) );
}
},
setColor: function () {
// set color
var option = this.container.querySelector( "li." + this.shape.color );
if ( option ) {
option.setAttribute( "class", option.getAttribute( "class" ) + " selected" );
}
},
setShape: function () {
// set shape
var option = this.container.querySelector( "li." + this.shape.drawer );
if ( option ) {
option.setAttribute( "class", option.getAttribute( "class" ) + " selected" );
}
},
setSize: function () {
// set size
this.container.querySelector( "input.width" ).value = this.shape.width;
this.container.querySelector( "input.height" ).value = this.shape.height;
},
setConnector: function () {
// set connector
var option = this.container.querySelector( "li." + this.shape.connector );
if ( option ) {
option.setAttribute( "class", option.getAttribute( "class" ) + " selected" );
}
},
select: function ( section, options, selected ) {
for ( var i = 0; i < options.length; i++ ) {
options[ i ].setAttribute( "class", options[ i ].getAttribute( "class" ).replace( / selected/, "" ) );
}
selected.setAttribute( "class", selected.getAttribute( "class" ) + " selected" );
var value = selected.getAttribute( "class" ).replace( / selected/, "" );
if ( section.getAttribute( "class" ) == "colors" ) {
this.shape.changeColor( value );
}
if ( section.getAttribute( "class" ) == "shapes" ) {
this.shape.changeDrawer( value );
this.setSize();
}
if ( section.getAttribute( "class" ) == "connectors" ) {
this.shape.setConnector( value );
}
},
input: function ( inputs, input ) {
var values = new Object();
for ( var i = 0; i < inputs.length; i++ ) {
values[ inputs[ i ].getAttribute( "class" ) ] = Number( inputs[ i ].value );
}
if ( values.width && values.height ) {
if ( this.shape.drawer == "circle" ) {
if ( inputs[ 0 ] != input ) {
inputs[ 0 ].value = input.value;
}
if ( inputs[ 1 ] != input ) {
inputs[ 1 ].value = input.value;
}
values.width = Number( input.value );
values.height = Number( input.value );
}
this.shape.resize( values.width, values.height );
}
},
close: function () {
this.container.style.display = "none";
this.container.style.opacity = "0";
}
};
/**
Parser Class
*/
var Parser = function () {
}
Parser.prototype = {
text: "",
parse: function ( text ) {
this.text = text;
var tokens = new Array();
var splits = this.text.split( /\n\n+/ );
for ( var i = 0; i < splits.length; i++ ) {
if ( splits[ i ].match( /^\s.*$/ ) ) {
continue;
}
tokens.push( splits[ i ].replace( /\n$/, "" ) );
}
var connections = new Array();
for ( var i = 0; i < tokens.length; i++ ) { ( function () {
if ( tokens[ i ].match( /^([0-9]+)(->*)([0-9]+)\.([\s\S]*)/ ) ) {
var from = RegExp.$1;
var to = RegExp.$3;
var arrow = false;
var text = RegExp.$4;
if ( RegExp.$2.match( />/ ) ) {
arrow =true
}
connections.push( {
from: from,
to: to,
arrow: arrow,
text: text
} );
delete tokens[ i ];
}
} )() }
var styles = new Array();
for ( var i = 0; i < tokens.length; i++ ) { ( function () {
if ( tokens[ i ] && tokens[ i ].match( /^[0-9]+\.style\.\((.+)\)$/ ) ) {
var style = RegExp.$1.split( /,/ );
styles.push( style );
delete tokens[ i ];
}
} )() }
var shapes = new Array();
for ( var i = 0; i < tokens.length; i++ ) { ( function () {
if ( tokens[ i ] && tokens[ i ].match( /^[0-9]+\.([\s\S]+)$/ ) ) {
var shape = RegExp.$1;
shapes.push( shape.replace( /^\s+/, "" ).replace( /\s+$/, "" ) );
}
} )() }
var parsed = {
shapes: shapes,
connections: connections,
styles: styles
};
return parsed;
},
serialize: function ( canvas ) {
var serialized = "";
// shapes
var shapes = canvas.shapes;
var shapeTexts = new Array();
for ( var i = 0; i < shapes.length; i++ ) {
shapeTexts.push( Number( i ) + 1 + "." + shapes[ i ].text );
}
serialized = shapeTexts.join( "\n\n" );
// connections
var connections = canvas.connections;
var connectionTexts = new Array();
if ( connections.length ) {
serialized += "\n\n";
}
for ( var i = 0; i < connections.length; i++ ) {
var startIndex = canvas.getIndexOfShape( connections[ i ].a ) + 1;
var endIndex = canvas.getIndexOfShape( connections[ i ].b ) + 1;
var line = "-";
if ( connections[ i ].arrow ) {
line = "->";
}
connectionTexts.push( startIndex + line + endIndex + "." + connections[ i ].text );
}
serialized += connectionTexts.join( "\n\n" );
// styles
serialized += "\n\n";
var styleTexts = new Array();
for ( var i = 0; i < shapes.length; i++ ) {
var shape = shapes[ i ];
styleTexts.push(
Number( i + 1 ) + ".style.("
+ shape.x + ","
+ shape.y + ","
+ shape.width + ","
+ shape.height + ","
+ shape.drawer + ","
+ shape.color + ","
+ shape.connector
+ ")"
);
}
serialized += styleTexts.join( "\n\n" );
return serialized;
}
};
/**
Main
*/
var parser = new Parser();
var canvas;
var initialize = function () {
try {
// textwell
T.closelets( [
{
title: 'Replace Diagram Data',
fn: function( arg ) { T( 'replaceCurrent', { text: parser.serialize( canvas ) } ) },
arg: {
parser: parser,
canvas: canvas
}
}
] );
input = parser.parse( T.current );
} catch ( e ) {
// for dev
var input = parser.parse( document.querySelector( "textarea" ).value );
}
// Make Canvas
canvas = new ResponsiveCanvas( {
container: document.querySelector( ".container" ),
} );
// Make Imagify Button
var button = new ImagifyButton( {
element: document.querySelector( ".button-imagify" ),
canvas: canvas
} );
// Make Transformer
var transformer = new Transformer( {
container: document.querySelector( ".transformer" )
} );
// Make Shapes and Connections
if ( input.shapes.length ) {
for ( var i = 0; i < input.shapes.length; i++ ) {
( new Shape( {
canvas: canvas,
transformer: transformer,
drawer: "roundedRect",
color: "blue",
text: input.shapes[ i ]
} ) ).draw();
}
}
if ( input.connections.length ) {
for ( var i = 0; i < input.connections.length; i++ ) {
( new Connection( {
canvas: canvas,
transformer: transformer,
drawer: "line",
a: canvas.shapes[ Number( input.connections[ i ].from ) - 1 ],
b: canvas.shapes[ Number( input.connections[ i ].to ) - 1 ],
arrow: input.connections[ i ].arrow,
text: input.connections[ i ].text
} ) ).draw();
}
}
// Set Style to Shapes
if ( input.styles.length ) {
var maxWidth = canvas.width;
var maxHeight = canvas.height;
for ( var i = 0; i < input.styles.length; i++ ) { ( function () {
var shape = canvas.shapes[ i ];
var style = input.styles[ i ];
// x, y
shape.handler.move( Number( style[ 0 ] ), Number( style[ 1 ] ) );
// width, height
shape.resize( Number( style[ 2 ] ), Number( style[ 3 ] ) );
// shape
shape.changeDrawer( style[ 4 ] );
// color
shape.changeColor( style[ 5 ] );
// connector
shape.setConnector( style[ 6 ] );
var xPlusWidth = Number( style[ 0 ] ) + Number( style[ 2 ] );
if ( xPlusWidth > maxWidth ) {
maxWidth = xPlusWidth;
}
var yPlusHeight = Number( style[ 1 ] ) + Number( style[ 3 ] );
if ( yPlusHeight > maxHeight ) {
maxHeight = yPlusHeight;
}
} )() }
if ( maxWidth != canvas.width ) {
maxWidth += 20;
}
if ( maxHeight != canvas.height ) {
maxHeight += 20;
}
canvas.resize( maxWidth, maxHeight );
}
}
// User Command
var serialize = function () {
console.log( parser.serialize( canvas ) );
}
// Onload
window.onload = function () {
if ( window.webkit ) {
window.webkit.messageHandlers.initT.postMessage( "initialize" );
}
else {
// dev
initialize();
}
}
</script>
<!-- /* file:dialog.html */ -->
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment