easyUI1.5+HTML5+canvas+javascript实现B/S工作流引擎:(一),浏览器客户端

最近公司需要开发一个工作流引擎,现有的流行的引擎框架都是基于桌面客户端的,B/S的基本没有,为了满足公司的业务需要,决定开发一个基于B/S的工作流引擎,

   1 <!DOCTYPE html>
   2 <html>
   3 <head>
   4 <meta charset="UTF-8">
   5 <title>流程图</title>
   6 <link rel="stylesheet" type="text/css"
   7     href="res/css/easyUI/themes/metro-green/easyui.css">
   8 <link rel="stylesheet" type="text/css"
   9     href="res/css/easyUI/themes/icon.css">
  10 <link rel="stylesheet" type="text/css"
  11     href="res/css/easyUI/themes/demo.css">
  12 <link rel="stylesheet" type="text/css"
  13     href="res/css/easyUI/themes/color.css">
  14 <script type="text/javascript" src="res/js/jquery.min.js"></script>
  15 <script type="text/javascript" src="res/js/jquery.easyui.min.js"></script>
  16 <script type="text/javascript" src="res/js/jquery.portal.js"></script>
  17 <script type="text/javascript" src="res/js/easyui-lang-zh_CN.js"></script>
  18 </style>
  19 </head>
  20 <body>
  21     <div class="workflow-diagram" style="width: 800px; height: 600px;">
  22 
  23     </div>
  24     <script type="text/javascript">
  25         "use strict";
  26         $(function(){
  27             /*
  28              *定义抽象类
  29              */
  30             function Shape(text) {
  31                 this.text = text;
  32             }
  33             //定义抽象方法
  34             Shape.prototype.draw = null;
  35             //定义抽象方法
  36             Shape.prototype.move = null;
  37             //定义默认方法
  38             Shape.prototype.isInner = function(x, y) {
  39                 return false;
  40             };
  41             //定义实例方法
  42             Shape.prototype.getText = function() {
  43                 return this.text;
  44             };
  45             //定义实例方法
  46             Shape.prototype.setText = function(text) {
  47                 this.text = text;
  48             };
  49 
  50             /*
  51              *工具函数
  52              */
  53             //创建2D上下文
  54             function createContext2d(canvas) {
  55                 if (canvas.getContext) {
  56                     var context2d = canvas.getContext('2d');
  57                     //半透明蓝色描边色
  58                     context2d.strokeStyle = '#b1c242';
  59                     //context2d.fillStyle = '#b1c242';
  60                     context2d.linewidth=2;
  61                     context2d.lineJoin='round';
  62                     //字体属性
  63                     context2d.font = 'bold 14px Arial';
  64                     context2d.textAlign = 'center';
  65                     context2d.textBaseline = 'middle';
  66                 } else
  67                     throw new Error('不支持2D绘图.');
  68             }
  69             //绘制图像
  70             function drawImage(context2d,x,y,w,h)
  71             {
  72                 context2d.drawImage(WorkflowEngine.image,x,y,w,h);
  73             }
  74             //获取文本宽
  75             function getTextWidth(context2d, text) {
  76                 return context2d.measureText(text).width;
  77             }
  78             //绘制圆
  79             function drawCircle(context2d, x, y, radius) {
  80                 //开始一个路径
  81                 context2d.beginPath();
  82                 //画圆:圆心,半径,起始弧度,终止弧度,顺时针绘制
  83                 context2d.arc(x, y, radius, 0, 2 * Math.PI, false);
  84                 //画十字线
  85                 context2d.moveTo(x - radius, y);
  86                 context2d.lineTo(x + radius, y);
  87                 context2d.moveTo(x, y - radius);
  88                 context2d.lineTo(x, y + radius);
  89                 //描边路径
  90                 context2d.stroke();
  91             }
  92             //绘制文本
  93             function drawText(context2d, x, y, text) {
  94                 context2d.strokeText(text, x, y);
  95             }
  96             //绘制矩形
  97             function drawRect(context2d, x, y, width, height) {
  98                 context2d.strokeRect(x, y, width, height);
  99             }
 100             //绘制连接线
 101             function drawLine(context2d, x0, y0, x1, y1) {
 102                 //开始一个路径
 103                 context2d.beginPath();
 104                 //画直线
 105                 context2d.moveTo(x0, y0);
 106                 context2d.lineTo(x1, y1);
 107                 //描边路径
 108                 context2d.stroke();
 109             }
 110             //绘制一个箭头
 111             function drawArrow(context2d, x0, y0, x1, y1) {
 112                 //总长
 113                 var len=Math.sqrt((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1));
 114                 var dx=x0-x1;
 115                 var dy=y0-y1;
 116                 var x=dx*10/len+x1;
 117                 var y=dy*10/len+y1;
 118                 //开始一个路径
 119                 context2d.beginPath();
 120                 //画直线
 121                 context2d.moveTo(x1, y1);
 122                 //逆时针旋转30度
 123                 var x2 = (x - x1) * Math.cos(Math.PI/6) - (y - y1) * Math.sin(Math.PI/6) + x1;
 124                 var y2 = (x - x1) * Math.sin(Math.PI/6) + (y - y1) * Math.cos(Math.PI/6) + y1;
 125                 context2d.lineTo(x2, y2);
 126                 x2 = (x - x1) * Math.cos(Math.PI*11/6) - (y - y1) * Math.sin(Math.PI*11/6) + x1;
 127                 y2 = (x - x1) * Math.sin(Math.PI*11/6) + (y - y1) * Math.cos(Math.PI*11/6) + y1;
 128                 context2d.lineTo(x2, y2);
 129                 context2d.lineTo(x1, y1);
 130                 //描边路径
 131                 context2d.fill();
 132                 
 133             }
 134             //绑定事件给canvas
 135             function bindEvents(canvas) {
 136                 //鼠标移动
 137                 canvas.mousemove(function(e) {
 138                     WorkflowEngine.onMousemove(e);
 139 
 140                 });
 141                 //鼠标按下
 142                 canvas.mousedown(function(e) {
 143                     WorkflowEngine.onMousedown(e);
 144 
 145                 });
 146                 //鼠标松开
 147                 canvas.mouseup(function(e) {
 148                     WorkflowEngine.onMouseup(e);
 149 
 150                 });
 151                 //鼠标离开
 152                 canvas.mouseleave(function(e) {
 153                     WorkflowEngine.onMouseup(e);
 154 
 155                 });
 156             }
 157 
 158             /*
 159              *定义一个单例对象:WorkflowEngine
 160              */
 161             var WorkflowEngine = {};
 162             WorkflowEngine.Dragger={};
 163             //定义事件
 164             WorkflowEngine.onMouseup = function(event) {
 165                 WorkflowEngine.readyDrag = false;
 166             };
 167             //定义事件
 168             WorkflowEngine.onMousedown = function(event) {
 169                 var mustRepaint = false;
 170                 //拾取当前的Activity or Actor
 171                 var pick = WorkflowEngine.pick(event);
 172                 if(WorkflowEngine.hitTopEar(event.offsetX,event.offsetY))
 173                 {
 174                     WorkflowEngine.readyAddNewActor=true;
 175                 }
 176                 else if(WorkflowEngine.hitRightEar(event.offsetX,event.offsetY))
 177                 {
 178                     WorkflowEngine.readyLink=true;
 179                 }
 180                 else if(WorkflowEngine.hitActivity(pick))
 181                 {
 182                     if(WorkflowEngine.readyLink)
 183                     {
 184                         var arrow=new Arrow(WorkflowEngine.currentChecked().id(),pick.id());
 185                         WorkflowEngine.workflow.add('Arrow',arrow);
 186                         WorkflowEngine.readyLink=false;
 187                         WorkflowEngine.cursor=null;
 188                         mustRepaint=true;
 189                     }
 190                     
 191                 }
 192                 else if(WorkflowEngine.hitBlank(pick))
 193                 {
 194                     if(WorkflowEngine.readyAddNewActor)
 195                         {
 196                         var actor=new Actor(WorkflowEngine.currentChecked().id(),
 197                                 WorkflowEngine.context2d, '参与者',
 198                                 event.offsetX,event.offsetY);
 199                         WorkflowEngine.workflow.add('Actor',actor);
 200                         WorkflowEngine.readyAddNewActor=false;
 201                         WorkflowEngine.cursor=null;
 202                         mustRepaint=true;
 203                                                 
 204                         }
 205                     else if(WorkflowEngine.readyAddNewActivity)
 206                         {
 207                         var activity=new Activity(WorkflowEngine.workflow.id(),
 208                                 WorkflowEngine.context2d, '新建节点',event.offsetX,event.offsetY);
 209                         WorkflowEngine.workflow.add('Activity',activity);
 210                         WorkflowEngine.readyAddNewActivity=false;
 211                         WorkflowEngine.cursor=null;
 212                         mustRepaint=true;
 213                         }
 214                     else
 215                         {
 216                         var checked=WorkflowEngine.currentChecked();
 217                         //让先前的当前节点正常化
 218                         if (checked) {
 219                             checked.setNormaled();
 220                             WorkflowEngine.currentChecked(null);
 221                             mustRepaint = true;
 222                         }
 223                         if(WorkflowEngine.readyLink)
 224                             {
 225                             WorkflowEngine.readyLink=false;
 226                             mustRepaint = true;
 227                             }
 228                         
 229                         if(WorkflowEngine.readyAddNewActivity)
 230                             {
 231                             WorkflowEngine.readyAddNewActivity=false;
 232                             mustRepaint = true;
 233                             }
 234                         
 235                         if(WorkflowEngine.readyAddNewActor)
 236                             {
 237                             WorkflowEngine.readyAddNewActor=false;
 238                             mustRepaint = true;
 239                             }
 240                         WorkflowEngine.cursor=null;
 241                         }
 242                 }
 243                 if(WorkflowEngine.hit(pick))
 244                     {
 245                     WorkflowEngine.readyDrag=true;
 246                     WorkflowEngine.Dragger.x = event.offsetX;
 247                     WorkflowEngine.Dragger.y = event.offsetY;
 248                     var checked = WorkflowEngine.currentChecked();
 249                     //让先前的当前节点正常化
 250                     if (checked && checked!=pick) {
 251                         checked.setNormaled();
 252                         WorkflowEngine.currentChecked(null);
 253                         mustRepaint = true;
 254                     }
 255                     //当前节点被选中
 256                     if (!pick.isPicked()) {
 257                         pick.setPicked();
 258                         WorkflowEngine.currentFocused(null);
 259                         WorkflowEngine.currentChecked(pick);
 260                         mustRepaint = true;
 261                     }
 262                     }
 263                 if (mustRepaint) {
 264                     WorkflowEngine.repaint();
 265                 }
 266                 
 267             };
 268             
 269             /*
 270              *定义事件,处理两件事:1,拖动.2,焦点.
 271              */
 272             WorkflowEngine.onMousemove = function(event) {
 273                  var mustRepaint = false;
 274                  if(WorkflowEngine.readyDrag)
 275                      {
 276                      var checked = WorkflowEngine.currentChecked();
 277                      if (checked) {
 278                             //计算x,y的改变量
 279                             var dx = event.offsetX - WorkflowEngine.Dragger.x;
 280                             var dy = event.offsetY - WorkflowEngine.Dragger.y;
 281                             //修改当前位置
 282                             WorkflowEngine.Dragger.x = event.offsetX;
 283                             WorkflowEngine.Dragger.y = event.offsetY;
 284                             //移动
 285                             WorkflowEngine.workflow.move(dx, dy);
 286                             mustRepaint = true;
 287                         }
 288                      }
 289                  else if(WorkflowEngine.readyAddNewActivity)
 290                      {
 291                      this.cursor=new Cursor(WorkflowEngine.workflow,event.offsetX,event.offsetY,'Activity');
 292                      mustRepaint = true;
 293                      }
 294                  else if(WorkflowEngine.readyAddNewActor)
 295                      {
 296                      this.cursor=new Cursor(WorkflowEngine.workflow,event.offsetX,event.offsetY,'Actor');
 297                      mustRepaint = true;
 298                      }
 299                 
 300                 else if(WorkflowEngine.readyLink)
 301                  {
 302                      this.cursor=new Cursor(WorkflowEngine.workflow,event.offsetX,event.offsetY,'Arrow');
 303                      mustRepaint = true;
 304                 }
 305               //移动
 306                 else {
 307                     var pick = WorkflowEngine.pick(event);
 308                     //在某个节点上移动或者进入
 309                     if (pick) {
 310                         var foucused = WorkflowEngine.currentFocused();
 311                         //如果焦点不在当前节点上
 312                         if (foucused && foucused!=pick) {
 313                             foucused.setNormaled();
 314                             WorkflowEngine.currentFocused(null);
 315                             mustRepaint = true;
 316     
 317                         }
 318                         if (pick.isNormaled()) {
 319                             pick.setFocused();
 320                             WorkflowEngine.currentFocused(pick);
 321                             mustRepaint = true;                                            
 322                         }
 323                     }
 324                     //在空白处移动
 325                     else {
 326                         var foucused = WorkflowEngine.currentFocused();
 327                         if (foucused) {
 328                             foucused.setNormaled();
 329                             WorkflowEngine.currentFocused(null);
 330                             mustRepaint = true;
 331     
 332                         }
 333                     }
 334                 }
 335                 if (mustRepaint) {
 336                     WorkflowEngine.repaint();
 337                 }
 338                 
 339             };
 340             //
 341             WorkflowEngine.hitActivity=function(pick){
 342                 return pick && pick.constructor==Activity;
 343             };
 344             //
 345             WorkflowEngine.hitActor=function(pick){
 346                 return pick && pick.constructor==Actor;
 347             };
 348             //
 349             WorkflowEngine.hitBlank=function(pick){
 350                 return !pick;
 351             };
 352             WorkflowEngine.hit=function(pick){
 353                 return  pick;
 354             };
 355             //
 356             WorkflowEngine.hitTopEar=function(x,y){
 357                 return this.workflow && this.workflow.checked && this.workflow.checked.constructor==Activity && WorkflowEngine.workflow.getTopEar() && WorkflowEngine.workflow.getTopEar().isInner(x,y);
 358             };
 359             //
 360             WorkflowEngine.hitRightEar=function(x,y){
 361                 return this.workflow && this.workflow.checked && this.workflow.checked.constructor==Activity && WorkflowEngine.workflow.getRightEar() && WorkflowEngine.workflow.getRightEar().isInner(x,y);
 362             };
 363             //
 364             WorkflowEngine.readyAddNewActivity=false;
 365             WorkflowEngine.readyLink=false;
 366             WorkflowEngine.readyAddNewActor=false;
 367             WorkflowEngine.readyDrag=false;
 368             //getter/setter
 369             WorkflowEngine.currentChecked = function(obj) {
 370                 if(arguments && arguments.length==1)
 371                     return WorkflowEngine.workflow && (WorkflowEngine.workflow.checked=obj);
 372                 else
 373                     return WorkflowEngine.workflow && WorkflowEngine.workflow.checked;
 374                 
 375             };
 376             //getter/setter
 377             WorkflowEngine.currentFocused = function(obj) {
 378                 if(arguments && arguments.length==1)
 379                     return WorkflowEngine.workflow && (WorkflowEngine.workflow.focused=obj);
 380                else
 381                     return WorkflowEngine.workflow && WorkflowEngine.workflow.focused;
 382             };
 383             //拾取选中的节点
 384             WorkflowEngine.pick = function(event) {
 385                 return WorkflowEngine.workflow
 386                         && WorkflowEngine.workflow.pick(event.offsetX,
 387                                 event.offsetY);
 388             };
 389             //定义一个方法
 390             WorkflowEngine.draw = (function() {
 391                 return function() {
 392                     //清除屏幕
 393                     WorkflowEngine.context2d.clearRect(0, 0,
 394                             WorkflowEngine.canvasWidth,
 395                             WorkflowEngine.canvasHeight);
 396                     //绘制光标
 397                     if(WorkflowEngine.cursor)
 398                         WorkflowEngine.cursor.draw(WorkflowEngine.context2d);
 399                     WorkflowEngine.workflow.draw(WorkflowEngine.context2d);
 400                 };
 401             })();
 402             //定义一个方法,重绘屏幕
 403             WorkflowEngine.repaint = function() {
 404                   //刷新属性列表视图
 405                 var checked=WorkflowEngine.currentChecked() || WorkflowEngine.workflow;
 406                 var data=WorkflowEngine.propertygrid();
 407                 //切换属性
 408                 if(data && data.rows[0] && data.rows[0].value!=checked.id())
 409                     {
 410                     //显示工作流属性
 411                     if(checked.constructor==Workflow)
 412                         {
 413                         WorkflowEngine.propertyTitle(checked.getText());
 414                         }
 415                     //显示节点属性
 416                     else
 417                         {
 418                         //只有activity有转移条件
 419                         if(checked.constructor==Activity)
 420                             {
 421                             var arrows=this.workflow.findArrows(checked.id(),true);
 422                             checked.addConditions(arrows);
 423                             }
 424                         WorkflowEngine.propertyTitle(checked.node.getText());
 425                         } 
 426                     //覆盖右边属性
 427                     WorkflowEngine.propertygrid(checked.propertygrid());
 428                     }
 429                 //只有activity有转移条件
 430                 else if(checked.constructor==Activity)
 431                         {
 432                         var arrows=this.workflow.findArrows(checked.id(),true);
 433                         checked.addConditions(arrows);
 434                         WorkflowEngine.propertygrid(checked.propertygrid());
 435                         }
 436                 
 437                 //使用浏览器的自动动画支持机制
 438                 if (window.requestAnimationFrame) {
 439                     window.requestAnimationFrame(this.draw);
 440                 }
 441                 //使用火狐浏览器的自动动画支持机制
 442                 else if (window.mozRequestAnimationFrame) {
 443                     window.mozRequestAnimationFrame(this.draw);
 444                 }
 445                 //如果不支持HTML5动画绘制函数
 446                 else {
 447                     this.draw();
 448                 }
 449                 
 450             };
 451             //getter/setter如果不存在属性视图就创建
 452             WorkflowEngine.propertygrid=function(data){
 453                 this.propertyGrid = this.propertyGrid || this.table.propertygrid({
 454                     showGroup: true,
 455                     scrollbarSize: 0,
 456                     columns:  [[{field:'name',title:'属性名',width:80,sortable:true},{field:'value',title:'属性值',width:200,resizable:false}]],
 457                     
 458                   });
 459                 if(data)
 460                     this.propertyGrid.propertygrid('loadData',data);
 461                 else
 462                     return this.propertyGrid.propertygrid('getData');
 463                 
 464             };
 465             //seter方法:修改title
 466             WorkflowEngine.propertyTitle=function(propertyName){
 467                 this.propertyView.panel('setTitle','属性表-'+propertyName);
 468             };
 469             WorkflowEngine.workflowTitle=function(workflowName){
 470                  workflowName='流程图-'+workflowName;
 471                  this.workflowView.panel('setTitle',workflowName);  
 472             };
 473             //定义方法
 474             WorkflowEngine.initialize = function() {
 475                 this.image=new Image();
 476                 this.image.src='icons/man.png';
 477                 
 478                 var left=$('<div></div>').appendTo('.workflow-diagram').css('width','60%');
 479                 var right=$('<div></div>').appendTo('.workflow-diagram').css('width','40%');
 480                 
 481                 $('.workflow-diagram').portal({
 482                     border : true,
 483                     fit : true
 484                 });
 485                 
 486                 var p = $('<div></div>').appendTo(left);
 487                 p.panel({
 488                     title : '流程图'
 489                 });
 490                 $('.workflow-diagram').portal('add', {
 491                     panel: p,
 492                     columnIndex: 0
 493                 });
 494                 
 495                 //追加工具栏
 496                 var tb = $('<div></div>').appendTo(p);
 497                 var add = $('<a></a>').appendTo(tb);
 498                 //新建
 499                 add.linkbutton({
 500                     iconCls : 'icon-add',
 501                     onClick : function() {
 502                         //GC
 503                         WorkflowEngine.workflow=null;
 504                         WorkflowEngine.workflow = new Workflow('新建工作流');
 505                         WorkflowEngine.workflowTitle(WorkflowEngine.workflow.getText());
 506                         WorkflowEngine.workflow.add('Activity',new Activity(WorkflowEngine.workflow.id(),WorkflowEngine.context2d, '开始', 10, WorkflowEngine.canvasHeight/3,true));
 507                         WorkflowEngine.propertygrid(WorkflowEngine.workflow.propertygrid());
 508                         WorkflowEngine.repaint();
 509                         
 510                     }
 511                 });
 512                 //新建工具提示
 513                 add.tooltip({
 514                     position : 'top',
 515                     content : '<span style="color:#fff">新建一个工作流.</span>',
 516                     onShow : function() {
 517                         $(this).tooltip('tip').css({
 518                             backgroundColor : '#666',
 519                             borderColor : '#666'
 520                         });
 521                     }
 522                 });
 523                 //删除
 524                 var del = $('<a></a>').appendTo(tb);
 525                 del.linkbutton({
 526                     iconCls : 'icon-remove',
 527                     onClick : function() {
 528                         if(WorkflowEngine.workflow && WorkflowEngine.workflow.del())
 529                             WorkflowEngine.repaint();
 530                                    
 531                     }
 532                 });
 533                 //删除工具提示
 534                 del.tooltip({
 535                     position : 'top',
 536                     content : '<span style="color:#fff">删除</span>',
 537                     onShow : function() {
 538                         $(this).tooltip('tip').css({
 539                             backgroundColor : '#666',
 540                             borderColor : '#666'
 541                         });
 542                     }
 543                 });
 544                 //撤销
 545                 var undo = $('<a></a>').appendTo(tb);
 546                 undo.linkbutton({
 547                     iconCls : 'icon-undo',
 548                     onClick : function() {
 549                         if(WorkflowEngine.workflow && WorkflowEngine.workflow.undo())
 550                                    WorkflowEngine.repaint();
 551                     }
 552                 });
 553                 //撤销工具提示
 554                 undo.tooltip({
 555                     position : 'top',
 556                     content : '<span style="color:#fff">撤销</span>',
 557                     onShow : function() {
 558                         $(this).tooltip('tip').css({
 559                             backgroundColor : '#666',
 560                             borderColor : '#666'
 561                         });
 562                     }
 563                 });
 564                 //恢复
 565                 var redo = $('<a></a>').appendTo(tb);
 566                 redo.linkbutton({
 567                     iconCls : 'icon-redo',
 568                     onClick : function() {
 569                         if(WorkflowEngine.workflow && WorkflowEngine.workflow.redo())
 570                              WorkflowEngine.repaint();
 571                     }
 572                 });
 573                 //恢复工具提示
 574                 redo.tooltip({
 575                     position : 'top',
 576                     content : '<span style="color:#fff">恢复</span>',
 577                     onShow : function() {
 578                         $(this).tooltip('tip').css({
 579                             backgroundColor : '#666',
 580                             borderColor : '#666'
 581                         });
 582                     }
 583                 });
 584                 //保存
 585                 var save = $('<a></a>').appendTo(tb);
 586                 save.linkbutton({
 587                     iconCls : 'icon-save',
 588                     onClick : function() {
 589                         if(WorkflowEngine.workflow)
 590                             alert(JSON.stringify(WorkflowEngine.workflow.toJson()));
 591                             /* $.post('hawk/wws/new',{data:JSON.stringify(WorkflowEngine.workflow.toJson())},function(data){
 592                                 alert(data);
 593                             }); */
 594                     }
 595                 });
 596                 //保存工具提示
 597                 save.tooltip({
 598                     position : 'top',
 599                     content : '<span style="color:#fff">保存</span>',
 600                     onShow : function() {
 601                         $(this).tooltip('tip').css({
 602                             backgroundColor : '#666',
 603                             borderColor : '#666'
 604                         });
 605                     }
 606                 });
 607                 //清除
 608                 var clear = $('<a></a>').appendTo(tb);
 609                 clear.linkbutton({
 610                     iconCls : 'icon-clear',
 611                     onClick : function() {
 612                         if(WorkflowEngine.workflow && WorkflowEngine.workflow.clear())
 613                             WorkflowEngine.repaint();
 614                     }
 615                 });
 616                 //清除工具提示
 617                 clear.tooltip({
 618                     position : 'top',
 619                     content : '<span style="color:#fff">清除</span>',
 620                     onShow : function() {
 621                         $(this).tooltip('tip').css({
 622                             backgroundColor : '#666',
 623                             borderColor : '#666'
 624                         });
 625                     }
 626                 }); 
 627                 //新建浮动节点
 628                 var node = $('<a></a>').appendTo(tb);
 629                 node.linkbutton({
 630                     iconCls : 'icon-more',
 631                     onClick : function() {
 632                         if(WorkflowEngine.workflow)
 633                         WorkflowEngine.readyAddNewActivity=true;
 634                     }
 635                 });
 636                 //清除工具提示
 637                 node.tooltip({
 638                     position : 'top',
 639                     content : '<span style="color:#fff">新建浮动节点</span>',
 640                     onShow : function() {
 641                         $(this).tooltip('tip').css({
 642                             backgroundColor : '#666',
 643                             borderColor : '#666'
 644                         });
 645                     }
 646                 }); 
 647                 //追加画布
 648                 var canvas = $('<canvas></canvas>').appendTo(p);
 649                 this.canvasWidth = p.width();
 650                 this.canvasHeight = $('.workflow-diagram').innerHeight()-2*tb.height();
 651                 canvas.attr('width', this.canvasWidth);
 652                 canvas.attr('height', this.canvasHeight);
 653                 //私有方法
 654                 bindEvents(canvas);
 655                 //还原成Dom对象
 656                 var c = canvas.get(0);
 657                 this.context2d = c.getContext ? c.getContext('2d') : null;
 658                 if (!this.context2d)
 659                     throw new Error('不支持2D绘图.');
 660                                 
 661                 //保存为属性
 662                 this.workflowView=p;
 663                 
 664                 p = $('<div/></div>').appendTo(right);
 665                 p.panel({
 666                     title : '属性表'
 667                     
 668                 });
 669 
 670                 $('.workflow-diagram').portal('add', {
 671                     panel: p,
 672                     columnIndex: 1
 673                 });
 674                                     
 675                 this.table = $('<table></table>').appendTo(p);
 676                 //保存为属性
 677                 this.propertyView=p;
 678                     
 679             };
 680 
 681             /*
 682              *定义一个类:Workflow 继承了Shape
 683              */
 684             function Workflow(text) {
 685                 Shape.call(this, text);
 686                 this.activities = [];
 687                 this.actors=[];
 688                 this.arrows=[];
 689                 this.pointer=-1;
 690                 this.acts=[];
 691                 var self=this;
 692                 //属性
 693                 var id='Workflow'+new Date().getTime();
 694                 var data=[{text:"boolean"},{text:"short"},{text:"String",selected:true},{text:"int"},{text:"long"},{text:"float"},{text:"double"},{text:"java.util.Date"},{text:"java.math.BigDecimal"}];
 695                 var demo="[{\"field\":\"agree\",\"name\":\"是否同意\"}]";
 696                 var properties=[
 697                                    {"field":"id","name":"标识符","value":id,"group":"不可编辑"},
 698                                    {"field":"name","name":"文本","value":this.getText(),"group":"可编辑的","editor":{"type":"textbox","options":{"required": true,"validType":"length[1,20]","onChange":function(newValue,oldValue){
 699                                        
 700                                        if(newValue && oldValue && newValue!=oldValue)
 701                                            {
 702                                            self.setText(newValue);
 703                                            WorkflowEngine.workflowTitle(newValue);
 704                                            WorkflowEngine.propertyTitle(newValue);
 705                                            }
 706                                         
 707                                    }}}},
 708                                    {"field":"form","name":"定义表单","value":demo,"group":"表单","editor":{"type":"textbox","options":{"required": true,"multiline":true,"width":100,"height":120,"onChange":function(newValue,oldValue){
 709                                           
 710                                        if(newValue && oldValue && newValue!=oldValue)
 711                                            {
 712                                            try
 713                                            {
 714                                                var json=JSON.parse($.trim(newValue));
 715                                            }
 716                                            catch(e)
 717                                            {
 718                                                console.error(e);
 719                                                return;
 720                                            }
 721                                            //刷新数据
 722                                            for(var i=0;i<json.length;i++)
 723                                                {
 724                                                json[i].value=null;
 725                                                json[i].group="表单数据";
 726                                                json[i].editor={type:"combobox",options:{required: true,editable:false,panelHeight:100,valueField:"text",textField:"text",data:data}};
 727                                                 }
 728                                            //刷新数据
 729                                            self.properties=properties.concat(json);
 730                                            //刷新视图
 731                                            WorkflowEngine.propertygrid(self.properties);
 732                                            }
 733                                         
 734                                    }}}}
 735                                ];
 736                 this.properties=properties;
 737                             
 738             };
 739             //定义内部类
 740             Workflow.Action=function(type,data){
 741                 this.data=data;
 742                 this.type=type;
 743             };
 744             //枚举
 745             Workflow.Action.Type={add:1,del:2};
 746             //继承Shape
 747             Workflow.prototype = new Shape();
 748             //修改构造器
 749             Workflow.prototype.constructor = Workflow;
 750             //获取表单数据
 751             Workflow.prototype.form=function(){
 752                 var ret=[];
 753                 for(var i=0;i<this.properties.length;i++)
 754                     {
 755                     if(this.properties[i]['group']=='表单数据')
 756                         {
 757                         ret.push({name:this.properties[i].field,text:this.properties[i].name,type:this.properties[i].value});
 758                         }
 759                     }
 760                 return ret;
 761             };
 762             //return={id:id,name:name,activities:[{activity_id,id,name:name,actors:[],conditions:[]},{activity_id,id,name:name,actors:[],conditions:[]}...]}
 763             Workflow.prototype.toJson=function(){
 764                 var result={id:this.id(),name:this.properties[1].value,form:this.form()};
 765                 result.activities=[];
 766                 for(var i=0;i<this.activities.length;i++)
 767                     {
 768                     var activity=this.activities[i];
 769                     var json=activity.toJson();
 770                     result.activities.push(json);
 771                     var actors=this.findActors(activity.id());
 772                     json.actors=[];
 773                     for(var j=0;j<actors.length;j++)
 774                         {
 775                         var actor=actors[j];
 776                         json.actors.push(actor.toJson());
 777                         }
 778                     var arrows=this.findArrows(activity.id(),true);
 779                     json.conditions=[];
 780                     for(var j=0;j<arrows.length;j++)
 781                     {
 782                     var arrow=arrows[j];
 783                     json.conditions.push(arrow.toJson());
 784                     }
 785                     }
 786                 
 787                 return result;                
 788             };
 789             
 790             //查找Activity 或者Actor
 791             Workflow.prototype.findById=function(id){
 792                 for(var i=0;i<this.activities.length;i+=1)
 793                 {
 794                 if(this.activities[i].id()==id)
 795                     return this.activities[i];
 796                 }
 797                 for(var i=0;i<this.actors.length;i+=1)
 798                 {
 799                 if(this.actors[i].id()==id)
 800                     return this.actors[i];
 801                 }
 802                 return null;
 803             };
 804             //findByActivityId
 805             Workflow.prototype.findActors=function(activity_id){
 806                 var actors=[];
 807                 for(var i=0;i<this.actors.length;i+=1)
 808                     {
 809                     if(this.actors[i].foreignKey()==activity_id)
 810                         actors[actors.length]=this.actors[i];
 811                     }
 812                 return actors;
 813             };
 814             //查找连线,重载
 815             Workflow.prototype.findArrows=function(activity_id,isAway){
 816                 var arrows=[];
 817                 for(var i=0;i<this.arrows.length;i+=1)
 818                     {
 819                     var cond=isAway?(this.arrows[i].id1==activity_id):(this.arrows[i].id1==activity_id  || this.arrows[i].id2==activity_id);
 820                     if(cond)
 821                         arrows[arrows.length]=this.arrows[i];
 822                     }
 823                 return arrows;
 824             };
 825             //根据id查找节点
 826             Workflow.prototype.findActivity=function(activity_id){
 827                 for(var i=0;i<this.activities.length;i+=1)
 828                     {
 829                     if(this.activities[i].id()==activity_id)
 830                         return this.activities[i];
 831                     }
 832                 return null;
 833             };
 834             //find foreignkey!=activity_id的Actor
 835             Workflow.prototype.notActors=function(activity_id){
 836                 var actors=[];
 837                 for(var i=0;i<this.actors.length;i+=1)
 838                     {
 839                     if(this.actors[i].foreignKey()!=activity_id)
 840                         actors[actors.length]=this.actors[i];
 841                     }
 842                 return actors;
 843             };
 844             //find 跟activity_id无关的连线
 845             Workflow.prototype.notArrows=function(activity_id){
 846                 var arrows=[];
 847                 for(var i=0;i<this.arrows.length;i+=1)
 848                     {
 849                     if(this.arrows[i].id1!=activity_id && this.arrows[i].id2!=activity_id)
 850                         arrows[arrows.length]=this.arrows[i];
 851                     }
 852                 return arrows;
 853             };
 854             
 855             //getter flow_id
 856             Workflow.prototype.id=function(id){
 857                 return id?(this.properties[0].value=id):this.properties[0].value;
 858             };
 859             //处理属性
 860             Workflow.prototype.propertygrid=function(){
 861                 return this.properties;
 862             };
 863             
 864             //处理动作数组acts的方法
 865             Workflow.prototype.handle=function(){
 866                 //清空数组
 867                 this.activities.length=0;
 868                 this.actors.length=0;
 869                 this.arrows.length=0;
 870                 this.checked=null;
 871                 for(var i=0;i<=this.pointer;i+=1)
 872                     {
 873                     var act=this.acts[i];
 874                     switch(act.type)
 875                     {
 876                     case Workflow.Action.Type.add:
 877                         if(act.data.constructor==Activity)
 878                             {
 879                              if(act.data.isPicked())
 880                                  this.checked=act.data;
 881                              this.activities[this.activities.length]=act.data;
 882                             }
 883                           
 884                         else if(act.data.constructor==Actor)
 885                             {
 886                             if(act.data.isPicked())
 887                                  this.checked=act.data;
 888                             this.actors[this.actors.length]=act.data;
 889                             }
 890                            
 891                         else if(act.data.constructor==Arrow)
 892                              this.arrows[this.arrows.length]=act.data;
 893                         break;
 894                     case Workflow.Action.Type.del:
 895                         this.remove(act.data);
 896                         break;
 897                     
 898                     }
 899                 }
 900             };
 901             //定义事件:redo
 902             Workflow.prototype.redo=function(){
 903                 if(this.pointer==this.acts.length-1)
 904                     return false;
 905                 this.pointer++;
 906                 this.handle();
 907                 return true;
 908             };
 909             //定义事件:undo
 910             Workflow.prototype.undo=function(){
 911                 if(this.pointer==0)
 912                     return false;
 913                 this.pointer--;
 914                 this.handle();
 915                 return true;
 916             };
 917             //定义事件:del
 918             Workflow.prototype.del=function(){
 919                 if(this.checked)
 920                     {
 921                     if(this.checked.constructor==Activity && this.checked.isHead())
 922                          return false;
 923                     var del=this.addDelAction();
 924                     this.remove(del.data);
 925                     return true;
 926                     }
 927                 return false;
 928             };
 929             //定义事件:add Activity 或者 Actor
 930             Workflow.prototype.add=function(type,obj){
 931                 if(type=='Actor')
 932                     {
 933                     this.actors[this.actors.length]=obj;
 934                     }
 935                 else if(type=='Activity')
 936                     {
 937                     this.activities[this.activities.length]=obj;
 938                     }
 939                 else if(type=='Arrow')
 940                 {
 941                 this.arrows[this.arrows.length]=obj;
 942                 }
 943                 this.addAddAction(obj);
 944             };
 945             //定义事件:clear
 946             Workflow.prototype.clear=function(){
 947                 if(this.activities.length==1)
 948                     return false;
 949                 this.activities.splice(1,this.activities.length-1);
 950                 this.pointer=0;
 951                 this.acts.splice(1,this.acts.length-1);
 952                 this.actors=[];
 953                 this.arrows=[];
 954                 return true;
 955             };
 956             //重写draw方法
 957             Workflow.prototype.draw = function(context2d) {
 958                 for (var i = 0; i < this.activities.length; i += 1) {
 959                     //当前节点
 960                     var child = this.activities[i];
 961                     //画右耳朵
 962                     if(child.isPicked())
 963                         {
 964                         this.rightEar=new Ear(child.node.maxX + Ear.RADIUS,(child.node.minY + child.node.maxY) / 2);
 965                         this.rightEar.draw(WorkflowEngine.context2d);
 966                         if(!child.isHead())
 967                             {
 968                             //画上耳朵
 969                             if(child.isPicked()&&!child.isHead())
 970                                 {
 971                                 this.topEar=new Ear((child.node.minX+child.node.maxX)/2, child.node.minY- Ear.RADIUS);
 972                                 this.topEar.draw(WorkflowEngine.context2d);
 973                                 }
 974                             }
 975                         }
 976 
 977                     child.draw(context2d);
 978                     var actors=this.findActors(child.id());
 979                     for(var j=0;j<actors.length;j+=1)
 980                         {
 981                         actors[j].draw(context2d);
 982                         //画连线
 983                         drawLine(context2d, (child.node.minX+child.node.maxX)/2,
 984                                 child.node.minY,
 985                                 (actors[j].node.minX+actors[j].node.maxX)/2,
 986                                 actors[j].node.maxY);
 987                         } 
 988                     
 989                 }
 990                 //画连线
 991                 for(var i=0;i<this.arrows.length;i+=1)
 992                     {
 993                     var arrow=this.arrows[i];
 994                     arrow.draw(WorkflowEngine.context2d,this.findActivity(arrow.id1),this.findActivity(arrow.id2));
 995                     }
 996                 
 997             };
 998             //获取上耳朵
 999             Workflow.prototype.getTopEar=function(){
1000                 return this.topEar;
1001             };
1002             //获取右耳朵
1003             Workflow.prototype.getRightEar=function(){
1004                 return this.rightEar;
1005             };
1006             
1007             //定义实例方法:addAddAction,有三种可能,Actor,Activity,Arrow
1008             Workflow.prototype.addAddAction = function(data) {
1009                 //删除pointer后面的动作(不包括当前位置)
1010                 this.acts.splice(this.pointer+1,this.acts.length-this.pointer-1);
1011                 this.pointer=this.acts.length;
1012                 this.acts.push(new Workflow.Action(Workflow.Action.Type.add,data));
1013                     
1014             };
1015             
1016             //定义实例方法:addDelAction,两种可能,Actor,Activity
1017             Workflow.prototype.addDelAction = function() {
1018                 var del=this.checked;
1019                 //删除pointer后面的动作(不包括当前位置)
1020                 this.acts.splice(this.pointer+1,this.acts.length-this.pointer-1);
1021                 this.pointer=this.acts.length;
1022                 this.acts.push(del=new Workflow.Action(Workflow.Action.Type.del,del));
1023                 this.checked.setNormaled();
1024                 this.checked=null;
1025                 return     del;                        
1026             };
1027             //定义实例方法:remove Activity 或者 Actor
1028             Workflow.prototype.remove = function(obj) {
1029                 if(obj.constructor==Actor)
1030                 {
1031                 this.actors.splice(obj.index,1);
1032                 }
1033                 else if(obj.constructor==Activity)
1034                     {
1035                     //删除节点
1036                     this.activities.splice(obj.index,1);
1037                     //删除连线
1038                     this.arrows=this.notArrows(obj.id());
1039                     //删除参与者
1040                     this.actors=this.notActors(obj.id());
1041                     }
1042                 
1043             };
1044             //定义实例方法:pick Activity 或者 Actor
1045             Workflow.prototype.pick = function(x, y) {
1046                 for (var i = 0; i < this.activities.length; i += 1) {
1047                     if (this.activities[i].isInner(x, y)) {
1048                         //保存索引
1049                         this.activities[i].index = i;
1050                         return this.activities[i];
1051                     }
1052 
1053                 }
1054                 for(var i=0;i<this.actors.length;i+=1)
1055                     {
1056                     if (this.actors[i].isInner(x, y)) {
1057                         //保存索引
1058                         this.actors[i].index = i;
1059                         return this.actors[i];
1060                     }
1061                 }
1062                 return null;
1063             };
1064             //重写实例方法;move
1065             Workflow.prototype.move = function(dx, dy) {
1066                 if(this.checked)
1067                     {
1068                     if(this.checked.constructor==Activity)
1069                     {
1070                         //是头
1071                         if(this.checked.isHead())
1072                             {
1073                             for(var i=1;i<this.activities.length;i+=1)
1074                                 {
1075                                 this.activities[i].move(dx, dy);
1076                                 var actors=this.findActors(this.activities[i].id());
1077                                 for(var j=0;j<actors.length;j+=1)
1078                                     actors[j].move(dx,dy);
1079                                 }
1080                             }
1081                         this.checked.move(dx, dy);
1082                         var actors=this.findActors(this.checked.id());
1083                         for(var j=0;j<actors.length;j+=1)
1084                             actors[j].move(dx,dy);
1085                         
1086                     }
1087                     else if(this.checked.constructor==Actor)
1088                     {
1089                     this.checked.move(dx,dy);
1090                     }
1091                 
1092                 }
1093                 
1094             };
1095             //重写方法:isInner
1096             Workflow.prototype.isInner = function(x, y) {
1097                 return this.pick(x, y) != null;
1098             };
1099             //定义连接线的长度
1100             Workflow.LINE_LENGTH = 30;
1101 
1102             /*
1103              *辅助类:Node 继承了Shape
1104              */
1105             function Node(text, minX, minY, maxX, maxY) {
1106                 //调用Shape的构造器,构造器继承
1107                 Shape.call(this, text);
1108                 this.minX = minX;
1109                 this.minY = minY;
1110                 this.maxX = maxX;
1111                 this.maxY = maxY;
1112 
1113             }
1114             //原型继承
1115             Node.prototype = new Shape();
1116             //new Node().getText()
1117             //修改构造器类型
1118             Node.prototype.constructor = Node;
1119             //重写实例方法:draw
1120             Node.prototype.draw = function(context2d) {
1121                 drawRect(context2d, this.minX, this.minY,
1122                         this.maxX - this.minX, this.maxY - this.minY);
1123                 drawText(context2d, this.minX, (this.minY + this.maxY) / 2+5,
1124                         this.getText());
1125                                 
1126             };
1127             //重写实例方法;move
1128             Node.prototype.move = function(dx, dy) {
1129                 this.minX += dx;
1130                 this.minY += dy;
1131                 this.maxX += dx;
1132                 this.maxY += dy;
1133             };
1134             //重写方法:isInner
1135             Node.prototype.isInner = function(x, y) {
1136                 return x > this.minX && x<this.maxX && y>this.minY
1137                         && y < this.maxY;
1138             };
1139             //选中高亮重绘
1140             Node.prototype.checkedHighlight = function(context2d) {
1141                 //半透明红色
1142                 context2d.strokeStyle = 'rgba(255,0,0,0.5)';
1143                 this.draw(context2d);
1144                 //恢复半透明蓝色
1145                 context2d.strokeStyle = '#b1c242';
1146             };
1147 
1148             //悬浮高亮重绘
1149             Node.prototype.hoveHighlight = function(context2d) {
1150                 //半透明绿色
1151                 context2d.strokeStyle = 'rgba(0,255,0,0.5)';
1152                 this.draw(context2d);
1153                 //恢复半透明蓝色
1154                 context2d.strokeStyle = '#b1c242';
1155             };
1156 
1157             /*
1158              *辅助类:Ear 继承了Shape
1159              */
1160             function Ear(x, y) {
1161                 //调用Shape的构造器,构造器继承
1162                 Shape.call(this, '');
1163                 this.x = x;
1164                 this.y = y;
1165                 this.radius = Ear.RADIUS;
1166             }
1167             //原型继承
1168             Ear.prototype = new Shape();
1169             //修改构造器类型
1170             Ear.prototype.constructor = Ear;
1171             //重写实例方法:draw
1172             Ear.prototype.draw = function(context2d) {
1173                 drawCircle(context2d, this.x, this.y, this.radius);
1174             };
1175             //重写实例方法;move
1176             Ear.prototype.move = function(dx, dy) {
1177                 this.x += dx;
1178                 this.y += dy;
1179             };
1180             //重写方法:isInner
1181             Ear.prototype.isInner = function(x, y) {
1182                 return Math.sqrt((x - this.x) * (x - this.x) + (y - this.y)
1183                         * (y - this.y)) <= this.radius;
1184             };
1185             //定义常量
1186             Ear.RADIUS = 5;
1187 
1188             /*
1189              *定义一个类:Activity
1190              */
1191             function Activity(flow_id,context2d, text, x, y,head) {
1192                 var w = context2d?getTextWidth(context2d, text):0;
1193                 this.node = new Node(text, x, y, x + w, y + Activity.HEIGHT);
1194                 this.status = Activity.NORMAL;
1195                 this.head=head || false;
1196                 var self=this;
1197                 //属性
1198                 var id=(head?'Activity-Head':'Activity')+new Date().getTime();
1199                 var data=[{text:"boolean"},{text:"short"},{text:"String",selected:true},{text:"int"},{text:"long"},{text:"float"},{text:"double"},{text:"java.util.Date"},{text:"java.math.BigDecimal"}];
1200                 var demo="[{\"field\":\"agree\",\"name\":\"是否同意\"}]";
1201                 this.fixed=[  {"field":"id","name":"标识符","value":id,"group":"不可编辑"},
1202                                   {"field":"flow_id","name":"流程编号","value":flow_id,"group":"不可编辑"},
1203                                   {"field":"name","name":"文本","value":this.node.getText(),"group":"可编辑的","editor":{"type":"textbox","options":{"required": true,"validType":"length[1,20]","onChange":function(newValue,oldValue){
1204                       
1205                                        if(newValue && oldValue && newValue!=oldValue)
1206                                            {
1207                                            self.node.setText(newValue);
1208                                            self.node.maxX=self.node.minX+getTextWidth(context2d, newValue);
1209                                            WorkflowEngine.repaint();
1210                                            }
1211                                         
1212                                    }}}},
1213                                    {"field":"form","name":"定义属性","value":demo,"group":"动态属性","editor":{"type":"textbox","options":{"required": true,"multiline":true,"width":100,"height":120,"onChange":function(newValue,oldValue){
1214                                           
1215                                        if(newValue && oldValue && newValue!=oldValue)
1216                                            {
1217                                            try
1218                                            {
1219                                                var json=JSON.parse($.trim(newValue));
1220                                            }
1221                                            catch(e)
1222                                            {
1223                                                console.error(e);
1224                                                return;
1225                                            }
1226                                            //刷新数据
1227                                            for(var i=0;i<json.length;i++)
1228                                                {
1229                                                json[i].value=null;
1230                                                json[i].group="动态属性";
1231                                                json[i].editor={type:"combobox",options:{required: true,editable:false,panelHeight:100,valueField:"text",textField:"text",data:data}};
1232                                                 }
1233                                            self.properties=self.fixed.concat(json);
1234                                            //刷新视图
1235                                            WorkflowEngine.propertygrid(self.properties);
1236                                            }
1237                                         
1238                                    }}}}
1239                                ];  
1240                 if(head)
1241                     {
1242                     delete this.fixed[2]["editor"];
1243                     }
1244                 this.properties=this.fixed;
1245             };
1246             Activity.prototype.toJson=function(){
1247                 return {id:this.id(),name:this.properties[2].value,properties:this.getProperties()};
1248             };
1249             Activity.prototype.getProperties=function(){
1250                 var ret=[];
1251                 for(var i=0;i<this.properties.length;i+=1)
1252                     {
1253                     var prop=this.properties[i];
1254                     if(prop.group=='动态属性' && prop.field!='form')
1255                         ret.push({name:prop.field,text:prop.name,type:prop.value});
1256                     }
1257                 return ret;
1258             };
1259             Activity.prototype.addConditions=function(arrows){
1260                 this.conditions=[];
1261                     for(var i=0;i<arrows.length;i++)
1262                     {
1263                         this.conditions.push(arrows[i].conditions[0]);
1264                         this.conditions.push(arrows[i].conditions[1]);
1265                     }
1266                     //this.properties=this.fixed.concat(conditions);
1267             };
1268             //setter/getter 方法
1269             Activity.prototype.id=function(id){
1270                 return id?(this.properties[0].value=id):this.properties[0].value;
1271             };
1272             //setter/getter flow_id的值
1273             Activity.prototype.foreignKey=function(flow_id){
1274                 return flow_id?(this.properties[1].value=flow_id):this.properties[1].value;
1275             };
1276             //读属性
1277             Activity.prototype.propertygrid=function(){
1278                 return this.properties.concat(this.conditions);
1279             };
1280             
1281             //draw方法
1282             Activity.prototype.draw = function(context2d) {
1283                 switch (this.status) {
1284                 case Activity.NORMAL:
1285                     this.node.draw(context2d);
1286                     break;
1287                 case Activity.FOCUS:
1288                     this.node.hoveHighlight(context2d);
1289                     break;
1290                 case Activity.PICKED:
1291                     this.node.checkedHighlight(context2d);
1292                     break;
1293                 }
1294 
1295             };
1296             Activity.prototype.isHead=function(){
1297                 return this.head;
1298             };
1299             //方法:isInner
1300             Activity.prototype.isInner = function(x, y) {
1301                 return this.node.isInner(x, y);
1302             };
1303             //实例方法;move
1304             Activity.prototype.move = function(dx, dy) {
1305                 this.node.move(dx, dy);
1306             };
1307             //
1308             Activity.prototype.setPicked = function() {
1309                 this.status = Activity.PICKED;
1310             };
1311             //
1312             Activity.prototype.setFocused = function() {
1313                 this.status = Activity.FOCUS;
1314             };
1315             //
1316             Activity.prototype.setNormaled = function() {
1317                 this.status = Activity.NORMAL;
1318             };
1319             //
1320             Activity.prototype.isPicked = function() {
1321                 return this.status == Activity.PICKED;
1322             };
1323             //
1324             Activity.prototype.isFocused = function() {
1325                 return this.status == Activity.FOCUS;
1326             };
1327             //
1328             Activity.prototype.isNormaled = function() {
1329                 return this.status == Activity.NORMAL;
1330             };
1331             //定义常量高度
1332             Activity.HEIGHT = 20;
1333             //定义状态常量
1334             Activity.NORMAL = 1;
1335             //表示获得了焦点
1336             Activity.FOCUS = 2;
1337             //被选中
1338             Activity.PICKED = 3;
1339             
1340             /*
1341             *定义一个类:Actor,继承Activity
1342             */
1343             function Actor(activity_id,context2d, text, x, y)
1344             {
1345                 Activity.call(this,activity_id,context2d,text,x,y,true);
1346                 this.node.maxX+=Actor.LENGTH;
1347                 //初始化ID
1348                 var id='Actor'+new Date().getTime();
1349                 var self=this;
1350                 //重新定义
1351                 this.properties=[
1352                                    {"field":"id","name":"标识符","value":id,"group":"可编辑的","editor":{"type":"textbox","options":{"required": true}}},
1353                                    {"field":"activity_id","name":"活动节点","value":activity_id,"group":"不可编辑的"},
1354                                    {"field":"name","name":"文本","value":this.node.getText(),"group":"可编辑的","editor":{"type":"textbox","options":{"required": true,"validType":"length[1,20]","onChange":function(newValue,oldValue){
1355                                        
1356                                        if(newValue && oldValue && newValue!=oldValue)
1357                                            {
1358                                            self.node.setText(newValue);
1359                                            self.node.maxX=self.node.minX+getTextWidth(context2d, newValue)+Actor.LENGTH;
1360                                            WorkflowEngine.repaint();
1361                                            }
1362                                         
1363                                    }}}},
1364                                   
1365                                 ];
1366                 
1367             }
1368             //继承方法
1369             Actor.prototype=new Activity();
1370             //修改构造器
1371             Actor.prototype.constructor=Actor;
1372             //覆盖
1373             Actor.prototype.propertygrid=function(){
1374                 return this.properties;
1375             };
1376             //覆盖
1377             Actor.prototype.toJson=function(){
1378                 return {id:this.id(),name:this.properties[2].value};
1379             };
1380             //覆盖draw方法
1381             Actor.prototype.draw = function(context2d) {
1382                 drawImage(context2d,this.node.maxX-Actor.LENGTH,this.node.minY,Actor.LENGTH,Actor.LENGTH);
1383                 switch (this.status) {
1384                 case Activity.NORMAL:
1385                     drawText(context2d, this.node.minX, (this.node.minY + this.node.maxY) / 2+5,
1386                             this.node.getText());
1387                     break;
1388                 case Activity.FOCUS:
1389                     this.node.hoveHighlight(context2d);
1390                     break;
1391                 case Activity.PICKED:
1392                     this.node.checkedHighlight(context2d);
1393                     break;
1394                 }
1395 
1396             };
1397             Actor.LENGTH=16;
1398             
1399             /*
1400             *定义一个带箭头的连线
1401             */
1402             function Arrow(id1,id2)
1403             {
1404                 this.id1=id1;
1405                 this.id2=id2;
1406                 var self=this;
1407                 this.conditions=[{"field":"case","name":"判断","value":"","group":"条件(->"+id2+")","editor":{"type":"textbox","options":{"required": true},"onChange":function(newValue,oldValue){
1408                    if(newValue && oldValue && newValue!=oldValue)
1409                         {
1410                         self.condition(newValue);
1411                         }
1412                        }}},
1413                   {"field":"status","name":"跳转","value":id2,"group":"条件(->"+id2+")"}
1414                    ];
1415                 
1416             }
1417             //getter 条件
1418             Arrow.prototype.toJson=function(){
1419                 return {id2:this.id2,condition:this.condition()};
1420             };
1421             //setter/getter判断值
1422             Arrow.prototype.condition=function(cond){
1423                 return cond?(this.conditions[0].value=cond):this.conditions[0].value;
1424             };
1425             //getter
1426             Arrow.prototype.id1=function(){
1427                 return this.id1;
1428             };
1429             Arrow.prototype.id2=function(){
1430                 return this.id2;
1431             };
1432             Arrow.prototype.draw=function(context2d,parent,child){
1433                 //画连线
1434                 drawLine(context2d, parent.node.maxX,
1435                         (parent.node.minY + parent.node.maxY) / 2,
1436                         child.node.minX,
1437                         (child.node.minY + child.node.maxY) / 2);
1438                 //绘制箭头
1439                 drawArrow(context2d, parent.node.maxX,
1440                         (parent.node.minY + parent.node.maxY) / 2,
1441                         child.node.minX,
1442                         (child.node.minY + child.node.maxY) / 2);
1443                                         
1444             };
1445             /*
1446             *定义虚拟光标类:Cursor
1447             */
1448             function Cursor(workflow,x,y,type)
1449             {
1450                 this.x=x;
1451                 this.y=y;
1452                 this.type=type;
1453                 this.workflow=workflow;
1454             }
1455             
1456             Cursor.prototype.draw=function(context2d){
1457                 switch(this.type)
1458                 {
1459                 case 'Activity':
1460                     drawText(context2d, this.x, this.y+5,
1461                             '新建节点');
1462                     break;
1463                 case 'Actor':
1464                     //画连线
1465                     drawLine(context2d, (this.workflow.checked.node.minX+this.workflow.checked.node.maxX)/2,
1466                             this.workflow.checked.node.minY,
1467                             this.x+Actor.LENGTH/2,this.y+Actor.LENGTH);
1468                     drawImage(context2d,this.x,this.y,Actor.LENGTH,Actor.LENGTH);
1469                     break;
1470                 case 'Arrow':
1471                     //画连线
1472                     drawLine(context2d, this.workflow.checked.node.maxX,
1473                             (this.workflow.checked.node.minY + this.workflow.checked.node.maxY) / 2,
1474                             this.x,this.y);
1475                     drawArrow(context2d, this.x-10,    this.y,    this.x,    this.y);
1476                     break;
1477                 }
1478             };
1479             WorkflowEngine.initialize();
1480             
1481         });
1482     </script>
1483 </body>
1484 </html>

浏览器里的效果图如下:

posted @ 2017-03-20 16:58  江金汉  阅读(640)  评论(0编辑  收藏  举报