(function(snaker){
	var designer={};
	designer.config={
		editable:true,
		lineHeight:15,
		basePath:"",
        ctxPath:"",
        orderId:"",
		rect:{
			attr:{
				x:10,y:10,width:100,height:50,r:5,fill:"90-#fff-#C0C0C0",stroke:"#000","stroke-width":1
			},
			showType:"image&text",
			type:"state",
			name:{text:"state","font-style":"italic"},
			text:{text:"状态","font-size":13},
			margin:5,
			props:[],
			img:{}
		},
		path:{
			attr:{
				path:{path:"M10 10L100 100",stroke:"#808080",fill:"none","stroke-width":2},
				arrow:{path:"M10 10L10 10",stroke:"#808080",fill:"#808080","stroke-width":2,radius:4},
				fromDot:{width:5,height:5,stroke:"#fff",fill:"#000",cursor:"move","stroke-width":2},
				toDot:{width:5,height:5,stroke:"#fff",fill:"#000",cursor:"move","stroke-width":2},
				bigDot:{width:5,height:5,stroke:"#fff",fill:"#000",cursor:"move","stroke-width":2},
				smallDot:{width:5,height:5,stroke:"#fff",fill:"#000",cursor:"move","stroke-width":3}
			},
			text:{text:"",cursor:"move",background:"#000"},
			textPos:{x:0,y:-10},
			props:{
				name:{name:"name",label:"名称",value:"",editor:function(){return new designer.editors.inputEditor()}},
				displayName:{name:"displayName",label:"显示",value:"",editor:function(){return new designer.editors.textEditor()}},
                expr:{name:"expr",label:"表达式",value:"",editor:function(){return new designer.editors.inputEditor()}}
			}
		},
		tools:{
			attr:{
				left:10,top:10
			},
			pointer:{},
			path:{},
			states:{},
			save:{
				onclick:function(c){alert(c)}
			}
		},
		props:{
			attr:{top:328,left:10},
			props:{}
		},
		restore:"",
		activeRects:{
			rects:[],
			rectAttr:{stroke:"#ff0000","stroke-width":2}
		},
		historyRects:{
			rects:[],
            rectAttr:{stroke:"#00ff00","stroke-width":2},
			pathAttr:{
				path:{stroke:"#00ff00"},
				arrow:{stroke:"#00ff00",fill:"#00ff00"}
			}
		}
	};
    designer.util = {
        isLine : function(p1, p2, p3) {// 三个点是否在一条直线上
            var s, p2y;
            if ((p1.x - p3.x) == 0)
                s = 1;
            else
                s = (p1.y - p3.y) / (p1.x - p3.x);
            p2y = (p2.x - p3.x) * s + p3.y;
            // $('body').append(p2.y+'-'+p2y+'='+(p2.y-p2y)+', ');
            if ((p2.y - p2y) < 10 && (p2.y - p2y) > -10) {
                p2.y = p2y;
                return true;
            }
            return false;
        },
        center : function(p1, p2) {// 两个点的中间点
            return {
                x : (p1.x - p2.x) / 2 + p2.x,
                y : (p1.y - p2.y) / 2 + p2.y
            };
        },
        nextId : (function() {
            var uid = 0;
            return function() {
                return ++uid;
            };
        })(),

        connPoint : function(rect, p) {// 计算矩形中心到p的连线与矩形的交叉点
            var start = p, end = {
                x : rect.x + rect.width / 2,
                y : rect.y + rect.height / 2
            };
            // 计算正切角度
            var tag = (end.y - start.y) / (end.x - start.x);
            tag = isNaN(tag) ? 0 : tag;

            var rectTag = rect.height / rect.width;
            // 计算箭头位置
            var xFlag = start.y < end.y ? -1 : 1, yFlag = start.x < end.x
                ? -1
                : 1, arrowTop, arrowLeft;
            // 按角度判断箭头位置
            if (Math.abs(tag) > rectTag && xFlag == -1) {// top边
                arrowTop = end.y - rect.height / 2;
                arrowLeft = end.x + xFlag * rect.height / 2 / tag;
            } else if (Math.abs(tag) > rectTag && xFlag == 1) {// bottom边
                arrowTop = end.y + rect.height / 2;
                arrowLeft = end.x + xFlag * rect.height / 2 / tag;
            } else if (Math.abs(tag) < rectTag && yFlag == -1) {// left边
                arrowTop = end.y + yFlag * rect.width / 2 * tag;
                arrowLeft = end.x - rect.width / 2;
            } else if (Math.abs(tag) < rectTag && yFlag == 1) {// right边
                arrowTop = end.y + rect.width / 2 * tag;
                arrowLeft = end.x + rect.width / 2;
            }
            return {
                x : arrowLeft,
                y : arrowTop
            };
        },

        arrow : function(p1, p2, r) {// 画箭头，p1 开始位置,p2 结束位置, r前头的边长
            var atan = Math.atan2(p1.y - p2.y, p2.x - p1.x) * (180 / Math.PI);

            var centerX = p2.x - r * Math.cos(atan * (Math.PI / 180));
            var centerY = p2.y + r * Math.sin(atan * (Math.PI / 180));

            var x2 = centerX + r * Math.cos((atan + 120) * (Math.PI / 180));
            var y2 = centerY - r * Math.sin((atan + 120) * (Math.PI / 180));

            var x3 = centerX + r * Math.cos((atan + 240) * (Math.PI / 180));
            var y3 = centerY - r * Math.sin((atan + 240) * (Math.PI / 180));
            return [p2, {
                x : x2,
                y : y2
            }, {
                x : x3,
                y : y3
            }];
        },

        tip : function(rect, name) {
        	if(workFlow){
        	var ar = designer.config.activeRects;
        	var matched = false;
        	for(var u=0;u<ar.rects.length;u++){
				if(ar.rects[u].name == name){
					matched = true;
				}
        	}
            var tipDIV = document.getElementById("tipDIV");
            if (tipDIV) {
                document.body.removeChild(tipDIV);
            }
            tipDIV = document.createElement("tipDIV");
            tipDIV.id = "tipDIV";
            tipDIV.style.styleFloat="left";
            tipDIV.style.overflow="hidden";
            tipDIV.style.left=document.getElementById('snakerflow').offsetLeft+rect.attr('x');
            tipDIV.style.top=$("#snakerflow").position().top+rect.attr('y')+52;
            tipDIV.style.width=180;
            tipDIV.style.height=60;
            tipDIV.style.position="absolute";
            tipDIV.style.backgroundColor="#FFE699";
            $.ajax({
                type:'GET',
                url:designer.config.ctxPath+"/tip_workflow.action?r="+Math.random(),
                data:"orderId=" + designer.config.orderId + "&taskName=" + name,//_o.props['name'].value,
                dataType: 'json',
        		async: false,
                error: function(){
                    alert('数据处理错误！');
                    return false;
                },
                success: function(data){
                	if(data.actors!=""){
                        tipDIV.innerHTML="<div style='width:180px; height:20px;border: 1px solid #d2dde2;'><a href='javascript:void(0)' onclick='doWorkFlowReminders(\""+designer.config.orderId+"\",\""+name+"\")' class='btnClock' title='流程催办'></a></div><div style='width:180px; height:40px;border: 1px solid #d2dde2;'><div id='currentActorDIV' style='width:180px; height:20px;'>参与者:" + data.actors + "</div><div id='arrivalDIV' style='width:180px; height:20px;'>抵达时间:" + data.createTime + "</div></div>";
                        document.body.appendChild(tipDIV);
                	}
                }
            });
        }
        }
    }
	designer.rect=function(o, r){
        var _this = this, _uid = 'rect' + designer.util.nextId(), _o = snaker.extend(true, {},
                designer.config.rect, o), _id = _uid, _r = r, // Raphael画笔
            _rect, _img, // 图标
            _name, // 状态名称
            _text, // 显示文本
            _ox, _oy; // 拖动时，保存起点位置;

        _rect = _r.rect(_o.attr.x, _o.attr.y, _o.attr.width, _o.attr.height,
            _o.attr.r).hide().attr(_o.attr);

        _img = _r.image(designer.config.basePath + _o.img.src,
            _o.attr.x + _o.img.width / 2,
            _o.attr.y + (_o.attr.height - _o.img.height) / 2, _o.img.width,
            _o.img.height).hide();
        _name = _r.text(
                _o.attr.x + _o.img.width + (_o.attr.width - _o.img.width) / 2,
                _o.attr.y + designer.config.lineHeight / 2, _o.name.text).hide()
            .attr(_o.name);
        _text = _r.text(
                _o.attr.x + _o.img.width + (_o.attr.width - _o.img.width) / 2,
                _o.attr.y + (_o.attr.height - designer.config.lineHeight) / 2
                    + designer.config.lineHeight, _o.text.text).hide()
            .attr(_o.text);// 文本
        // 拖动处理----------------------------------------
        _rect.drag(function(dx, dy) {
            dragMove(dx, dy);
        }, function() {
            dragStart()
        }, function() {
            dragUp();
        });
        _text.click(function(){
            if (!designer.config.editable) {
                designer.util.tip(_rect, _o.props['name'].value);
            } else {
                /*var returnValue = window.showModalDialog(designer.config.ctxPath + '/config/form?lookup=1',window,'dialogWidth:1000px;dialogHeight:600px');
                if(returnValue) {
                    var formPath = "/config/form/use/" + returnValue;
                    _o.props.form.value = formPath;
                    document.getElementById("pform").innerHTML = '<input style="width:98%;" value="' + formPath + '"/>';
                }*/
            }
        });
        _rect.click(function(){
            if (!designer.config.editable) {
                designer.util.tip(_rect, _o.props['name'].value);
            }
        });
        _rect.dblclick(function(){
            /*if (designer.config.editable) {
                var returnValue = window.showModalDialog(designer.config.ctxPath + '/config/form?lookup=1',window,'dialogWidth:1000px;dialogHeight:600px');
                if(returnValue) {
                    var formPath = "/config/form/use/" + returnValue;
                    _o.props.form.value = formPath;
                    document.getElementById("pform").innerHTML = '<input style="width:98%;" value="' + formPath + '"/>';
                }
            }*/
        });
        _img.drag(function(dx, dy) {
            dragMove(dx, dy);
        }, function() {
            dragStart()
        }, function() {
            dragUp();
        });
        _name.drag(function(dx, dy) {
            dragMove(dx, dy);
        }, function() {
            dragStart()
        }, function() {
            dragUp();
        });
        _text.drag(function(dx, dy) {
            dragMove(dx, dy);
        }, function() {
            dragStart()
        }, function() {
            dragUp();
        });
        var dragMove = function(dx, dy) {// 拖动中
            if (!designer.config.editable)
                return;

            var x = (_ox + dx);// -((_ox+dx)%10);
            var y = (_oy + dy);// -((_oy+dy)%10);

            _bbox.x = x - _o.margin;
            _bbox.y = y - _o.margin;
            resize();
        };

        var dragStart = function() {// 开始拖动
            _ox = _rect.attr("x");
            _oy = _rect.attr("y");
            _rect.attr({
                opacity : 0.5
            });
            _img.attr({
                opacity : 0.5
            });
            _text.attr({
                opacity : 0.5
            });
        };

        var dragUp = function() {// 拖动结束
            _rect.attr({
                opacity : 1
            });
            _img.attr({
                opacity : 1
            });
            _text.attr({
                opacity : 1
            });
        };
        // 改变大小的边框
        var _bpath, _bdots = {}, _bw = 5, _bbox = {
            x : _o.attr.x - _o.margin,
            y : _o.attr.y - _o.margin,
            width : _o.attr.width + _o.margin * 2,
            height : _o.attr.height + _o.margin * 2
        };

        _bpath = _r.path('M0 0L1 1').hide();
        _bdots['t'] = _r.rect(0, 0, _bw, _bw).attr({
            fill : '#000',
            stroke : '#fff',
            cursor : 's-resize'
        }).hide().drag(function(dx, dy) {
            bdragMove(dx, dy, 't');
        }, function() {
            bdragStart(this.attr('x') + _bw / 2, this.attr('y') + _bw
                / 2, 't');
        }, function() {
        });// 上
        _bdots['lt'] = _r.rect(0, 0, _bw, _bw).attr({
            fill : '#000',
            stroke : '#fff',
            cursor : 'nw-resize'
        }).hide().drag(function(dx, dy) {
            bdragMove(dx, dy, 'lt');
        }, function() {
            bdragStart(this.attr('x') + _bw / 2, this.attr('y') + _bw
                / 2, 'lt');
        }, function() {
        });// 左上
        _bdots['l'] = _r.rect(0, 0, _bw, _bw).attr({
            fill : '#000',
            stroke : '#fff',
            cursor : 'w-resize'
        }).hide().drag(function(dx, dy) {
            bdragMove(dx, dy, 'l');
        }, function() {
            bdragStart(this.attr('x') + _bw / 2, this.attr('y') + _bw
                / 2, 'l');
        }, function() {
        });// 左
        _bdots['lb'] = _r.rect(0, 0, _bw, _bw).attr({
            fill : '#000',
            stroke : '#fff',
            cursor : 'sw-resize'
        }).hide().drag(function(dx, dy) {
            bdragMove(dx, dy, 'lb');
        }, function() {
            bdragStart(this.attr('x') + _bw / 2, this.attr('y') + _bw
                / 2, 'lb');
        }, function() {
        });// 左下
        _bdots['b'] = _r.rect(0, 0, _bw, _bw).attr({
            fill : '#000',
            stroke : '#fff',
            cursor : 's-resize'
        }).hide().drag(function(dx, dy) {
            bdragMove(dx, dy, 'b');
        }, function() {
            bdragStart(this.attr('x') + _bw / 2, this.attr('y') + _bw
                / 2, 'b');
        }, function() {
        });// 下
        _bdots['rb'] = _r.rect(0, 0, _bw, _bw).attr({
            fill : '#000',
            stroke : '#fff',
            cursor : 'se-resize'
        }).hide().drag(function(dx, dy) {
            bdragMove(dx, dy, 'rb');
        }, function() {
            bdragStart(this.attr('x') + _bw / 2, this.attr('y') + _bw
                / 2, 'rb');
        }, function() {
        });// 右下
        _bdots['r'] = _r.rect(0, 0, _bw, _bw).attr({
            fill : '#000',
            stroke : '#fff',
            cursor : 'w-resize'
        }).hide().drag(function(dx, dy) {
            bdragMove(dx, dy, 'r');
        }, function() {
            bdragStart(this.attr('x') + _bw / 2, this.attr('y') + _bw
                / 2, 'r')
        }, function() {
        });// 右
        _bdots['rt'] = _r.rect(0, 0, _bw, _bw).attr({
            fill : '#000',
            stroke : '#fff',
            cursor : 'ne-resize'
        }).hide().drag(function(dx, dy) {
            bdragMove(dx, dy, 'rt');
        }, function() {
            bdragStart(this.attr('x') + _bw / 2, this.attr('y') + _bw
                / 2, 'rt')
        }, function() {
        });// 右上
        $([_bdots['t'].node,_bdots['lt'].node,_bdots['l'].node,_bdots['lb'].node,_bdots['b'].node,_bdots['rb'].node,_bdots['r'].node,_bdots['rt'].node]).click(function(){return false;});

        var bdragMove = function(dx, dy, t) {
            if (!designer.config.editable)
                return;
            var x = _bx + dx, y = _by + dy;
            switch (t) {
                case 't' :
                    _bbox.height += _bbox.y - y;
                    _bbox.y = y;
                    break;
                case 'lt' :
                    _bbox.width += _bbox.x - x;
                    _bbox.height += _bbox.y - y;
                    _bbox.x = x;
                    _bbox.y = y;
                    break;
                case 'l' :
                    _bbox.width += _bbox.x - x;
                    _bbox.x = x;
                    break;
                case 'lb' :
                    _bbox.height = y - _bbox.y;
                    _bbox.width += _bbox.x - x;
                    _bbox.x = x;
                    break;
                case 'b' :
                    _bbox.height = y - _bbox.y;
                    break;
                case 'rb' :
                    _bbox.height = y - _bbox.y;
                    _bbox.width = x - _bbox.x;
                    break;
                case 'r' :
                    _bbox.width = x - _bbox.x;
                    break;
                case 'rt' :
                    _bbox.width = x - _bbox.x;
                    _bbox.height += _bbox.y - y;
                    _bbox.y = y;
                    break;
            }
            resize();
            // $('body').append(t);
        };
        var bdragStart = function(ox, oy, t) {
            _bx = ox;
            _by = oy;
        };

        // 事件处理--------------------------------
        snaker([_rect.node, _text.node, _name.node, _img.node]).bind('click',
            function() {
                if (!designer.config.editable)
                    return;

                showBox();
                var mod = snaker(_r).data('mod');
                switch (mod) {
                    case 'pointer' :
                        break;
                    case 'path' :
                        var pre = snaker(_r).data('currNode');
                        if (pre && pre.getId() != _id
                            && pre.getId().substring(0, 4) == 'rect') {
                            snaker(_r).trigger('addpath', [pre, _this]);
                        }
                        break;
                }
                snaker(_r).trigger('click', _this);
                snaker(_r).data('currNode', _this);
                return false;
            });

        var clickHandler = function(e, src) {
            if (!designer.config.editable)
                return;
            if (src.getId() == _id) {
                snaker(_r).trigger('showprops', [_o.props, src]);
            } else {
                hideBox();
            }
        };
        snaker(_r).bind('click', clickHandler);

        var textchangeHandler = function(e, text, src) {
            if (src.getId() == _id) {
                _text.attr({
                    text : text
                });
            }
        };
        snaker(_r).bind('textchange', textchangeHandler);

        // 私有函数-----------------------
        // 边框路径
        function getBoxPathString() {
            return 'M' + _bbox.x + ' ' + _bbox.y + 'L' + _bbox.x + ' '
                + (_bbox.y + _bbox.height) + 'L' + (_bbox.x + _bbox.width)
                + ' ' + (_bbox.y + _bbox.height) + 'L'
                + (_bbox.x + _bbox.width) + ' ' + _bbox.y + 'L' + _bbox.x
                + ' ' + _bbox.y;
        }
        // 显示边框
        function showBox() {
            _bpath.show();
            for (var k in _bdots) {
                _bdots[k].show();
            }
        }
        // 隐藏
        function hideBox() {
            _bpath.hide();
            for (var k in _bdots) {
                _bdots[k].hide();
            }
        }

        // 根据_bbox，更新位置信息
        function resize() {
            var rx = _bbox.x + _o.margin, ry = _bbox.y + _o.margin, rw = _bbox.width
                - _o.margin * 2, rh = _bbox.height - _o.margin * 2;

            _rect.attr({
                x : rx,
                y : ry,
                width : rw,
                height : rh
            });
            switch (_o.showType) {
                case 'image' :
                    _img.attr({
                        x : rx + (rw - _o.img.width) / 2,
                        y : ry + (rh - _o.img.height) / 2
                    }).show();
                    break;
                case 'text' :
                    _rect.show();
                    _text.attr({
                        x : rx + rw / 2,
                        y : ry + rh / 2
                    }).show();// 文本
                    break;
                case 'image&text' :
                    _rect.show();
                    _name.attr({
                        x : rx + _o.img.width + (rw - _o.img.width) / 2,
                        y : ry + designer.config.lineHeight / 2
                    }).show();
                    _text.attr({
                        x : rx + _o.img.width + (rw - _o.img.width) / 2,
                        y : ry + (rh - designer.config.lineHeight) / 2
                            + designer.config.lineHeight
                    }).show();// 文本
                    _img.attr({
                        x : rx + _o.img.width / 2,
                        y : ry + (rh - _o.img.height) / 2
                    }).show();
                    break;
            }

            _bdots['t'].attr({
                x : _bbox.x + _bbox.width / 2 - _bw / 2,
                y : _bbox.y - _bw / 2
            });// 上
            _bdots['lt'].attr({
                x : _bbox.x - _bw / 2,
                y : _bbox.y - _bw / 2
            });// 左上
            _bdots['l'].attr({
                x : _bbox.x - _bw / 2,
                y : _bbox.y - _bw / 2 + _bbox.height / 2
            });// 左
            _bdots['lb'].attr({
                x : _bbox.x - _bw / 2,
                y : _bbox.y - _bw / 2 + _bbox.height
            });// 左下
            _bdots['b'].attr({
                x : _bbox.x - _bw / 2 + _bbox.width / 2,
                y : _bbox.y - _bw / 2 + _bbox.height
            });// 下
            _bdots['rb'].attr({
                x : _bbox.x - _bw / 2 + _bbox.width,
                y : _bbox.y - _bw / 2 + _bbox.height
            });// 右下
            _bdots['r'].attr({
                x : _bbox.x - _bw / 2 + _bbox.width,
                y : _bbox.y - _bw / 2 + _bbox.height / 2
            });// 右
            _bdots['rt'].attr({
                x : _bbox.x - _bw / 2 + _bbox.width,
                y : _bbox.y - _bw / 2
            });// 右上
            _bpath.attr({
                path : getBoxPathString()
            });

            snaker(_r).trigger('rectresize', _this);
        };

        // 函数----------------
        // 转化json字串
        this.toJson = function() {
            var data = "{type:'" + _o.type + "',text:{text:'"
                + _text.attr('text') + "'}, attr:{ x:"
                + Math.round(_rect.attr('x')) + ", y:"
                + Math.round(_rect.attr('y')) + ", width:"
                + Math.round(_rect.attr('width')) + ", height:"
                + Math.round(_rect.attr('height')) + "}, props:{";
            for (var k in _o.props) {
                data += k + ":{value:'"
                    + _o.props[k].value + "'},";
            }
            if (data.substring(data.length - 1, data.length) == ',')
                data = data.substring(0, data.length - 1);
            data += "}}";
            return data;
        };
        this.toBeforeXml=function(){
            var data="<"+_o.type+" layout=\""+(Math.round(_rect.attr("x"))-180)+","+Math.round(_rect.attr("y"))+","+Math.round(_rect.attr("width"))+","+Math.round(_rect.attr("height"))+"\" ";
            for(var k in _o.props){
                if(k=="name"&&_o.props[k].value==""){
                    alert(_o.type+" 名称 不能为空");
                    return ""
                }
                if(k=="layout") continue;
                if(_o.props[k].value!=""){
                    var vv = _o.props[k].value.replace(/>/g,"#5");
                    vv = vv.replace(/</g,"#6");
                    vv = vv.replace(/&/g,"#7");
                    data+=k+"=\""+vv+"\" "
                }
            }
            data+=">";
            return data
        };
        this.toAfterXml=function(){
            var data="</"+_o.type+">";
            return data
        };
        // 从数据中恢复图
        this.restore = function(data) {
            var obj = data;
            // if (typeof data === 'string')
            // obj = eval(data);

            _o = snaker.extend(true, _o, data);

            _text.attr({
                text : obj.text.text
            });
            resize();
        };
        this.getName=function(){
            for(var k in _o.props){
                if(k=="name"){
                    return _o.props[k].value;
                }
            }
        }
        this.getBBox = function() {
            return _bbox;
        };
        this.getId = function() {
            return _id;
        };
        this.remove = function() {
            _rect.remove();
            _text.remove();
            _name.remove();
            _img.remove();
            _bpath.remove();
            for (var k in _bdots) {
                _bdots[k].remove();
            }
        };
        this.text = function() {
            return _text.attr('text');
        };
        this.attr = function(attr) {
            if (attr)
                _rect.attr(attr);
        };

        resize();// 初始化位置
    };
    designer.path=function(o, r, from, to){
        var _this = this, _r = r, _o = snaker.extend(true,{},designer.config.path),_path, _arrow, _text, _textPos = _o.textPos,
            _ox, _oy, _from = from, _to = to, _id = "path"+designer.util.nextId(), _dotList;
        function dot(type, pos, left, right){
            var _this = this, _t = type, _n, _lt = left, _rt = right, _ox, _oy, _pos = pos;
            switch(_t){
                case"from":_n=_r.rect(pos.x-_o.attr.fromDot.width/2,pos.y-_o.attr.fromDot.height/2,_o.attr.fromDot.width,_o.attr.fromDot.height).attr(_o.attr.fromDot);break;
                case"big":_n=_r.rect(pos.x-_o.attr.bigDot.width/2,pos.y-_o.attr.bigDot.height/2,_o.attr.bigDot.width,_o.attr.bigDot.height).attr(_o.attr.bigDot);break;
                case"small":_n=_r.rect(pos.x-_o.attr.smallDot.width/2,pos.y-_o.attr.smallDot.height/2,_o.attr.smallDot.width,_o.attr.smallDot.height).attr(_o.attr.smallDot);break;
                case"to":_n=_r.rect(pos.x-_o.attr.toDot.width/2,pos.y-_o.attr.toDot.height/2,_o.attr.toDot.width,_o.attr.toDot.height).attr(_o.attr.toDot);break
            }
            if(_n&&(_t=="big"||_t=="small")){
                _n.drag(function(dx, dy){
                    dragMove(dx,dy)
                },function(){
                    dragStart()
                },function(){
                    dragUp()
                });
                var dragMove = function(dx, dy){
                    var x = (_ox+dx), y=(_oy+dy);
                    _this.moveTo(x,y)
                };
                var dragStart = function(){
                    if(_t=="big"){_ox=_n.attr("x")+_o.attr.bigDot.width/2;_oy=_n.attr("y")+_o.attr.bigDot.height/2}
                    if(_t=="small"){_ox=_n.attr("x")+_o.attr.smallDot.width/2;_oy=_n.attr("y")+_o.attr.smallDot.height/2}
                };
                var dragUp = function(){}
            }
            this.type=function(P){if(P){_t=P}else{return _t}};
            this.node=function(P){if(P){_n=P}else{return _n}};
            this.left=function(P){if(P){_lt=P}else{return _lt}};
            this.right=function(P){if(P){_rt=P}else{return _rt}};
            this.remove=function(){_lt=null;_rt=null;_n.remove()};
            this.pos=function(P){if(P){_pos=P;_n.attr({x:_pos.x-_n.attr("width")/2,y:_pos.y-_n.attr("height")/2});return this}else{return _pos}};
            this.moveTo=function(Q,T){
                this.pos({x:Q,y:T});
                switch(_t){
                    case"from":
                    	if(_rt&&_rt.right()&&_rt.right().type()=="to"){
                    		_rt.right().pos(designer.util.connPoint(_to.getBBox(),_pos))
                    	}
                    	if(_rt&&_rt.right()){
                    		_rt.pos(designer.util.center(_pos,_rt.right().pos()))
                    	}
                    	break;
                    case"big":
                    	if(_rt&&_rt.right()&&_rt.right().type()=="to"){
                    		_rt.right().pos(designer.util.connPoint(_to.getBBox(),_pos))
                    	}
                    	if(_lt&&_lt.left()&&_lt.left().type()=="from"){
                    		_lt.left().pos(designer.util.connPoint(_from.getBBox(),_pos))
                    	}
                    	if(_rt&&_rt.right()){
                    		_rt.pos(designer.util.center(_pos,_rt.right().pos()))
                    	}
                    	if(_lt&&_lt.left()){
                    		_lt.pos(designer.util.center(_pos,_lt.left().pos()))
                    	}
                    	var S={x:_pos.x,y:_pos.y};
                    	if(designer.util.isLine(_lt.left().pos(),S,_rt.right().pos())){
                    		_t="small";
                    		_n.attr(_o.attr.smallDot);
                    		this.pos(S);
                    		var P=_lt;
                    		_lt.left().right(_lt.right());
                    		_lt=_lt.left();P.remove();
                    		var R=_rt;
                    		_rt.right().left(_rt.left());
                    		_rt=_rt.right();
                    		R.remove()
                    	}
                    	break;
                    case"small":
                    	if(_lt&&_rt&&!designer.util.isLine(_lt.pos(),{x:_pos.x,y:_pos.y},_rt.pos())){
                    		_t="big";
                    		_n.attr(_o.attr.bigDot);
                    		var P=new dot("small",designer.util.center(_lt.pos(),_pos),_lt,_lt.right());
                    		_lt.right(P);
                    		_lt=P;
                    		var R=new dot("small",designer.util.center(_rt.pos(),_pos),_rt.left(),_rt);
                    		_rt.left(R);
                    		_rt=R
                    	}
                    	break;
                    case"to":if(_lt&&_lt.left()&&_lt.left().type()=="from"){_lt.left().pos(designer.util.connPoint(_from.getBBox(),_pos))}if(_lt&&_lt.left()){_lt.pos(designer.util.center(_pos,_lt.left().pos()))}break
                }
                refreshpath()
            }
        }
        function dotList(){
            var _fromDot, _toDot, _fromBB = _from.getBBox(), _toBB = _to.getBBox(),_fromPos, o;
            _fromPos=designer.util.connPoint(_fromBB,{x:_toBB.x+_toBB.width/2,y:_toBB.y+_toBB.height/2});
            o=designer.util.connPoint(_toBB,_fromPos);
            _fromDot=new dot("from",_fromPos,null,new dot("small",{x:(_fromPos.x+o.x)/2,y:(_fromPos.y+o.y)/2}));
            _fromDot.right().left(_fromDot);
            _toDot=new dot("to",o,_fromDot.right(),null);
            _fromDot.right().right(_toDot);
            this.toPathString=function(){
                if(!_fromDot){return""}
                var d = _fromDot, p = "M"+d.pos().x+" "+d.pos().y, arr = "";
                while(d.right()){
                    d=d.right();
                    p+="L"+d.pos().x+" "+d.pos().y
                }
                var arrPos = designer.util.arrow(d.left().pos(),d.pos(),_o.attr.arrow.radius);
                arr="M"+arrPos[0].x+" "+arrPos[0].y+"L"+arrPos[1].x+" "+arrPos[1].y+"L"+arrPos[2].x+" "+arrPos[2].y+"z";
                return[p,arr]
            };
            this.toJson=function(){
                var data = "[", d = _fromDot;
                while(d){
                    if(d.type()=="big"){
                        data+="{_dotList:"+Math.round(d.pos().x)+",_ox:"+Math.round(d.pos().y)+"},"
                    }
                    d=d.right()
                }
                if(data.substring(data.length-1,data.length)==","){
                    data=data.substring(0,data.length-1)
                }
                data+="]";
                return data
            };

            this.toXml=function(){
                var data = "", d = _fromDot;
                while(d){
                    if(d.type()=="big"){
                        data+=(Math.round(d.pos().x)-180)+","+Math.round(d.pos().y)+";"
                    }
                    d=d.right()
                }
                if(data.substring(data.length-1,data.length)==";"){
                    data=data.substring(0,data.length-1)
                }
                return data
            };
            this.restore=function(data){
                var obj = data,d = _fromDot.right();
                for(var i = 0; i < obj.length; i++){
                    d.moveTo(obj[i].x,obj[i].y);
                    d.moveTo(obj[i].x,obj[i].y);
                    d=d.right()
                }
                this.hide()
            };
            this.fromDot=function(){return _fromDot};
            this.toDot=function(){return _toDot};
            this.midDot=function(){
                var mid = _fromDot.right(), end = _fromDot.right().right();
                while(end.right()&&end.right().right()){
                    end=end.right().right();
                    mid=mid.right()
                }
                return mid
            };
            this.show=function(){
                var d = _fromDot;
                while(d){
                    d.node().show();
                    d=d.right()
                }
            };
            this.hide=function(){
                var d = _fromDot;
                while(d){
                    d.node().hide();
                    d=d.right()
                }
            };
            this.remove=function(){
                var d = _fromDot;
                while(d){
                    if(d.right()){
                        d=d.right();
                        d.left().remove()
                    }else{
                        d.remove();
                        d=null
                    }
                }
            }
        }
        _o=snaker.extend(true,_o,o);
        _path=_r.path(_o.attr.path.path).attr(_o.attr.path);
        _arrow=_r.path(_o.attr.arrow.path).attr(_o.attr.arrow);
        _dotList=new dotList();
        _dotList.hide();
        _text=_r.text(0,0,_o.text.text).attr(_o.text).attr({text:_o.text.text.replace("{from}",_from.text()).replace("{to}",_to.text())});
        _text.drag(function(r,o){
                if(!designer.config.editable){return}
                _text.attr({x:_ox+r,y:_oy+o})
            },
            function(){_ox=_text.attr("x");_oy=_text.attr("y")},
            function(){
                var o=_dotList.midDot().pos();
                _textPos ={x:_text.attr("x")-o.x,y:_text.attr("y")-o.y}
            });
        refreshpath();
        snaker([_path.node,_arrow.node]).bind("click",function(){
            if(!designer.config.editable){return}
            snaker(_r).trigger("click",_this);
            snaker(_r).data("currNode",_this);
            return false
        });
        var clickHandler = function(e, src){
            if(!designer.config.editable){return}
            if(src&&src.getId()==_id){
                _dotList.show();
                snaker(_r).trigger("showprops",[_o.props,_this])
            }else{
                _dotList.hide()
            }
            var o = snaker(_r).data("mod");
            switch(o){
                case"pointer":break;
                case"path":break
            }
        };
        snaker(_r).bind("click",clickHandler);
        var removerectHandler = function(e, src){
            if(!designer.config.editable){return}
            if(src&&(src.getId()==_from.getId()||src.getId()==_to.getId())){
                snaker(_r).trigger("removepath",_this)
            }
        };
        snaker(_r).bind("removerect",removerectHandler);
        var rectresizeHandler = function(e, src){
            if(!designer.config.editable){return}
            if(_from&&_from.getId()==src.getId()){
                var o;
                if(_dotList.fromDot().right().right().type()=="to"){
                    o={x:_to.getBBox().x+_to.getBBox().width/2,y:_to.getBBox().y+_to.getBBox().height/2}
                }else{
                    o=_dotList.fromDot().right().right().pos()
                }
                var r=designer.util.connPoint(_from.getBBox(),o);
                _dotList.fromDot().moveTo(r.x,r.y);
                refreshpath()
            }
            if(_to&&_to.getId()==src.getId()){
                var o;
                if(_dotList.toDot().left().left().type()=="from"){
                    o={x:_from.getBBox().x+_from.getBBox().width/2,y:_from.getBBox().y+_from.getBBox().height/2}
                }else{
                    o=_dotList.toDot().left().left().pos()
                }
                var r=designer.util.connPoint(_to.getBBox(),o);
                _dotList.toDot().moveTo(r.x,r.y);
                refreshpath()
            }
        };
        snaker(_r).bind("rectresize",rectresizeHandler);
        var textchangeHandler = function(e, v, src){
            if(src.getId()==_id){
                _text.attr({text:v})
            }
        };
        snaker(_r).bind("textchange",textchangeHandler);
        this.from=function(){return _from};
        this.to=function(){return _to};
        this.toJson=function(){
            var r="{from:'"+_from.getId()+"',to:'"+_to.getId()+"', dots:"+_dotList.toJson()+",text:{text:'"+_text.attr("text")+"'},textPos:{_dotList:"+Math.round(_textPos .x)+",_ox:"+Math.round(_textPos .y)+"}, props:{";
            for(var o in _o.props){
                r+=o+":{value:'"+_o.props[o].value+"'},"
            }
            if(r.substring(r.length-1,r.length)==","){
                r=r.substring(0,r.length-1)
            }
            r+="}}";
            return r
        };

        this.toXml=function(){
            var hx = Math.round(_textPos .x);
            var r="<transition offset=\""+hx+","+Math.round(_textPos .y)+"\" to=\""+_to.getName()+"\" ";
            var dots=_dotList.toXml();
            if(dots!="") r+=" g=\""+_dotList.toXml()+"\" ";
            for(var o in _o.props){
                if(o=="name"&&_o.props[o].value==""){
                    r+=o+"=\""+_id+"\" ";
                    continue;
                }
                if(_o.props[o].value!=""){
                    var vv = _o.props[o].value.replace(/>/g,"#5");
                    vv = vv.replace(/</g,"#6");
                    vv = vv.replace(/&/g,"#7");
                    r+=o+"=\""+vv+"\" ";
                }
            }
            r+="/>";
            return r
        };
        this.restore=function(data){
            var obj = data;
            _o.props.displayName.value = obj.text.text;
            _o = snaker.extend(true,_o,data);
            _dotList.restore(obj.dots)
        };
        this.remove=function(){
            _dotList.remove();
            _path.remove();
            _arrow.remove();
            _text.remove();
            try{
                snaker(_r).unbind("click",clickHandler)
            }catch(o){}
            try{
                snaker(_r).unbind("removerect",removerectHandler)
            }catch(o){}
            try{
                snaker(_r).unbind("rectresize",rectresizeHandler)
            }catch(o){}
            try{
                snaker(_r).unbind("textchange",textchangeHandler)
            }catch(o){}
        };
        function refreshpath(){
            var r = _dotList.toPathString(), mid = _dotList.midDot().pos();
            _path.attr({path:r[0]});
            _arrow.attr({path:r[1]});
            _text.attr({x:mid.x+_textPos .x,y:mid.y+_textPos .y})
        }
        this.getId=function(){return _id};
        this.text=function(){
            return _text.attr("text")
        };
        this.name=function() {
            return _o.props["name"].value;
        }
        this.attr=function(o){
            if(o&&o.path){
                _path.attr(o.path)
            }
            if(o&&o.arrow){
                _arrow.attr(o.arrow)
            }
        }
    };
	designer.props=function(o, r){
		var _this = this, _pdiv = snaker("#properties").hide().draggable({handle:"#properties_handle"}).resizable().css(designer.config.props.attr).bind("click",function(){return false}),
            _tb = _pdiv.find("table"), _r = r, _src;
		var showpropsHandler = function(e, props, src){
			if(_src&&_src.getId()==src.getId()){
				return
			}
			_src = src;
			snaker(_tb).find(".editor").each(function(){
				var k=snaker(this).data("editor");
				if(k){k.destroy()}
			});
			_tb.empty();
			_pdiv.show();
			for(var l in props){
				if(!props[l].name) continue;
				if((props[l].name=="name"||props[l].name=="displayName")&&props[l].value==""){
					props[l].value=src.getId()
				}
				props[l].value=props[l].value.replace(/#1/g,"'");
				props[l].value=props[l].value.replace(/#2/g,"\"");
				props[l].value=props[l].value.replace(/#3/g,"\r\n");
				props[l].value=props[l].value.replace(/#4/g,"\n");
				props[l].value=props[l].value.replace(/#5/g,">");
				props[l].value=props[l].value.replace(/#6/g,"<");
                props[l].value=props[l].value.replace(/#7/g,"&");
				if(!props[l].label){
					continue;
				}
				_tb.append("<tr><td class='properties_name'>"+props[l].label+"</td><td class='properties_value'><div id='p"+l+"' class='editor'></div></td></tr>");
				if(props[l].editor){
					props[l].editor().init(props,l,"p"+l,src,_r)
				}
			}
		};
		snaker(_r).bind("showprops",showpropsHandler)
	};
	designer.editors={
		textEditor:function(){
			var _props, _k, _div, _src, _r;
			this.init=function(props, _k, _div, _src, _r){
				_props = props; _k = _k; _div = _div;_src=_src;_r=_r;
				snaker('<input style="width:98%;"/>').val(_src.text()).change(function(){
					props[_k].value=snaker(this).val();
					snaker(_r).trigger("textchange",[snaker(this).val(),_src])
				}).appendTo("#"+_div);
				snaker("#"+_div).data("editor",this)
			};
			this.destroy=function(){
				snaker("#"+_div+" input").each(function(){
					_props[_k].value=snaker(this).val();
					snaker(_r).trigger("textchange",[snaker(this).val(),_src])
				})
			}
		}
	};
	designer.init=function(c, o){
		//_r = Raphael(c,_w*1.5,_h*1.5)
		var _w = snaker(window).width(), _h = snaker(window).height(), _r = Raphael(c,1100,500), _states = {}, _paths = {};
		debugger;
		snaker.extend(true,designer.config,o);
		snaker(document).keydown(function(i){
			if(!designer.config.editable){return}
			if(i.keyCode==46){
				var j=snaker(_r).data("currNode");
				if(j){
					if(j.getId().substring(0,4)=="rect"){
						snaker(_r).trigger("removerect",j)
					}else{
						if(j.getId().substring(0,4)=="path"){
							snaker(_r).trigger("removepath",j)
						}
					}
					snaker(_r).removeData("currNode")
				}
			}
		});
		snaker(document).click(function(){
			snaker(_r).data("currNode",null);
			snaker(_r).trigger("click",{
				getId:function(){return ""}
			});
			snaker(_r).trigger("showprops",[designer.config.props.props,{getId:function(){return ""}}])
		});
		var removeHandler = function(e, src){
			if(!designer.config.editable){return}
			if(src.getId().substring(0,4)=="rect"){
				_states[src.getId()]=null;
				src.remove()
			}else{
				if(src.getId().substring(0,4)=="path"){
					_paths[src.getId()]=null;
					src.remove()
				}
			}
		};
		snaker(_r).bind("removepath",removeHandler);
		snaker(_r).bind("removerect",removeHandler);
		snaker(_r).bind("addrect",function(e, type, o){
			var rect = new designer.rect(snaker.extend(true,{},designer.config.tools.states[type],o),_r);
			_states[rect.getId()]=rect
		});
		var addpathHandler = function(e, from, to){
			var path = new designer.path({},_r,from,to);
			_paths[path.getId()]=path
		};
		snaker(_r).bind("addpath",addpathHandler);
		snaker(_r).data("mod","point");
		if(designer.config.editable){
			snaker("#toolbox").draggable({handle:"#toolbox_handle"}).css(designer.config.tools.attr);
			snaker("#toolbox .node").hover(function(){snaker(this).addClass("mover")},function(){snaker(this).removeClass("mover")});
			snaker("#toolbox .selectable").click(function(){
				snaker(".selected").removeClass("selected");
				snaker(this).addClass("selected");
				snaker(_r).data("mod",this.id)
			});
			snaker("#toolbox .state").each(function(){
				snaker(this).draggable({helper:"clone"})
			});
			snaker(c).droppable({
				accept:".state",
				drop:function(c,i){
					snaker(_r).trigger("addrect",[i.helper.attr("type"),{attr:{x:i.helper.offset().left,y:i.helper.offset().top}}])
				}
			});
			snaker("#save").click(function(){
				var i="<process ";
				for(var c in designer.config.props.props){
					if((c=="name"||c=="displayName")&&designer.config.props.props[c].value==""){
						alert("流程定义名称、显示名称不能为空");
						return;
					}
					if(designer.config.props.props[c].value!=""){
						i+=c+"=\""+designer.config.props.props[c].value+"\" "
					}
				}
				i+=">\n";
                var tarray = new Array();
				for(var node in _states){
					if(_states[node]){
						i+=_states[node].toBeforeXml();
						for(var transition in _paths){
							if(_paths[transition]){
								var from=_paths[transition].from().getId();
								if(from==_states[node].getId()){
									var transitionXml=_paths[transition].toXml();
									if(transitionXml==""){
										alert("连接线名称不能为空");
										return
									}else{
                                        tarray.push(_paths[transition].name());
										i+="\n";
										i+=transitionXml;
									}
								}
							}
						}
						i+="\n";
						i+=_states[node].toAfterXml();
						i+="\n";
					}
				}
				i+="</process>";
                var nary=tarray.sort();
                for(var idx=0;idx<tarray.length;idx++){
                    if (nary[idx]==nary[idx+1]){
                        alert("连接线名称不能重复[" + nary[idx] + "]");
                        return;
                    }
                }
				designer.config.tools.save.onclick(i)
			});
			new designer.props({},_r)
		}
		if(o.restore){
			var data = o.restore;
			var rmap = {};
			if(data.states){
				for(var s in data.states){
					var rect = new designer.rect(snaker.extend(true,{},designer.config.tools.states[data.states[s].type],data.states[s]),_r);
					rect.restore(data.states[s]);
					rmap[s]=rect;
					_states[rect.getId()]=rect
				}
			}
			if(data.paths){
				for(var s in data.paths){
					var p = new designer.path(snaker.extend(true,{},designer.config.tools.path,data.paths[s]),_r,rmap[data.paths[s].from],rmap[data.paths[s].to]);
					p.restore(data.paths[s]);
					_paths[p.getId()]=p
				}
			}
			if(data.props&&data.props.props){
				for(var s in designer.config.props.props){
					var tmp=designer.config.props.props[s];
					for(var ss in data.props.props){
						if(tmp.name==data.props.props[ss].name){
							tmp.value=data.props.props[ss].value;
							break;
						}
					}
				}
			}
		}
		var hr = designer.config.historyRects, ar = designer.config.activeRects;
		if(hr.rects.length||ar.rects.length){
			var pmap = {}, rmap = {};
			for(var h in _paths){
				if(!rmap[_paths[h].from().getName()]){
					rmap[_paths[h].from().getName()]={rect:_paths[h].from(),paths:{}}
				}

				if(!rmap[_paths[h].to().getName()]){
					rmap[_paths[h].to().getName()]={rect:_paths[h].to(),paths:{}}
				}
                rmap[_paths[h].from().getName()].paths[_paths[h].name()]=_paths[h];
                //alert(_paths[h].from().getName() + "======>" + _paths[h].name());
			}
			for(var u=0;u<hr.rects.length;u++){
				if(rmap[hr.rects[u].name]){
					rmap[hr.rects[u].name].rect.attr(hr.rectAttr)
				}
				for(var t=0;t<hr.rects[u].paths.length;t++){
					if(rmap[hr.rects[u].name].paths[hr.rects[u].paths[t]]){
						rmap[hr.rects[u].name].paths[hr.rects[u].paths[t]].attr(hr.pathAttr)
					}
				}
			}
			for(var u=0;u<ar.rects.length;u++){
				if(rmap[ar.rects[u].name]){
					rmap[ar.rects[u].name].rect.attr(ar.rectAttr)
				}
				for(var t=0;t<ar.rects[u].paths.length;t++){
					if(rmap[ar.rects[u].name].paths[ar.rects[u].paths[t]]){
						rmap[ar.rects[u].name].paths[ar.rects[u].paths[t]].attr(ar.pathAttr)
					}
				}
			}
		}
	};
	snaker.fn.snakerflow=function(c){
		return this.each(function(){
			designer.init(this,c)
		})
	};
	snaker.snakerflow=designer})(jQuery);