//定义一个区域图类: function GooFlow(bgDiv, property) { if (navigator.userAgent.indexOf("MSIE 8.0") > 0 || navigator.userAgent.indexOf("MSIE 7.0") > 0 || navigator.userAgent.indexOf("MSIE 6.0") > 0) GooFlow.prototype.useSVG = ""; else GooFlow.prototype.useSVG = "1"; //初始化区域图的对象 this.$id = bgDiv.attr("id"); this.$bgDiv = bgDiv;//最父框架的DIV this.$bgDiv.addClass("GooFlow"); var width = (property.width || 800) - 2; var height = (property.height || 500) - 2; this.$bgDiv.css({ width: width + "px", height: height + "px" }); this.$tool = null;//左侧工具栏对象 this.$head = null;//顶部标签及工具栏按钮 this.$title = "newFlow_1";//流程图的名称 this.$nodeRemark = {};//每一种结点或按钮的说明文字,JSON格式,key为类名,value为用户自定义文字说明 this.$nowType = "cursor";//当前要绘制的对象类型 this.$lineData = {}; this.$lineCount = 0; this.$nodeData = {}; this.$nodeCount = 0; this.$areaData = {}; this.$areaCount = 0; this.$lineDom = {}; this.$nodeDom = {}; this.$areaDom = {}; this.$max = property.initNum || 1;//计算默认ID值的起始SEQUENCE this.$focus = "";//当前被选定的结点/转换线ID,如果没选中或者工作区被清空,则为"" this.$cursor = "default";//鼠标指针在工作区内的样式 this.$editable = false;//工作区是否可编辑 this.$deletedItem = {};//在流程图的编辑操作中被删除掉的元素ID集合,元素ID为KEY,元素类型(node,line.area)为VALUE var headHeight = 0; var tmp = ""; if (property.haveHead) { tmp = "
"; for (var x = 0; x < property.headBtns.length; ++x) { tmp += "" } tmp += "
"; this.$head = $(tmp); this.$bgDiv.append(this.$head); headHeight = 24; //以下是当工具栏按钮被点击时触发的事件自定义(虚函数),格式为function(),因为可直接用THIS操作对象本身,不用传参;用户可自行重定义: this.onBtnNewClick = null;//新建流程图按钮被点中 this.onBtnOpenClick = null;//打开流程图按钮定义 this.onBtnSaveClick = null;//保存流程图按钮定义 this.onFreshClick = null;//重载流程图按钮定义 if (property.headBtns) this.$head.on("click", { inthis: this }, function (e) { if (!e) e = window.event; var tar = e.target; if (tar.tagName == "DIV" || tar.tagName == "SPAN") return; else if (tar.tagName == "a") tar = tar.childNode[0]; var This = e.data.inthis; //定义顶部操作栏按钮的事件 switch ($(tar).attr("class")) { case "ico_new": if (This.onBtnNewClick != null) This.onBtnNewClick(); break; case "ico_open": if (This.onBtnOpenClick != null) This.onBtnOpenClick(); break; case "ico_save": if (This.onBtnSaveClick != null) This.onBtnSaveClick(); break; case "ico_undo": This.undo(); break; case "ico_redo": This.redo(); break; case "ico_reload": if (This.onFreshClick != null) This.onFreshClick(); break; } }); } var toolWidth = 0; if (property.haveTool) { this.$bgDiv.append("
"); this.$tool = this.$bgDiv.find(".GooFlow_tool div"); //未加代码:加入绘图工具按钮 this.$tool.append(""); if (property.toolBtns && property.toolBtns.length > 0) { tmp = ""; for (var i = 0; i < property.toolBtns.length; ++i) { tmp += "";//加入自定义按钮 } this.$tool.append(tmp); } //加入区域划分框工具开关按钮 if (property.haveGroup) this.$tool.append(""); toolWidth = 31; this.$nowType = "cursor"; //绑定各个按钮的点击事件 this.$tool.on("click", { inthis: this }, function (e) { if (!e) e = window.event; var tar; switch (e.target.tagName) { case "SPAN": return false; case "DIV": return false; case "B": tar = e.target.parentNode; break; case "A": tar = e.target; }; var type = $(tar).attr("type"); e.data.inthis.switchToolBtn(type); return false; }); this.$editable = true;//只有具有工具栏时可编辑 } width = width - toolWidth - 8; height = height; this.$bgDiv.append("
"); this.$workArea = $("
") .attr({ "unselectable": "on", "onselectstart": 'return false', "onselect": 'document.selection.empty()' }); this.$bgDiv.children(".GooFlow_work").append(this.$workArea); this.$draw = null;//画矢量线条的容器 this.initDraw("draw_" + this.$id, width, height); this.$group = null; if (property.haveGroup) this.initGroup(width, height); if (this.$editable) { this.$workArea.on("click", { inthis: this }, function (e) { if (!e) e = window.event; if (!e.data.inthis.$editable) return; var type = e.data.inthis.$nowType; if (type == "cursor") { var t = $(e.target); var n = t.prop("tagName"); if (n == "svg" || (n == "DIV" && t.prop("class").indexOf("GooFlow_work") > -1) || n == "LABEL") e.data.inthis.blurItem(); return; } else if (type == "direct" || type == "group") return; var X, Y; var ev = mousePosition(e), t = getElCoordinate(this); X = ev.x - t.left + this.parentNode.scrollLeft - 1; Y = ev.y - t.top + this.parentNode.scrollTop - 1; var name = "新建节点" + e.data.inthis.$max; var type = e.data.inthis.$nowType; if (type == 'startround') { name = "开始"; } if (type == 'endround') { name = "结束"; } var executeadd = true; var _nodeData = e.data.inthis.$nodeData; $.each(_nodeData, function (i) { if (_nodeData[i].name == name) { alert(name + '节点不能重复'); executeadd = false; return false; } }) if (executeadd) { e.data.inthis.addNode(e.data.inthis.$id + "_node_" + e.data.inthis.$max, { name: name, left: X, top: Y, type: e.data.inthis.$nowType, css: '', img: '', }); e.data.inthis.$max++; } }); //划线时用的绑定 this.$workArea.mousemove({ inthis: this }, function (e) { if (e.data.inthis.$nowType != "direct") return; var lineStart = $(this).data("lineStart"); if (!lineStart) return; var ev = mousePosition(e), t = getElCoordinate(this); var X, Y; X = ev.x - t.left + this.parentNode.scrollLeft; Y = ev.y - t.top + this.parentNode.scrollTop; var line = document.getElementById("GooFlow_tmp_line"); if (GooFlow.prototype.useSVG != "") { line.childNodes[0].setAttribute("d", "M " + lineStart.x + " " + lineStart.y + " L " + X + " " + Y); line.childNodes[1].setAttribute("d", "M " + lineStart.x + " " + lineStart.y + " L " + X + " " + Y); if (line.childNodes[1].getAttribute("marker-end") == "url(\"#arrow2\")") line.childNodes[1].setAttribute("marker-end", "url(#arrow3)"); else line.childNodes[1].setAttribute("marker-end", "url(#arrow2)"); } else line.points.value = lineStart.x + "," + lineStart.y + " " + X + "," + Y; }); this.$workArea.mouseup({ inthis: this }, function (e) { if (e.data.inthis.$nowType != "direct") return; $(this).css("cursor", "auto").removeData("lineStart"); var tmp = document.getElementById("GooFlow_tmp_line"); if (tmp) e.data.inthis.$draw.removeChild(tmp); }); //为了结点而增加的一些集体delegate绑定 this.initWorkForNode(); //对结点进行移动或者RESIZE时用来显示的遮罩层 this.$ghost = $("
").attr({ "unselectable": "on", "onselectstart": 'return false', "onselect": 'document.selection.empty()' }); this.$bgDiv.append(this.$ghost); this.$textArea = $(""); this.$bgDiv.append(this.$textArea); this.$lineMove = $("");//操作折线时的移动框 this.$workArea.append(this.$lineMove); this.$lineMove.on("mousedown", { inthis: this }, function (e) { if (e.button == 2) return false; var lm = $(this); lm.css({ "background-color": "#333" }); var This = e.data.inthis; var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]); var X, Y; X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft; Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop; var p = This.$lineMove.position(); var vX = X - p.left, vY = Y - p.top; var isMove = false; document.onmousemove = function (e) { if (!e) e = window.event; var ev = mousePosition(e); var ps = This.$lineMove.position(); X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft; Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop; if (This.$lineMove.data("type") == "lr") { X = X - vX; if (X < 0) X = 0; else if (X > This.$workArea.width()) X = This.$workArea.width(); This.$lineMove.css({ left: X + "px" }); } else if (This.$lineMove.data("type") == "tb") { Y = Y - vY; if (Y < 0) Y = 0; else if (Y > This.$workArea.height()) Y = This.$workArea.height(); This.$lineMove.css({ top: Y + "px" }); } isMove = true; } document.onmouseup = function (e) { if (isMove) { var p = This.$lineMove.position(); if (This.$lineMove.data("type") == "lr") This.setLineM(This.$lineMove.data("tid"), p.left + 3); else if (This.$lineMove.data("type") == "tb") This.setLineM(This.$lineMove.data("tid"), p.top + 3); } This.$lineMove.css({ "background-color": "transparent" }); if (This.$focus == This.$lineMove.data("tid")) { This.focusItem(This.$lineMove.data("tid")); } document.onmousemove = null; document.onmouseup = null; } }); this.$lineOper = $("");//选定线时显示的操作框 this.$workArea.append(this.$lineOper); this.$lineOper.on("click", { inthis: this }, function (e) { if (!e) e = window.event; if (e.target.tagName != "A" && e.target.tagName != "B") return; var This = e.data.inthis; var id = $(this).data("tid"); switch ($(e.target).attr("class")) { case "b_x": This.delLine(id); this.style.display = "none"; break; case "b_l1": This.setLineType(id, "lr"); break; case "b_l2": This.setLineType(id, "tb"); break; case "b_l3": This.setLineType(id, "sl"); break; break; } }); //下面绑定当结点/线/分组块的一些操作事件,这些事件可直接通过this访问对象本身 //当操作某个单元(结点/线/分组块)被添加时,触发的方法,返回FALSE可阻止添加事件的发生 //格式function(id,type,json):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值,json即addNode,addLine或addArea方法的第二个传参json. this.onItemAdd = null; //当操作某个单元(结点/线/分组块)被删除时,触发的方法,返回FALSE可阻止删除事件的发生 //格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值 this.onItemDel = null; //当操作某个单元(结点/分组块)被移动时,触发的方法,返回FALSE可阻止移动事件的发生 //格式function(id,type,left,top):id是单元的唯一标识ID,type是单元的种类,有"node","area"两种取值,线line不支持移动,left是新的左边距坐标,top是新的顶边距坐标 this.onItemMove = null; //当操作某个单元(结点/线/分组块)被重命名时,触发的方法,返回FALSE可阻止重命名事件的发生 //格式function(id,name,type):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值,name是新的名称 this.onItemRename = null; //当操作某个单元(结点/线)被由不选中变成选中时,触发的方法,返回FALSE可阻止选中事件的发生 //格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line"两种取值,"area"不支持被选中 this.onItemFocus = null; //当操作某个单元(结点/线)被由选中变成不选中时,触发的方法,返回FALSE可阻止取消选中事件的发生 //格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line"两种取值,"area"不支持被取消选中 this.onItemBlur = null; //当操作某个单元(结点/分组块)被重定义大小或造型时,触发的方法,返回FALSE可阻止重定大小/造型事件的发生 //格式function(id,type,width,height):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值;width是新的宽度,height是新的高度 this.onItemResize = null; //当移动某条折线中段的位置,触发的方法,返回FALSE可阻止重定大小/造型事件的发生 //格式function(id,M):id是单元的唯一标识ID,M是中段的新X(或Y)的坐标 this.onLineMove = null; //当变换某条连接线的类型,触发的方法,返回FALSE可阻止重定大小/造型事件的发生 //格式function(id,type):id是单元的唯一标识ID,type是连接线的新类型,"sl":直线,"lr":中段可左右移动的折线,"tb":中段可上下移动的折线 this.onLineSetType = null; //当用重色标注某个结点/转换线时触发的方法,返回FALSE可阻止重定大小/造型事件的发生 //格式function(id,type,mark):id是单元的唯一标识ID,type是单元类型("node"结点,"line"转换线),mark为布尔值,表示是要标注TRUE还是取消标注FALSE this.onItemMark = null; if (property.useOperStack && this.$editable) {//如果要使用堆栈记录操作并提供“撤销/重做”的功能,只在编辑状态下有效 this.$undoStack = []; this.$redoStack = []; this.$isUndo = 0; ///////////////以下是构造撤销操作/重做操作的方法 //为了节省浏览器内存空间,undo/redo中的操作缓存栈,最多只可放40步操作;超过40步时,将自动删掉最旧的一个缓存 this.pushOper = function (funcName, paras) { var len = this.$undoStack.length; if (this.$isUndo == 1) { this.$redoStack.push([funcName, paras]); this.$isUndo = false; if (this.$redoStack.length > 40) this.$redoStack.shift(); } else { this.$undoStack.push([funcName, paras]); if (this.$undoStack.length > 40) this.$undoStack.shift(); if (this.$isUndo == 0) { this.$redoStack.splice(0, this.$redoStack.length); } this.$isUndo = 0; } }; //将外部的方法加入到GooFlow对象的事务操作堆栈中,在过后的undo/redo操作中可以进行控制,一般用于对流程图以外的附加信息进行编辑的事务撤销/重做控制; //传参func为要执行方法对象,jsonPara为外部方法仅有的一个面向字面的JSON传参,由JSON对象带入所有要传的信息; //提示:为了让外部方法能够被UNDO/REDO,需要在编写这些外部方法实现时,加入对该方法执行后效果回退的另一个执行方法的pushExternalOper this.pushExternalOper = function (func, jsonPara) { this.pushOper("externalFunc", [func, jsonPara]); }; //撤销上一步操作 this.undo = function () { if (this.$undoStack.length == 0) return; var tmp = this.$undoStack.pop(); this.$isUndo = 1; if (tmp[0] == "externalFunc") { tmp[1][0](tmp[1][1]); } else { //传参的数量,最多支持6个. switch (tmp[1].length) { case 0: this[tmp[0]](); break; case 1: this[tmp[0]](tmp[1][0]); break; case 2: this[tmp[0]](tmp[1][0], tmp[1][1]); break; case 3: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2]); break; case 4: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3]); break; case 5: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3], tmp[1][4]); break; case 6: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3], tmp[1][4], tmp[1][5]); break; } } }; //重做最近一次被撤销的操作 this.redo = function () { if (this.$redoStack.length == 0) return; var tmp = this.$redoStack.pop(); this.$isUndo = 2; if (tmp[0] == "externalFunc") { tmp[1][0](tmp[1][1]); } else { //传参的数量,最多支持6个. switch (tmp[1].length) { case 0: this[tmp[0]](); break; case 1: this[tmp[0]](tmp[1][0]); break; case 2: this[tmp[0]](tmp[1][0], tmp[1][1]); break; case 3: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2]); break; case 4: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3]); break; case 5: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3], tmp[1][4]); break; case 6: this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3], tmp[1][4], tmp[1][5]); break; } } }; } $(document).keydown({ inthis: this }, function (e) { //绑定键盘操作 var This = e.data.inthis; if (This.$focus == "") return; switch (e.keyCode) { case 46://删除 This.delNode(This.$focus, true); This.delLine(This.$focus); break; } }); } } GooFlow.prototype = { useSVG: "", getSvgMarker: function (id, color) { var m = document.createElementNS("http://www.w3.org/2000/svg", "marker"); m.setAttribute("id", id); m.setAttribute("viewBox", "0 0 6 6"); m.setAttribute("refX", 5); m.setAttribute("refY", 3); m.setAttribute("markerUnits", "strokeWidth"); m.setAttribute("markerWidth", 6); m.setAttribute("markerHeight", 6); m.setAttribute("orient", "auto"); var path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("d", "M 0 0 L 6 3 L 0 6 z"); path.setAttribute("fill", color); path.setAttribute("stroke-width", 0); m.appendChild(path); return m; }, initDraw: function (id, width, height) { var elem; if (GooFlow.prototype.useSVG != "") { this.$draw = document.createElementNS("http://www.w3.org/2000/svg", "svg");//可创建带有指定命名空间的元素节点 this.$workArea.prepend(this.$draw); var defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); this.$draw.appendChild(defs); defs.appendChild(GooFlow.prototype.getSvgMarker("arrow1", "gray")); defs.appendChild(GooFlow.prototype.getSvgMarker("arrow2", "#ff3300")); defs.appendChild(GooFlow.prototype.getSvgMarker("arrow3", "#ff3300")); } else { this.$draw = document.createElement("v:group"); this.$draw.coordsize = width * 3 + "," + height * 3; this.$workArea.prepend("
"); this.$workArea.children("div")[0].insertBefore(this.$draw, null); } this.$draw.id = id; this.$draw.style.width = width * 3 + "px"; this.$draw.style.height = +height * 3 + "px"; //绑定连线的点击选中以及双击编辑事件 var tmpClk = null; if (GooFlow.prototype.useSVG != "") tmpClk = "g"; else tmpClk = "PolyLine"; if (this.$editable) { $(this.$draw).delegate(tmpClk, "click", { inthis: this }, function (e) { e.data.inthis.focusItem(this.id, true); }); $(this.$draw).delegate(tmpClk, "dblclick", { inthis: this }, function (e) { var This = e.data.inthis; OpenLine(this.id, This); //var oldTxt, x, y, from, to; //var This = e.data.inthis; //if (GooFlow.prototype.useSVG != "") { // oldTxt = this.childNodes[2].textContent; // from = this.getAttribute("from").split(","); // to = this.getAttribute("to").split(","); //} else { // oldTxt = this.childNodes[1].innerHTML; // var n = this.getAttribute("fromTo").split(","); // from = [n[0], n[1]]; // to = [n[2], n[3]]; //} //if (This.$lineData[this.id].type == "lr") { // from[0] = This.$lineData[this.id].M; // to[0] = from[0]; //} //else if (This.$lineData[this.id].type == "tb") { // from[1] = This.$lineData[this.id].M; // to[1] = from[1]; //} //x = (parseInt(from[0], 10) + parseInt(to[0], 10)) / 2 - 60; //y = (parseInt(from[1], 10) + parseInt(to[1], 10)) / 2 - 12; //var t = getElCoordinate(This.$workArea[0]); //This.$textArea.val(oldTxt).css({ // display: "block", width: 120, height: 14, // left: t.left + x - This.$workArea[0].parentNode.scrollLeft, // top: t.top + y - This.$workArea[0].parentNode.scrollTop //}).data("id", This.$focus).focus(); //This.$workArea.parent().one("mousedown", function (e) { // if (e.button == 2) return false; // This.setName(This.$textArea.data("id"), This.$textArea.val(), "line"); // This.$textArea.val("").removeData("id").hide(); //}); }); } }, initGroup: function (width, height) { this.$group = $("
");//存放背景区域的容器 this.$workArea.prepend(this.$group); if (!this.$editable) return; //区域划分框操作区的事件绑定 this.$group.on("mousedown", { inthis: this }, function (e) {//绑定RESIZE功能以及移动功能 if (e.button == 2) return false; var This = e.data.inthis; if (This.$nowType != "group") return; if (This.$textArea.css("display") == "block") { This.setName(This.$textArea.data("id"), This.$textArea.val(), "area"); This.$textArea.val("").removeData("id").hide(); return false; }; if (!e) e = window.event; var cursor = $(e.target).css("cursor"); var id = e.target.parentNode; switch (cursor) { case "nw-resize": id = id.parentNode; break; case "w-resize": id = id.parentNode; break; case "n-resize": id = id.parentNode; break; case "move": break; default: return; } id = id.id; var hack = 1; if (navigator.userAgent.indexOf("8.0") != -1) hack = 0; var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]); var X, Y; X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft; Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop; if (cursor != "move") { This.$ghost.css({ display: "block", width: This.$areaData[id].width - 2 + "px", height: This.$areaData[id].height - 2 + "px", top: This.$areaData[id].top + t.top - This.$workArea[0].parentNode.scrollTop + hack + "px", left: This.$areaData[id].left + t.left - This.$workArea[0].parentNode.scrollLeft + hack + "px", cursor: cursor }); var vX = (This.$areaData[id].left + This.$areaData[id].width) - X; var vY = (This.$areaData[id].top + This.$areaData[id].height) - Y; } else { var vX = X - This.$areaData[id].left; var vY = Y - This.$areaData[id].top; } var isMove = false; This.$ghost.css("cursor", cursor); document.onmousemove = function (e) { if (!e) e = window.event; var ev = mousePosition(e); if (cursor != "move") { X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft - This.$areaData[id].left + vX; Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop - This.$areaData[id].top + vY; if (X < 200) X = 200; if (Y < 100) Y = 100; switch (cursor) { case "nw-resize": This.$ghost.css({ width: X - 2 + "px", height: Y - 2 + "px" }); break; case "w-resize": This.$ghost.css({ width: X - 2 + "px" }); break; case "n-resize": This.$ghost.css({ height: Y - 2 + "px" }); break; } } else { if (This.$ghost.css("display") == "none") { This.$ghost.css({ display: "block", width: This.$areaData[id].width - 2 + "px", height: This.$areaData[id].height - 2 + "px", top: This.$areaData[id].top + t.top - This.$workArea[0].parentNode.scrollTop + hack + "px", left: This.$areaData[id].left + t.left - This.$workArea[0].parentNode.scrollLeft + hack + "px", cursor: cursor }); } X = ev.x - vX; Y = ev.y - vY; if (X < t.left - This.$workArea[0].parentNode.scrollLeft) X = t.left - This.$workArea[0].parentNode.scrollLeft; else if (X + This.$workArea[0].parentNode.scrollLeft + This.$areaData[id].width > t.left + This.$workArea.width()) X = t.left + This.$workArea.width() - This.$workArea[0].parentNode.scrollLeft - This.$areaData[id].width; if (Y < t.top - This.$workArea[0].parentNode.scrollTop) Y = t.top - This.$workArea[0].parentNode.scrollTop; else if (Y + This.$workArea[0].parentNode.scrollTop + This.$areaData[id].height > t.top + This.$workArea.height()) Y = t.top + This.$workArea.height() - This.$workArea[0].parentNode.scrollTop - This.$areaData[id].height; This.$ghost.css({ left: X + hack + "px", top: Y + hack + "px" }); } isMove = true; } document.onmouseup = function (e) { This.$ghost.empty().hide(); document.onmousemove = null; document.onmouseup = null; if (!isMove) return; if (cursor != "move") This.resizeArea(id, This.$ghost.outerWidth(), This.$ghost.outerHeight()); else This.moveArea(id, X + This.$workArea[0].parentNode.scrollLeft - t.left, Y + This.$workArea[0].parentNode.scrollTop - t.top); return false; } }); //绑定修改文字说明功能 this.$group.on("dblclick", { inthis: this }, function (e) { var This = e.data.inthis; if (This.$nowType != "group") return; if (!e) e = window.event; if (e.target.tagName != "LABEL") return false; var oldTxt = e.target.innerHTML; var p = e.target.parentNode; var x = parseInt(p.style.left, 10) + 18, y = parseInt(p.style.top, 10) + 1; var t = getElCoordinate(This.$workArea[0]); This.$textArea.val(oldTxt).css({ display: "block", width: 100, height: 14, left: t.left + x - This.$workArea[0].parentNode.scrollLeft, top: t.top + y - This.$workArea[0].parentNode.scrollTop }).data("id", p.id).focus(); This.$workArea.parent().one("mousedown", function (e) { if (e.button == 2) return false; if (This.$textArea.css("display") == "block") { This.setName(This.$textArea.data("id"), This.$textArea.val(), "area"); This.$textArea.val("").removeData("id").hide(); } }); return false; }); //绑定点击事件 this.$group.mouseup({ inthis: this }, function (e) { var This = e.data.inthis; if (This.$nowType != "group") return; if (!e) e = window.event; switch ($(e.target).attr("class")) { case "rs_close": This.delArea(e.target.parentNode.parentNode.id); return false;//删除该分组区域 case "bg": return; } switch (e.target.tagName) { case "LABEL": return false; case "B"://绑定变色功能 var id = e.target.parentNode.id; switch (This.$areaData[id].color) { case "red": This.setAreaColor(id, "yellow"); break; case "yellow": This.setAreaColor(id, "blue"); break; case "blue": This.setAreaColor(id, "green"); break; case "green": This.setAreaColor(id, "red"); break; } return false; } if (e.data.inthis.$ghost.css("display") == "none") { var X, Y; var ev = mousePosition(e), t = getElCoordinate(this); X = ev.x - t.left + this.parentNode.parentNode.scrollLeft - 1; Y = ev.y - t.top + this.parentNode.parentNode.scrollTop - 1; var color = ["red", "yellow", "blue", "green"]; e.data.inthis.addArea(e.data.inthis.$id + "_area_" + e.data.inthis.$max, { name: "area_" + e.data.inthis.$max, left: X, top: Y, color: color[e.data.inthis.$max % 4], width: 200, height: 100 }); e.data.inthis.$max++; return false; } }); }, //每一种类型结点及其按钮的说明文字 setNodeRemarks: function (remark) { if (this.$tool != null) { this.$tool.children("a").each(function () { this.title = remark[$(this).attr("id").split("btn_")[1]]; }); this.$nodeRemark = remark; } }, //切换左边工具栏按钮,传参TYPE表示切换成哪种类型的按钮 switchToolBtn: function (type) { this.$tool.children("#" + this.$id + "_btn_" + this.$nowType.split(" ")[0]).attr("class", "GooFlow_tool_btn"); if (this.$nowType == "group") { this.$workArea.prepend(this.$group); for (var key in this.$areaDom) this.$areaDom[key].addClass("lock").children("div:eq(1)").css("display", "none"); } this.$nowType = type; this.$tool.children("#" + this.$id + "_btn_" + type.split(" ")[0]).attr("class", "GooFlow_tool_btndown"); if (this.$nowType == "group") { this.blurItem(); this.$workArea.append(this.$group); for (var key in this.$areaDom) this.$areaDom[key].removeClass("lock").children("div:eq(1)").css("display", ""); } if (this.$textArea.css("display") == "none") this.$textArea.removeData("id").val("").hide(); }, //增加一个流程结点,传参为一个JSON,有id,name,top,left,width,height,type(结点类型)等属性 addNode: function (id, json) { if (this.onItemAdd != null && !this.onItemAdd(id, "node", json)) return; if (this.$undoStack && this.$editable) { this.pushOper("delNode", [id]); } var mark = json.type; if (json.type != "startround" && json.type != "endround") { if (!json.width || json.width < 86) json.width = 150; if (!json.height || json.height < 24) json.height = 65; if (!json.top || json.top < 0) json.top = 0; if (!json.left || json.left < 0) json.left = 0; var hack = 0; if (navigator.userAgent.indexOf("8.0") != -1) hack = 2; this.$nodeDom[id] = $("
" + json.name + "
"); if (json.type.indexOf(" mix") > -1) this.$nodeDom[id].addClass(mark); //json.css = mark; //json.img = mark; } else { json.width = 24; json.height = 24; var name = json.name; if (json.type == 'startround') { name = "开始"; } if (json.type == 'endround') { name = "结束"; } this.$nodeDom[id] = $("
" + name + "
"); } var ua = navigator.userAgent.toLowerCase(); if (ua.indexOf('msie') != -1 && ua.indexOf('8.0') != -1) this.$nodeDom[id].css("filter", "progid:DXImageTransform.Microsoft.Shadow(color=#94AAC2,direction=135,strength=2)"); this.$workArea.append(this.$nodeDom[id]); this.$nodeData[id] = json; ++this.$nodeCount; if (this.$editable) { this.$nodeData[id].alt = true; if (this.$deletedItem[id]) delete this.$deletedItem[id];//在回退删除操作时,去掉该元素的删除记录 } }, initWorkForNode: function () { //绑定点击事件 this.$workArea.delegate(".GooFlow_item", "click", { inthis: this }, function (e) { e.data.inthis.focusItem(this.id, true); $(this).removeClass("item_mark"); //if (!$(this).hasClass("item_startround")) { // LoadrightMenu("#" + this.id); //} //if (!$(this).hasClass("item_endround")) { // LoadrightMenu("#" + this.id); //} }); //绑定右击事件 this.$workArea.delegate(".GooFlow_item", "contextmenu", { inthis: this }, function (e) { e.data.inthis.focusItem(this.id, true); $(this).removeClass("item_mark"); return false; }); //绑定用鼠标移动事件 this.$workArea.delegate(".ico", "mousedown", { inthis: this }, function (e) { if (!e) e = window.event; if (e.button == 2) return false; var This = e.data.inthis; if (This.$nowType == "direct") return; var Dom = $(this).parents(".GooFlow_item"); var id = Dom.attr("id"); This.focusItem(id, true); var hack = 1; if (navigator.userAgent.indexOf("8.0") != -1) hack = 0; var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]); Dom.children("table").clone().prependTo(This.$ghost); var X, Y; X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft; Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop; var vX = X - This.$nodeData[id].left, vY = Y - This.$nodeData[id].top; var isMove = false; document.onmousemove = function (e) { if (!e) e = window.event; var ev = mousePosition(e); if (X == ev.x - vX && Y == ev.y - vY) return false; X = ev.x - vX; Y = ev.y - vY; if (isMove && This.$ghost.css("display") == "none") { This.$ghost.css({ display: "block", width: $('#' + id).width() - 2 + "px", height: $('#' + id).height() - 2 + "px", top: This.$nodeData[id].top + t.top - This.$workArea[0].parentNode.scrollTop + hack + "px", left: This.$nodeData[id].left + t.left - This.$workArea[0].parentNode.scrollLeft + hack + "px", cursor: "move" }); } if (X < t.left - This.$workArea[0].parentNode.scrollLeft) X = t.left - This.$workArea[0].parentNode.scrollLeft; else if (X + This.$workArea[0].parentNode.scrollLeft + This.$nodeData[id].width > t.left + This.$workArea.width()) X = t.left + This.$workArea.width() - This.$workArea[0].parentNode.scrollLeft - This.$nodeData[id].width; if (Y < t.top - This.$workArea[0].parentNode.scrollTop) Y = t.top - This.$workArea[0].parentNode.scrollTop; else if (Y + This.$workArea[0].parentNode.scrollTop + This.$nodeData[id].height > t.top + This.$workArea.height()) Y = t.top + This.$workArea.height() - This.$workArea[0].parentNode.scrollTop - This.$nodeData[id].height; This.$ghost.css({ left: X + hack + "px", top: Y + hack + "px" }); isMove = true; } document.onmouseup = function (e) { if (isMove) This.moveNode(id, X + This.$workArea[0].parentNode.scrollLeft - t.left, Y + This.$workArea[0].parentNode.scrollTop - t.top); This.$ghost.empty().hide(); document.onmousemove = null; document.onmouseup = null; } }); if (!this.$editable) return; //绑定鼠标覆盖/移出事件 this.$workArea.delegate(".GooFlow_item", "mouseenter", { inthis: this }, function (e) { if (e.data.inthis.$nowType != "direct") return; $(this).addClass("item_mark"); }); this.$workArea.delegate(".GooFlow_item", "mouseleave", { inthis: this }, function (e) { if (e.data.inthis.$nowType != "direct") return; $(this).removeClass("item_mark"); }); //绑定连线时确定初始点 this.$workArea.delegate(".GooFlow_item", "mousedown", { inthis: this }, function (e) { if (e.button == 2) return false; var This = e.data.inthis; if (This.$nowType != "direct") return; var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]); var X, Y; X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft; Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop; This.$workArea.data("lineStart", { "x": X, "y": Y, "id": this.id }).css("cursor", "crosshair"); var line = GooFlow.prototype.drawLine("GooFlow_tmp_line", [X, Y], [X, Y], true, true); This.$draw.appendChild(line); }); //绑定连线时确定结束点 this.$workArea.delegate(".GooFlow_item", "mouseup", { inthis: this }, function (e) { var This = e.data.inthis; if (This.$nowType != "direct") return; var lineStart = This.$workArea.data("lineStart"); if (lineStart) This.addLine(This.$id + "_line_" + This.$max, { from: lineStart.id, to: this.id, name: "" }); This.$max++; }); //绑定双击编辑事件 this.$workArea.delegate(".GooFlow_item > .span", "dblclick", { inthis: this }, function (e) { var This = e.data.inthis; var type = $('.item_focus').hasClass('item_startround'); if (type) { OpenNode(This); } //var oldTxt = this.innerHTML; //var This = e.data.inthis; //var id = this.parentNode.id; //var t = getElCoordinate(This.$workArea[0]); //This.$textArea.val(oldTxt).css({ // display: "block", height: $(this).height(), width: 100, // left: t.left + This.$nodeData[id].left - This.$workArea[0].parentNode.scrollLeft - 24, // top: t.top + This.$nodeData[id].top - This.$workArea[0].parentNode.scrollTop + 26 //}) // .data("id", This.$focus).focus(); //This.$workArea.parent().one("mousedown", function (e) { // if (e.button == 2) return false; // This.setName(This.$textArea.data("id"), This.$textArea.val(), "node"); // This.$textArea.val("").removeData("id").hide(); //}); }); //节点双击事件 this.$workArea.delegate(".ico + td", "dblclick", { inthis: this }, function (e) { var This = e.data.inthis; OpenNode(This); //var oldTxt = this.innerHTML; //var This = e.data.inthis; //var id = $(this).parents(".GooFlow_item").attr("id"); //var t = getElCoordinate(This.$workArea[0]); //This.$textArea.val(oldTxt).css({ // display: "block", width: $(this).width() + 24, height: $(this).height(), // left: t.left + 24 + This.$nodeData[id].left - This.$workArea[0].parentNode.scrollLeft, // top: t.top + 2 + This.$nodeData[id].top - This.$workArea[0].parentNode.scrollTop //}) // .data("id", This.$focus).focus(); //This.$workArea.parent().one("mousedown", function (e) { // if (e.button == 2) return false; // This.setName(This.$textArea.data("id"), This.$textArea.val(), "node"); // This.$textArea.val("").removeData("id").hide(); //}); }); //绑定结点的删除功能 this.$workArea.delegate(".rs_close", "click", { inthis: this }, function (e) { if (!e) e = window.event; e.data.inthis.delNode(e.data.inthis.$focus); return false; }); //绑定结点的RESIZE功能 this.$workArea.delegate(".GooFlow_item > div > div[class!=rs_close]", "mousedown", { inthis: this }, function (e) { if (!e) e = window.event; if (e.button == 2) return false; var cursor = $(this).css("cursor"); if (cursor == "pointer") { return; } var This = e.data.inthis; var id = This.$focus; This.switchToolBtn("cursor"); e.cancelBubble = true; e.stopPropagation(); var hack = 1; if (navigator.userAgent.indexOf("8.0") != -1) hack = 0; var ev = mousePosition(e), t = getElCoordinate(This.$workArea[0]); This.$ghost.css({ display: "block", width: This.$nodeData[id].width - 2 + "px", height: This.$nodeData[id].height - 2 + "px", top: This.$nodeData[id].top + t.top - This.$workArea[0].parentNode.scrollTop + hack + "px", left: This.$nodeData[id].left + t.left - This.$workArea[0].parentNode.scrollLeft + hack + "px", cursor: cursor }); var X, Y; X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft; Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop; var vX = (This.$nodeData[id].left + This.$nodeData[id].width) - X; var vY = (This.$nodeData[id].top + This.$nodeData[id].height) - Y; var isMove = false; This.$ghost.css("cursor", cursor); document.onmousemove = function (e) { if (!e) e = window.event; var ev = mousePosition(e); X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft - This.$nodeData[id].left + vX; Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop - This.$nodeData[id].top + vY; if (X < 86) X = 86; if (Y < 24) Y = 24; isMove = true; switch (cursor) { case "nw-resize": This.$ghost.css({ width: X - 2 + "px", height: Y - 2 + "px" }); break; case "w-resize": This.$ghost.css({ width: X - 2 + "px" }); break; case "n-resize": This.$ghost.css({ height: Y - 2 + "px" }); break; } } document.onmouseup = function (e) { This.$ghost.hide(); if (!isMove) return; if (!e) e = window.event; This.resizeNode(id, This.$ghost.outerWidth(), This.$ghost.outerHeight()); document.onmousemove = null; document.onmouseup = null; } }); }, //获取结点/连线/分组区域的详细信息 getItemInfo: function (id, type) { switch (type) { case "node": return this.$nodeData[id] || null; case "line": return this.$lineData[id] || null; case "area": return this.$areaData[id] || null; } }, //取消所有结点/连线被选定的状态 blurItem: function () { if (this.$focus != "") { var jq = $("#" + this.$focus); if (jq.prop("tagName") == "DIV") { if (this.onItemBlur != null && !this.onItemBlur(id, "node")) return false; jq.removeClass("item_focus").children("div:eq(0)").css("display", "none"); } else { if (this.onItemBlur != null && !this.onItemBlur(id, "line")) return false; if (GooFlow.prototype.useSVG != "") { if (!this.$lineData[this.$focus].marked) { jq[0].childNodes[1].setAttribute("stroke", "gray"); jq[0].childNodes[1].setAttribute("marker-end", "url(#arrow1)"); } } else { if (!this.$lineData[this.$focus].marked) jq[0].strokeColor = "gray"; } this.$lineMove.hide().removeData("type").removeData("tid"); if (this.$editable) this.$lineOper.hide().removeData("tid"); } } this.$focus = ""; return true; }, //选定某个结点/转换线 bool:TRUE决定了要触发选中事件,FALSE则不触发选中事件,多用在程序内部调用。 focusItem: function (id, bool) { var jq = $("#" + id); if (jq.length == 0) return; if (!this.blurItem()) return;//先执行"取消选中",如果返回FLASE,则也会阻止选定事件继续进行. if (jq.prop("tagName") == "DIV") { if (bool && this.onItemFocus != null && !this.onItemFocus(id, "node")) return; jq.addClass("item_focus"); if (this.$editable) jq.children("div:eq(0)").css("display", "block"); this.$workArea.append(jq); } else {//如果是连接线 if (this.onItemFocus != null && !this.onItemFocus(id, "line")) return; if (GooFlow.prototype.useSVG != "") { jq[0].childNodes[1].setAttribute("stroke", "#ff3300"); jq[0].childNodes[1].setAttribute("marker-end", "url(#arrow2)"); } else jq[0].strokeColor = "#ff3300"; if (!this.$editable) return; var x, y, from, to; if (GooFlow.prototype.useSVG != "") { from = jq.attr("from").split(","); to = jq.attr("to").split(","); } else { var n = jq[0].getAttribute("fromTo").split(","); from = [n[0], n[1]]; to = [n[2], n[3]]; } from[0] = parseInt(from[0], 10); from[1] = parseInt(from[1], 10); to[0] = parseInt(to[0], 10); to[1] = parseInt(to[1], 10); //var t=getElCoordinate(this.$workArea[0]); if (this.$lineData[id].type == "lr") { from[0] = this.$lineData[id].M; to[0] = from[0]; this.$lineMove.css({ width: "5px", height: (to[1] - from[1]) * (to[1] > from[1] ? 1 : -1) + "px", left: from[0] - 3 + "px", top: (to[1] > from[1] ? from[1] : to[1]) + 1 + "px", cursor: "e-resize", display: "block" }).data({ "type": "lr", "tid": id }); } else if (this.$lineData[id].type == "tb") { from[1] = this.$lineData[id].M; to[1] = from[1]; this.$lineMove.css({ width: (to[0] - from[0]) * (to[0] > from[0] ? 1 : -1) + "px", height: "5px", left: (to[0] > from[0] ? from[0] : to[0]) + 1 + "px", top: from[1] - 3 + "px", cursor: "s-resize", display: "block" }).data({ "type": "tb", "tid": id }); } x = (from[0] + to[0]) / 2 - 35; y = (from[1] + to[1]) / 2 + 6; this.$lineOper.css({ display: "block", left: x + "px", top: y + "px" }).data("tid", id); } this.$focus = id; this.switchToolBtn("cursor"); }, //移动结点到一个新的位置 moveNode: function (id, left, top) { if (!this.$nodeData[id]) return; if (this.onItemMove != null && !this.onItemMove(id, "node", left, top)) return; if (this.$undoStack) { var paras = [id, this.$nodeData[id].left, this.$nodeData[id].top]; this.pushOper("moveNode", paras); } if (left < 0) left = 0; if (top < 0) top = 0; $("#" + id).css({ left: left + "px", top: top + "px" }); this.$nodeData[id].left = left; this.$nodeData[id].top = top; //重画转换线 this.resetLines(id, this.$nodeData[id]); if (this.$editable) { this.$nodeData[id].alt = true; } }, //设置结点/连线/分组区域的文字信息 setName: function (id, name, type,setinfo) { var oldName; if (type == "node") {//如果是结点 this.$nodeData[id].setInfo = setinfo; if (!this.$nodeData[id]) if (this.$nodeData[id].name == name) if (this.onItemRename != null && !this.onItemRename(id, name, "node")) oldName = this.$nodeData[id].name; this.$nodeData[id].name = name; if (this.$nodeData[id].type.indexOf("round") > 1) { this.$nodeDom[id].children(".span").text(name); } else { this.$nodeDom[id].find("td:eq(1)").text(name); var hack = 0; if (navigator.userAgent.indexOf("8.0") != -1) hack = 2; var width = this.$nodeDom[id].outerWidth(); var height = this.$nodeDom[id].outerHeight(); this.$nodeDom[id].children("table").css({ width: width - 2 + "px", height: height - 2 + "px" }); this.$nodeData[id].width = width; this.$nodeData[id].height = height; } if (this.$editable) { this.$nodeData[id].alt = true; } //重画转换线 this.resetLines(id, this.$nodeData[id]); } else if (type == "line") {//如果是线 this.$lineData[id].setInfo = setinfo; if (!this.$lineData[id]) if (this.$lineData[id].name == name) if (this.onItemRename != null && !this.onItemRename(id, name, "line")) oldName = this.$lineData[id].name; this.$lineData[id].name = name; if (GooFlow.prototype.useSVG != "") { this.$lineDom[id].childNodes[2].textContent = name; } else { this.$lineDom[id].childNodes[1].innerHTML = name; var n = this.$lineDom[id].getAttribute("fromTo").split(","); var x; if (this.$lineData[id].type != "lr") { x = (n[2] - n[0]) / 2; } else { var Min = n[2] > n[0] ? n[0] : n[2]; if (Min > this.$lineData[id].M) Min = this.$lineData[id].M; x = this.$lineData[id].M - Min; } if (x < 0) x = x * -1; this.$lineDom[id].childNodes[1].style.left = x - this.$lineDom[id].childNodes[1].offsetWidth / 2 + 4 + "px"; } if (this.$editable) { this.$lineData[id].alt = true; } } else if (type == "area") {//如果是分组区域 if (!this.$areaData[id]) return; if (this.$areaData[id].name == name) return; if (this.onItemRename != null && !this.onItemRename(id, name, "area")) return; oldName = this.$areaData[id].name; this.$areaData[id].name = name; this.$areaDom[id].children("label").text(name); if (this.$editable) { this.$areaData[id].alt = true; } } if (this.$undoStack) { var paras = [id, oldName, type]; this.pushOper("setName", paras); } }, //设置结点的尺寸,仅支持非开始/结束结点 resizeNode: function (id, width, height) { if (!this.$nodeData[id]) return; if (this.onItemResize != null && !this.onItemResize(id, "node", width, height)) return; if (this.$nodeData[id].type == "start" || this.$nodeData[id].type == "end") return; if (this.$undoStack) { var paras = [id, this.$nodeData[id].width, this.$nodeData[id].height]; this.pushOper("resizeNode", paras); } var hack = 0; if (navigator.userAgent.indexOf("8.0") != -1) hack = 2; this.$nodeDom[id].children("table").css({ width: width - 2 + "px", height: height - 2 + "px" }); width = this.$nodeDom[id].outerWidth() - hack; height = this.$nodeDom[id].outerHeight() - hack; this.$nodeDom[id].children("table").css({ width: width - 2 + "px", height: height - 2 + "px" }); this.$nodeData[id].width = width; this.$nodeData[id].height = height; if (this.$editable) { this.$nodeData[id].alt = true; } //重画转换线 this.resetLines(id, this.$nodeData[id]); }, //删除结点 delNode: function (id) { if (!this.$nodeData[id]) return; if (this.onItemDel != null && !this.onItemDel(id, "node")) return; //先删除可能的连线 for (var k in this.$lineData) { if (this.$lineData[k].from == id || this.$lineData[k].to == id) { //this.$draw.removeChild(this.$lineDom[k]); //delete this.$lineData[k]; //delete this.$lineDom[k]; this.delLine(k); } } //再删除结点本身 if (this.$undoStack) { var paras = [id, this.$nodeData[id]]; this.pushOper("addNode", paras); } delete this.$nodeData[id]; this.$nodeDom[id].remove(); delete this.$nodeDom[id]; --this.$nodeCount; if (this.$focus == id) this.$focus = ""; if (this.$editable) { //在回退新增操作时,如果节点ID以this.$id+"_node_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中 if (id.indexOf(this.$id + "_node_") < 0) this.$deletedItem[id] = "node"; } }, //设置流程图的名称 setTitle: function (text) { this.$title = text; if (this.$head) this.$head.children("label").attr("title", text).text(text); }, //载入一组数据 loadData: function (data) { if (data == undefined) { data = ""; } var t = this.$editable; this.$editable = false; if (data.title) this.setTitle(data.title); if (data.initNum) this.$max = data.initNum; for (var i in data.nodes) this.addNode(data.nodes[i].id, data.nodes[i]); for (var j in data.lines) this.addLine(data.lines[j].id, data.lines[j]); for (var k in data.areas) this.addArea(data.areas[k].id, data.areas[k]); this.$editable = t; this.$deletedItem = {}; }, //用AJAX方式,远程读取一组数据 //参数para为JSON结构,与JQUERY中$.ajax()方法的传参一样 loadDataAjax: function (para) { var This = this; $.ajax({ type: para.type, url: para.url, dataType: "json", data: para.data, success: function (msg) { if (para.dataFilter) para.dataFilter(msg, "json"); This.loadData(msg); if (para.success) para.success(msg); }, error: function (XMLHttpRequest, textStatus, errorThrown) { if (para.error) para.error(textStatus, errorThrown); } }) }, //把画好的整个流程图导出到一个变量中(其实也可以直接访问GooFlow对象的$nodeData,$lineData,$areaData这三个JSON属性) exportData: function () { var ret = { title: this.$title, nodes: this.$nodeData, lines: this.$lineData, areas: this.$areaData, initNum: this.$max }; var _nodeobject = [],_lineobject = []; for (var k1 in ret.nodes) { if (!ret.nodes[k1].marked) { delete ret.nodes[k1]["marked"]; } ret.nodes[k1]["id"] = k1; _nodeobject.push(ret.nodes[k1]); } ret.nodes = _nodeobject; for (var k2 in ret.lines) { if (!ret.lines[k2].marked) { delete ret.lines[k2]["marked"]; } ret.lines[k2]["id"] = k2; _lineobject.push(ret.lines[k2]); } ret.lines = _lineobject; return ret; }, //只把本次编辑流程图中作了变更(包括增删改)的元素导出到一个变量中,以方便用户每次编辑载入的流程图后只获取变更过的数据 exportAlter: function () { var ret = { nodes: {}, lines: {}, areas: {} }; for (var k1 in this.$nodeData) { if (this.$nodeData[k1].alt) { ret.nodes[k1] = this.$nodeData[k1]; } } for (var k2 in this.$lineData) { if (this.$lineData[k2].alt) { ret.lines[k2] = this.$lineData[k2]; } } for (var k3 in this.$areaData) { if (this.$areaData[k3].alt) { ret.areas[k3] = this.$areaData[k3]; } } ret.deletedItem = this.$deletedItem; return ret; }, //变更元素的ID,一般用于快速保存后,将后台返回新元素的ID更新到页面中;type为元素类型(节点,连线,区块) transNewId: function (oldId, newId, type) { var tmp; switch (type) { case "node": if (this.$nodeData[oldId]) { tmp = this.$nodeData[oldId]; delete this.$nodeData[oldId]; this.$nodeData[newId] = tmp; } break; case "line": if (this.$lineData[oldId]) { tmp = this.$lineData[oldId]; delete this.$lineData[oldId]; this.$lineData[newId] = tmp; } break; case "area": if (this.$areaData[oldId]) { tmp = this.$areaData[oldId]; delete this.$areaData[oldId]; this.$areaData[newId] = tmp; } break; } }, //清空工作区及已载入的数据 clearData: function () { for (var key in this.$nodeData) { this.delNode(key); } for (var key in this.$lineData) { this.delLine(key); } for (var key in this.$areaData) { this.delArea(key); } this.$deletedItem = {}; }, //销毁自己 destrory: function () { this.$bgDiv.empty(); this.$lineData = null; this.$nodeData = null; this.$lineDom = null; this.$nodeDom = null; this.$areaDom = null; this.$areaData = null; this.$nodeCount = 0; this.$areaCount = 0; this.$areaCount = 0; this.$deletedItem = {}; }, ///////////以下为有关画线的方法 //绘制一条箭头线,并返回线的DOM drawLine: function (id, sp, ep, mark, dash) { var line; if (GooFlow.prototype.useSVG != "") { line = document.createElementNS("http://www.w3.org/2000/svg", "g"); var hi = document.createElementNS("http://www.w3.org/2000/svg", "path"); var path = document.createElementNS("http://www.w3.org/2000/svg", "path"); if (id != "") line.setAttribute("id", id); line.setAttribute("from", sp[0] + "," + sp[1]); line.setAttribute("to", ep[0] + "," + ep[1]); hi.setAttribute("visibility", "hidden"); hi.setAttribute("stroke-width", 9); hi.setAttribute("fill", "none"); hi.setAttribute("stroke", "white"); hi.setAttribute("d", "M " + sp[0] + " " + sp[1] + " L " + ep[0] + " " + ep[1]); hi.setAttribute("pointer-events", "stroke"); path.setAttribute("d", "M " + sp[0] + " " + sp[1] + " L " + ep[0] + " " + ep[1]); path.setAttribute("stroke-width", 2.0); path.setAttribute("stroke-linecap", "round"); path.setAttribute("fill", "none"); if (dash) path.setAttribute("style", "stroke-dasharray:6,5"); if (mark) { path.setAttribute("stroke", "#ff3300"); path.setAttribute("marker-end", "url(#arrow2)"); } else { path.setAttribute("stroke", "gray"); path.setAttribute("marker-end", "url(#arrow1)"); } line.appendChild(hi); line.appendChild(path); line.style.cursor = "crosshair"; if (id != "" && id != "GooFlow_tmp_line") { var text = document.createElementNS("http://www.w3.org/2000/svg", "text"); //text.textContent=id; line.appendChild(text); var x = (ep[0] + sp[0]) / 2; var y = (ep[1] + sp[1]) / 2; text.setAttribute("text-anchor", "middle"); text.setAttribute("x", x); text.setAttribute("y", y - 5); line.style.cursor = "pointer"; text.style.cursor = "text"; } } else { line = document.createElement("v:polyline"); if (id != "") line.id = id; //line.style.position="absolute"; line.points.value = sp[0] + "," + sp[1] + " " + ep[0] + "," + ep[1]; line.setAttribute("fromTo", sp[0] + "," + sp[1] + "," + ep[0] + "," + ep[1]); line.strokeWeight = "1.2"; line.stroke.EndArrow = "Block"; line.style.cursor = "crosshair"; if (id != "" && id != "GooFlow_tmp_line") { var text = document.createElement("div"); //text.innerHTML=id; line.appendChild(text); var x = (ep[0] - sp[0]) / 2; var y = (ep[1] - sp[1]) / 2; if (x < 0) x = x * -1; if (y < 0) y = y * -1; text.style.left = x + "px"; text.style.top = y - 6 + "px"; line.style.cursor = "pointer"; } if (dash) line.stroke.dashstyle = "Dash"; if (mark) line.strokeColor = "#ff3300"; else line.strokeColor = "gray"; } return line; }, //画一条只有两个中点的折线 drawPoly: function (id, sp, m1, m2, ep, mark) { var poly, strPath; if (GooFlow.prototype.useSVG != "") { poly = document.createElementNS("http://www.w3.org/2000/svg", "g"); var hi = document.createElementNS("http://www.w3.org/2000/svg", "path"); var path = document.createElementNS("http://www.w3.org/2000/svg", "path"); if (id != "") poly.setAttribute("id", id); poly.setAttribute("from", sp[0] + "," + sp[1]); poly.setAttribute("to", ep[0] + "," + ep[1]); hi.setAttribute("visibility", "hidden"); hi.setAttribute("stroke-width", 9); hi.setAttribute("fill", "none"); hi.setAttribute("stroke", "white"); strPath = "M " + sp[0] + " " + sp[1]; if (m1[0] != sp[0] || m1[1] != sp[1]) strPath += " L " + m1[0] + " " + m1[1]; if (m2[0] != ep[0] || m2[1] != ep[1]) strPath += " L " + m2[0] + " " + m2[1]; strPath += " L " + ep[0] + " " + ep[1]; hi.setAttribute("d", strPath); hi.setAttribute("pointer-events", "stroke"); path.setAttribute("d", strPath); path.setAttribute("stroke-width", 2.0); path.setAttribute("stroke-linecap", "round"); path.setAttribute("fill", "none"); if (mark) { path.setAttribute("stroke", "#ff3300"); path.setAttribute("marker-end", "url(#arrow2)"); } else { path.setAttribute("stroke", "gray"); path.setAttribute("marker-end", "url(#arrow1)"); } poly.appendChild(hi); poly.appendChild(path); var text = document.createElementNS("http://www.w3.org/2000/svg", "text"); //text.textContent=id; poly.appendChild(text); var x = (m2[0] + m1[0]) / 2; var y = (m2[1] + m1[1]) / 2; text.setAttribute("text-anchor", "middle"); text.setAttribute("x", x); text.setAttribute("y", y - 5); text.style.cursor = "text"; poly.style.cursor = "pointer"; } else { poly = document.createElement("v:Polyline"); if (id != "") poly.id = id; poly.filled = "false"; strPath = sp[0] + "," + sp[1]; if (m1[0] != sp[0] || m1[1] != sp[1]) strPath += " " + m1[0] + "," + m1[1]; if (m2[0] != ep[0] || m2[1] != ep[1]) strPath += " " + m2[0] + "," + m2[1]; strPath += " " + ep[0] + "," + ep[1]; poly.points.value = strPath; poly.setAttribute("fromTo", sp[0] + "," + sp[1] + "," + ep[0] + "," + ep[1]); poly.strokeWeight = "1.2"; poly.stroke.EndArrow = "Block"; var text = document.createElement("div"); //text.innerHTML=id; poly.appendChild(text); var x = (m2[0] - m1[0]) / 2; var y = (m2[1] - m1[1]) / 2; if (x < 0) x = x * -1; if (y < 0) y = y * -1; text.style.left = x + "px"; text.style.top = y - 4 + "px"; poly.style.cursor = "pointer"; if (mark) poly.strokeColor = "#ff3300"; else poly.strokeColor = "gray"; } return poly; }, //计算两个结点间要连直线的话,连线的开始坐标和结束坐标 calcStartEnd: function (n1, n2) { var X_1, Y_1, X_2, Y_2; //X判断: var x11 = n1.left, x12 = n1.left + n1.width, x21 = n2.left, x22 = n2.left + n2.width; //结点2在结点1左边 if (x11 >= x22) { X_1 = x11; X_2 = x22; } //结点2在结点1右边 else if (x12 <= x21) { X_1 = x12; X_2 = x21; } //结点2在结点1水平部分重合 else if (x11 <= x21 && x12 >= x21 && x12 <= x22) { X_1 = (x12 + x21) / 2; X_2 = X_1; } else if (x11 >= x21 && x12 <= x22) { X_1 = (x11 + x12) / 2; X_2 = X_1; } else if (x21 >= x11 && x22 <= x12) { X_1 = (x21 + x22) / 2; X_2 = X_1; } else if (x11 <= x22 && x12 >= x22) { X_1 = (x11 + x22) / 2; X_2 = X_1; } //Y判断: var y11 = n1.top, y12 = n1.top + n1.height, y21 = n2.top, y22 = n2.top + n2.height; //结点2在结点1上边 if (y11 >= y22) { Y_1 = y11; Y_2 = y22; } //结点2在结点1下边 else if (y12 <= y21) { Y_1 = y12; Y_2 = y21; } //结点2在结点1垂直部分重合 else if (y11 <= y21 && y12 >= y21 && y12 <= y22) { Y_1 = (y12 + y21) / 2; Y_2 = Y_1; } else if (y11 >= y21 && y12 <= y22) { Y_1 = (y11 + y12) / 2; Y_2 = Y_1; } else if (y21 >= y11 && y22 <= y12) { Y_1 = (y21 + y22) / 2; Y_2 = Y_1; } else if (y11 <= y22 && y12 >= y22) { Y_1 = (y11 + y22) / 2; Y_2 = Y_1; } return { "start": [X_1, Y_1], "end": [X_2, Y_2] }; }, //计算两个结点间要连折线的话,连线的所有坐标 calcPolyPoints: function (n1, n2, type, M) { //开始/结束两个结点的中心 var SP = { x: n1.left + n1.width / 2, y: n1.top + n1.height / 2 }; var EP = { x: n2.left + n2.width / 2, y: n2.top + n2.height / 2 }; var sp = [], m1 = [], m2 = [], ep = []; //如果是允许中段可左右移动的折线,则参数M为可移动中段线的X坐标 //粗略计算起始点 sp = [SP.x, SP.y]; ep = [EP.x, EP.y]; if (type == "lr") { //粗略计算2个中点 m1 = [M, SP.y]; m2 = [M, EP.y]; //再具体分析修改开始点和中点1 if (m1[0] > n1.left && m1[0] < n1.left + n1.width) { m1[1] = (SP.y > EP.y ? n1.top : n1.top + n1.height); sp[0] = m1[0]; sp[1] = m1[1]; } else { sp[0] = (m1[0] < n1.left ? n1.left : n1.left + n1.width) } //再具体分析中点2和结束点 if (m2[0] > n2.left && m2[0] < n2.left + n2.width) { m2[1] = (SP.y > EP.y ? n2.top + n2.height : n2.top); ep[0] = m2[0]; ep[1] = m2[1]; } else { ep[0] = (m2[0] < n2.left ? n2.left : n2.left + n2.width) } } //如果是允许中段可上下移动的折线,则参数M为可移动中段线的Y坐标 else if (type == "tb") { //粗略计算2个中点 m1 = [SP.x, M]; m2 = [EP.x, M]; //再具体分析修改开始点和中点1 if (m1[1] > n1.top && m1[1] < n1.top + n1.height) { m1[0] = (SP.x > EP.x ? n1.left : n1.left + n1.width); sp[0] = m1[0]; sp[1] = m1[1]; } else { sp[1] = (m1[1] < n1.top ? n1.top : n1.top + n1.height) } //再具体分析中点2和结束点 if (m2[1] > n2.top && m2[1] < n2.top + n2.height) { m2[0] = (SP.x > EP.x ? n2.left + n2.width : n2.left); ep[0] = m2[0]; ep[1] = m2[1]; } else { ep[1] = (m2[1] < n2.top ? n2.top : n2.top + n2.height); } } return { start: sp, m1: m1, m2: m2, end: ep }; }, //初始化折线中段的X/Y坐标,mType='rb'时为X坐标,mType='tb'时为Y坐标 getMValue: function (n1, n2, mType) { if (mType == "lr") { return (n1.left + n1.width / 2 + n2.left + n2.width / 2) / 2; } else if (mType == "tb") { return (n1.top + n1.height / 2 + n2.top + n2.height / 2) / 2; } }, //增加一条线 addLine: function (id, json) { if (this.onItemAdd != null && !this.onItemAdd(id, "line", json)) return; if (this.$undoStack && this.$editable) { this.pushOper("delLine", [id]); } var n1 = null, n2 = null;//获取开始/结束结点的数据 if (json.from == json.to) return; //避免两个节点间不能有一条以上同向接连线 for (var k in this.$lineData) { if ((json.from == this.$lineData[k].from && json.to == this.$lineData[k].to)) return; } var n1 = this.$nodeData[json.from], n2 = this.$nodeData[json.to];//获取开始/结束结点的数据 if (!n1 || !n2) return; var res; if (json.type && json.type != "sl") res = GooFlow.prototype.calcPolyPoints(n1, n2, json.type, json.M); else res = GooFlow.prototype.calcStartEnd(n1, n2); if (!res) return; this.$lineData[id] = {}; this.$lineData[id].setInfo = json.setInfo; this.$lineData[id].id = json.id; if (json.type) { this.$lineData[id].type = json.type; this.$lineData[id].M = json.M; } else this.$lineData[id].type = "sl";//默认为直线 this.$lineData[id].from = json.from; this.$lineData[id].to = json.to; this.$lineData[id].name = json.name; if (json.mark) this.$lineData[id].marked = json.mark; else this.$lineData[id].marked = false; if (this.$lineData[id].type == "sl") this.$lineDom[id] = GooFlow.prototype.drawLine(id, res.start, res.end, json.mark); else this.$lineDom[id] = GooFlow.prototype.drawPoly(id, res.start, res.m1, res.m2, res.end, json.mark); this.$draw.appendChild(this.$lineDom[id]); if (GooFlow.prototype.useSVG == "") { this.$lineDom[id].childNodes[1].innerHTML = json.name; if (this.$lineData[id].type != "sl") { var Min = (res.start[0] > res.end[0] ? res.end[0] : res.start[0]); if (Min > res.m2[0]) Min = res.m2[0]; if (Min > res.m1[0]) Min = res.m1[0]; this.$lineDom[id].childNodes[1].style.left = (res.m2[0] + res.m1[0]) / 2 - Min - this.$lineDom[id].childNodes[1].offsetWidth / 2 + 4; Min = (res.start[1] > res.end[1] ? res.end[1] : res.start[1]); if (Min > res.m2[1]) Min = res.m2[1]; if (Min > res.m1[1]) Min = res.m1[1]; this.$lineDom[id].childNodes[1].style.top = (res.m2[1] + res.m1[1]) / 2 - Min - this.$lineDom[id].childNodes[1].offsetHeight / 2; } else this.$lineDom[id].childNodes[1].style.left = ((res.end[0] - res.start[0]) * (res.end[0] > res.start[0] ? 1 : -1) - this.$lineDom[id].childNodes[1].offsetWidth) / 2 + 4; } else this.$lineDom[id].childNodes[2].textContent = json.name; ++this.$lineCount; if (this.$editable) { this.$lineData[id].alt = true; if (this.$deletedItem[id]) delete this.$deletedItem[id];//在回退删除操作时,去掉该元素的删除记录 } }, //重构所有连向某个结点的线的显示,传参结构为$nodeData数组的一个单元结构 resetLines: function (id, node) { for (var i in this.$lineData) { var other = null;//获取结束/开始结点的数据 var res; if (this.$lineData[i].from == id) {//找结束点 other = this.$nodeData[this.$lineData[i].to] || null; if (other == null) continue; if (this.$lineData[i].type == "sl") res = GooFlow.prototype.calcStartEnd(node, other); else res = GooFlow.prototype.calcPolyPoints(node, other, this.$lineData[i].type, this.$lineData[i].M) if (!res) break; } else if (this.$lineData[i].to == id) {//找开始点 other = this.$nodeData[this.$lineData[i].from] || null; if (other == null) continue; if (this.$lineData[i].type == "sl") res = GooFlow.prototype.calcStartEnd(other, node); else res = GooFlow.prototype.calcPolyPoints(other, node, this.$lineData[i].type, this.$lineData[i].M); if (!res) break; } if (other == null) continue; this.$draw.removeChild(this.$lineDom[i]); if (this.$lineData[i].type == "sl") { this.$lineDom[i] = GooFlow.prototype.drawLine(i, res.start, res.end, this.$lineData[i].marked); } else { this.$lineDom[i] = GooFlow.prototype.drawPoly(i, res.start, res.m1, res.m2, res.end, this.$lineData[i].marked); } this.$draw.appendChild(this.$lineDom[i]); if (GooFlow.prototype.useSVG == "") { this.$lineDom[i].childNodes[1].innerHTML = this.$lineData[i].name; if (this.$lineData[i].type != "sl") { var Min = (res.start[0] > res.end[0] ? res.end[0] : res.start[0]); if (Min > res.m2[0]) Min = res.m2[0]; if (Min > res.m1[0]) Min = res.m1[0]; this.$lineDom[i].childNodes[1].style.left = (res.m2[0] + res.m1[0]) / 2 - Min - this.$lineDom[i].childNodes[1].offsetWidth / 2 + 4; Min = (res.start[1] > res.end[1] ? res.end[1] : res.start[1]); if (Min > res.m2[1]) Min = res.m2[1]; if (Min > res.m1[1]) Min = res.m1[1]; this.$lineDom[i].childNodes[1].style.top = (res.m2[1] + res.m1[1]) / 2 - Min - this.$lineDom[i].childNodes[1].offsetHeight / 2 - 4; } else this.$lineDom[i].childNodes[1].style.left = ((res.end[0] - res.start[0]) * (res.end[0] > res.start[0] ? 1 : -1) - this.$lineDom[i].childNodes[1].offsetWidth) / 2 + 4; } else this.$lineDom[i].childNodes[2].textContent = this.$lineData[i].name; } }, //重新设置连线的样式 newType= "sl":直线, "lr":中段可左右移动型折线, "tb":中段可上下移动型折线 setLineType: function (id, newType) { if (!newType || newType == null || newType == "" || newType == this.$lineData[id].type) return false; if (this.onLineSetType != null && !this.onLineSetType(id, newType)) return; if (this.$undoStack) { var paras = [id, this.$lineData[id].type]; this.pushOper("setLineType", paras); if (this.$lineData[id].type != "sl") { var para2 = [id, this.$lineData[id].M]; this.pushOper("setLineM", para2); } } var from = this.$lineData[id].from; var to = this.$lineData[id].to; this.$lineData[id].type = newType; var res; //如果是变成折线 if (newType != "sl") { var res = GooFlow.prototype.calcPolyPoints(this.$nodeData[from], this.$nodeData[to], this.$lineData[id].type, this.$lineData[id].M); this.setLineM(id, this.getMValue(this.$nodeData[from], this.$nodeData[to], newType), true); } //如果是变回直线 else { delete this.$lineData[id].M; this.$lineMove.hide().removeData("type").removeData("tid"); res = GooFlow.prototype.calcStartEnd(this.$nodeData[from], this.$nodeData[to]); if (!res) return; this.$draw.removeChild(this.$lineDom[id]); this.$lineDom[id] = GooFlow.prototype.drawLine(id, res.start, res.end, this.$lineData[id].marked || this.$focus == id); this.$draw.appendChild(this.$lineDom[id]); if (GooFlow.prototype.useSVG == "") { this.$lineDom[id].childNodes[1].innerHTML = this.$lineData[id].name; this.$lineDom[id].childNodes[1].style.left = ((res.end[0] - res.start[0]) * (res.end[0] > res.start[0] ? 1 : -1) - this.$lineDom[id].childNodes[1].offsetWidth) / 2 + 4; } else this.$lineDom[id].childNodes[2].textContent = this.$lineData[id].name; } if (this.$focus == id) { this.focusItem(id); } if (this.$editable) { this.$lineData[id].alt = true; } }, //设置折线中段的X坐标值(可左右移动时)或Y坐标值(可上下移动时) setLineM: function (id, M, noStack) { if (!this.$lineData[id] || M < 0 || !this.$lineData[id].type || this.$lineData[id].type == "sl") return false; if (this.onLineMove != null && !this.onLineMove(id, M)) return false; if (this.$undoStack && !noStack) { var paras = [id, this.$lineData[id].M]; this.pushOper("setLineM", paras); } var from = this.$lineData[id].from; var to = this.$lineData[id].to; this.$lineData[id].M = M; var ps = GooFlow.prototype.calcPolyPoints(this.$nodeData[from], this.$nodeData[to], this.$lineData[id].type, this.$lineData[id].M); this.$draw.removeChild(this.$lineDom[id]); this.$lineDom[id] = GooFlow.prototype.drawPoly(id, ps.start, ps.m1, ps.m2, ps.end, this.$lineData[id].marked || this.$focus == id); this.$draw.appendChild(this.$lineDom[id]); if (GooFlow.prototype.useSVG == "") { this.$lineDom[id].childNodes[1].innerHTML = this.$lineData[id].name; var Min = (ps.start[0] > ps.end[0] ? ps.end[0] : ps.start[0]); if (Min > ps.m2[0]) Min = ps.m2[0]; if (Min > ps.m1[0]) Min = ps.m1[0]; this.$lineDom[id].childNodes[1].style.left = (ps.m2[0] + ps.m1[0]) / 2 - Min - this.$lineDom[id].childNodes[1].offsetWidth / 2 + 4; Min = (ps.start[1] > ps.end[1] ? ps.end[1] : ps.start[1]); if (Min > ps.m2[1]) Min = ps.m2[1]; if (Min > ps.m1[1]) Min = ps.m1[1]; this.$lineDom[id].childNodes[1].style.top = (ps.m2[1] + ps.m1[1]) / 2 - Min - this.$lineDom[id].childNodes[1].offsetHeight / 2 - 4; } else this.$lineDom[id].childNodes[2].textContent = this.$lineData[id].name; if (this.$editable) { this.$lineData[id].alt = true; } }, //删除转换线 delLine: function (id) { if (!this.$lineData[id]) return; if (this.onItemDel != null && !this.onItemDel(id, "node")) return; if (this.$undoStack) { var paras = [id, this.$lineData[id]]; this.pushOper("addLine", paras); } this.$draw.removeChild(this.$lineDom[id]); delete this.$lineData[id]; delete this.$lineDom[id]; if (this.$focus == id) this.$focus = ""; --this.$lineCount; if (this.$editable) { //在回退新增操作时,如果节点ID以this.$id+"_line_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中 if (id.indexOf(this.$id + "_line_") < 0) this.$deletedItem[id] = "line"; } this.$lineOper.hide(); }, //用颜色标注/取消标注一个结点或转换线,常用于显示重点或流程的进度。 //这是一个在编辑模式中无用,但是在纯浏览模式中非常有用的方法,实际运用中可用于跟踪流程的进度。 markItem: function (id, type, mark) { if (type == "node") { if (!this.$nodeData[id]) return; if (this.onItemMark != null && !this.onItemMark(id, "node", mark)) return; this.$nodeData[id].marked = mark || false; if (mark) this.$nodeDom[id].addClass("item_mark"); else this.$nodeDom[id].removeClass("item_mark"); } else if (type == "line") { if (!this.$lineData[id]) return; if (this.onItemMark != null && !this.onItemMark(id, "line", mark)) return; this.$lineData[id].marked = mark || false; if (GooFlow.prototype.useSVG != "") { if (mark) { this.$nodeDom[id].childNodes[1].setAttribute("stroke", "#ff3300"); this.$nodeDom[id].childNodes[1].setAttribute("marker-end", "url(#arrow2)"); } else { this.$nodeDom[id].childNodes[1].setAttribute("stroke", "gray"); this.$nodeDom[id].childNodes[1].setAttribute("marker-end", "url(#arrow1)"); } } else { if (mark) this.$nodeDom[id].strokeColor = "#ff3300"; else this.$nodeDom[id].strokeColor = "gray" } } if (this.$undoStatck) { var paras = [id, type, !mark]; this.pushOper("markItem", paras); } }, ////////////////////////以下为区域分组块操作 moveArea: function (id, left, top) { if (!this.$areaData[id]) return; if (this.onItemMove != null && !this.onItemMove(id, "area", left, top)) return; if (this.$undoStack) { var paras = [id, this.$areaData[id].left, this.$areaData[id].top]; this.pushOper("moveNode", paras); } if (left < 0) left = 0; if (top < 0) top = 0; $("#" + id).css({ left: left + "px", top: top + "px" }); this.$areaData[id].left = left; this.$areaData[id].top = top; if (this.$editable) { this.$areaData[id].alt = true; } }, //删除区域分组 delArea: function (id) { if (!this.$areaData[id]) return; if (this.$undoStack) { var paras = [id, this.$areaData[id]]; this.pushOper("addArea", paras); } if (this.onItemDel != null && !this.onItemDel(id, "node")) return; delete this.$areaData[id]; this.$areaDom[id].remove(); delete this.$areaDom[id]; --this.$areaCount; if (this.$editable) { //在回退新增操作时,如果节点ID以this.$id+"_area_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中 if (id.indexOf(this.$id + "_area_") < 0) this.$deletedItem[id] = "area"; } }, //设置区域分组的颜色 setAreaColor: function (id, color) { if (!this.$areaData[id]) return; if (this.$undoStack) { var paras = [id, this.$areaData[id].color]; this.pushOper("setAreaColor", paras); } if (color == "red" || color == "yellow" || color == "blue" || color == "green") { this.$areaDom[id].removeClass("area_" + this.$areaData[id].color).addClass("area_" + color); this.$areaData[id].color = color; } if (this.$editable) { this.$areaData[id].alt = true; } }, //设置区域分块的尺寸 resizeArea: function (id, width, height) { if (!this.$areaData[id]) return; if (this.onItemResize != null && !this.onItemResize(id, "area", width, height)) return; if (this.$undoStack) { var paras = [id, this.$areaData[id].width, this.$areaData[id].height]; this.pushOper("resizeArea", paras); } var hack = 0; if (navigator.userAgent.indexOf("8.0") != -1) hack = 2; this.$areaDom[id].children(".bg").css({ width: width - 2 + "px", height: height - 2 + "px" }); width = this.$areaDom[id].outerWidth(); height = this.$areaDom[id].outerHeight(); this.$areaDom[id].children("bg").css({ width: width - 2 + "px", height: height - 2 + "px" }); this.$areaData[id].width = width; this.$areaData[id].height = height; if (this.$editable) { this.$areaData[id].alt = true; } }, addArea: function (id, json) { if (this.onItemAdd != null && !this.onItemAdd(id, "area", json)) return; if (this.$undoStack && this.$editable) { this.pushOper("delArea", [id]); } this.$areaDom[id] = $("
" + "
"); this.$areaData[id] = json; this.$group.append(this.$areaDom[id]); if (this.$nowType != "group") this.$areaDom[id].children("div:eq(1)").css("display", "none"); ++this.$areaCount; if (this.$editable) { this.$areaData[id].alt = true; if (this.$deletedItem[id]) delete this.$deletedItem[id];//在回退删除操作时,去掉该元素的删除记录 } }, //重构整个流程图设计器的宽高 reinitSize: function (width, height) { var w = (width || 800) - 2; var h = (height || 500) - 2; this.$bgDiv.css({ height: h + "px", width: w + "px" }); var headHeight = 0, hack = 10; if (this.$head != null) { headHeight = 24; hack = 7; } if (this.$tool != null) { this.$tool.css({ height: h - headHeight - hack + "px" }); } w -= 39; h = h - headHeight - (this.$head != null ? 5 : 8); this.$workArea.parent().css({ height: h + "px", width: w + "px" }); this.$workArea.css({ height: h * 3 + "px", width: w * 3 + "px" }); if (GooFlow.prototype.useSVG == "") { this.$draw.coordsize = w * 3 + "," + h * 3; } this.$draw.style.width = w * 3 + "px"; this.$draw.style.height = +h * 3 + "px"; if (this.$group == null) { this.$group.css({ height: h * 3 + "px", width: w * 3 + "px" }); } } } //将此类的构造函数加入至JQUERY对象中 jQuery.extend({ createGooFlow: function (bgDiv, property) { return new GooFlow(bgDiv, property); } }); //获取一个DIV的绝对坐标的功能函数,即使是非绝对定位,一样能获取到 function getElCoordinate(dom) { var t = dom.offsetTop; var l = dom.offsetLeft; dom = dom.offsetParent; while (dom) { t += dom.offsetTop; l += dom.offsetLeft; dom = dom.offsetParent; }; return { top: t, left: l }; } //兼容各种浏览器的,获取鼠标真实位置 function mousePosition(ev) { if (!ev) ev = window.event; if (ev.pageX || ev.pageY) { return { x: ev.pageX, y: ev.pageY }; } return { x: ev.clientX + document.documentElement.scrollLeft - document.body.clientLeft, y: ev.clientY + document.documentElement.scrollTop - document.body.clientTop }; }