GooFlow.js 78 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000
  1. //定义一个区域图类:
  2. function GooFlow(bgDiv,property){
  3. if (navigator.userAgent.indexOf("MSIE 8.0")>0||navigator.userAgent.indexOf("MSIE 7.0")>0||navigator.userAgent.indexOf("MSIE 6.0")>0)
  4. GooFlow.prototype.useSVG="";
  5. else GooFlow.prototype.useSVG="1";
  6. //初始化区域图的对象
  7. this.$id=bgDiv.attr("id");
  8. this.$bgDiv=bgDiv;//最父框架的DIV
  9. this.$bgDiv.addClass("GooFlow");
  10. if(GooFlow.prototype.color.font){
  11. this.$bgDiv.css("color",GooFlow.prototype.color.font);
  12. }
  13. var width=(property.width||800)-2;
  14. var height=(property.height||500)-2;
  15. this.$bgDiv.css({width:width+"px",height:height+"px"});
  16. this.$tool=null;//左侧工具栏对象
  17. this.$head=null;//顶部标签及工具栏按钮
  18. this.$title="newFlow_1";//流程图的名称
  19. this.$nodeRemark={};//每一种结点或按钮的说明文字,JSON格式,key为类名,value为用户自定义文字说明
  20. this.$nowType="cursor";//当前要绘制的对象类型
  21. this.$lineData={};
  22. this.$lineCount=0;
  23. this.$nodeData={};
  24. this.$nodeCount=0;
  25. this.$areaData={};
  26. this.$areaCount=0;
  27. this.$lineDom={};
  28. this.$nodeDom={};
  29. this.$areaDom={};
  30. this.$max=property.initNum||1;//计算默认ID值的起始SEQUENCE
  31. this.$focus="";//当前被选定的结点/转换线ID,如果没选中或者工作区被清空,则为""
  32. this.$cursor="default";//鼠标指针在工作区内的样式
  33. this.$editable=false;//工作区是否可编辑
  34. this.$deletedItem={};//在流程图的编辑操作中被删除掉的元素ID集合,元素ID为KEY,元素类型(node,line.area)为VALUE
  35. var headHeight=0;
  36. var tmp="";
  37. if(property.haveHead){
  38. tmp="<div class='GooFlow_head' "+(GooFlow.prototype.color.main? "style='border-bottom-color:"+GooFlow.prototype.color.main+"'" : "")
  39. +">";
  40. if(property.headLabel){
  41. tmp+="<label title='"+(property.initLabelText||"newFlow_1")+"' "
  42. +(GooFlow.prototype.color.main? "style='background:"+GooFlow.prototype.color.main+"'" : "")+">"+(property.initLabelText||"newFlow_1")+"</label>";
  43. }
  44. for(var x=0;x<property.headBtns.length;++x){
  45. tmp+="<a href='javascript:void(0)' class='GooFlow_head_btn'><i class='ico_"+property.headBtns[x]+"'></i></a>"
  46. }
  47. tmp+="</div>";
  48. this.$head=$(tmp);
  49. this.$bgDiv.append(this.$head);
  50. headHeight=24;
  51. //以下是当工具栏按钮被点击时触发的事件自定义(虚函数),格式为function(),因为可直接用THIS操作对象本身,不用传参;用户可自行重定义:
  52. this.onBtnNewClick=null;//新建流程图按钮被点中
  53. this.onBtnOpenClick=null;//打开流程图按钮定义
  54. this.onBtnSaveClick=null;//保存流程图按钮定义
  55. this.onFreshClick=null;//重载流程图按钮定义
  56. if(property.headBtns)
  57. this.$head.on("click",{inthis:this},function(e){
  58. if(!e)e=window.event;
  59. var tar=e.target;
  60. if(tar.tagName=="DIV"||tar.tagName=="SPAN") return;
  61. else if(tar.tagName=="a") tar=tar.childNode[0];
  62. var This=e.data.inthis;
  63. //定义顶部操作栏按钮的事件
  64. switch($(tar).attr("class")){
  65. case "ico_new": if(This.onBtnNewClick!=null) This.onBtnNewClick();break;
  66. case "ico_open": if(This.onBtnOpenClick!=null) This.onBtnOpenClick();break;
  67. case "ico_save": if(This.onBtnSaveClick!=null) This.onBtnSaveClick();break;
  68. case "ico_undo": This.undo();break;
  69. case "ico_redo": This.redo();break;
  70. case "ico_reload" :if(This.onFreshClick!=null) This.onFreshClick();break;
  71. }
  72. });
  73. }
  74. var toolWidth=0;
  75. if(property.haveTool){
  76. this.$bgDiv.append("<div class='GooFlow_tool'"+(property.haveHead? "":" style='margin-top:3px'")+"><div style='height:"+(height-headHeight-(property.haveHead? 7:10))+"px' class='GooFlow_tool_div'></div></div>");
  77. this.$tool=this.$bgDiv.find(".GooFlow_tool div");
  78. //未加代码:加入绘图工具按钮
  79. this.$tool.append(
  80. "<a href='javascript:void(0)' type='cursor' class='GooFlow_tool_btndown' id='"+this.$id+"_btn_cursor'><i class='ico_cursor'/></a>"
  81. //+"<a href='javascript:void(0)' type='mutiselect' class='GooFlow_tool_btn' id='"+this.$id+"_btn_mutiselect'><i class='ico_mutiselect'/></a>"
  82. +"<a href='javascript:void(0)' type='direct' class='GooFlow_tool_btn' id='"+this.$id+"_btn_direct'><i class='ico_direct'/></a>"
  83. );
  84. if(property.toolBtns&&property.toolBtns.length>0){
  85. tmp="<span/>";
  86. for(var i=0;i<property.toolBtns.length;++i){
  87. tmp+="<a href='javascript:void(0)' type='"+property.toolBtns[i]+"' id='"+this.$id+"_btn_"+property.toolBtns[i].split(" ")[0]+"' class='GooFlow_tool_btn'><i class='ico_"+property.toolBtns[i]+"'/></a>";//加入自定义按钮
  88. }
  89. this.$tool.append(tmp);
  90. }
  91. //加入区域划分框工具开关按钮
  92. if(property.haveGroup)
  93. this.$tool.append("<span/><a href='javascript:void(0)' type='group' class='GooFlow_tool_btn' id='"+this.$id+"_btn_group'><i class='ico_group'/></a>");
  94. toolWidth=31;
  95. this.$nowType="cursor";
  96. //绑定各个按钮的点击事件
  97. this.$tool.on("click",{inthis:this},function(e){
  98. if(!e)e=window.event;
  99. var tar;
  100. switch(e.target.tagName){
  101. case "SPAN":return false;
  102. case "DIV":return false;
  103. case "I": tar=e.target.parentNode;break;
  104. case "A": tar=e.target;
  105. };
  106. var type=$(tar).attr("type");
  107. e.data.inthis.switchToolBtn(type);
  108. return false;
  109. });
  110. this.$editable=true;//只有具有工具栏时可编辑
  111. }
  112. width=width-toolWidth-9;
  113. height=height-headHeight-(property.haveHead? 5:8);
  114. this.$bgDiv.append("<div class='GooFlow_work' style='width:"+(width)+"px;height:"+(height)+"px;"+(property.haveHead? "":"margin-top:3px")+"'></div>");
  115. this.$workArea=$("<div class='GooFlow_work_inner' style='width:"+width*3+"px;height:"+height*3+"px'></div>")
  116. .attr({"unselectable":"on","onselectstart":'return false',"onselect":'document.selection.empty()'});
  117. this.$bgDiv.children(".GooFlow_work").append(this.$workArea);
  118. this.$draw=null;//画矢量线条的容器
  119. this.initDraw("draw_"+this.$id,width,height);
  120. this.$group=null;
  121. if(property.haveGroup)
  122. this.initGroup(width,height);
  123. if(this.$editable){
  124. this.$workArea.on("click",{inthis:this},function(e){
  125. if(!e)e=window.event;
  126. var This=e.data.inthis;
  127. if(!This.$editable)return;
  128. var type=This.$nowType;
  129. if(type=="cursor"){
  130. var t=$(e.target);
  131. var n=t.prop("tagName");
  132. //alert(n);
  133. if(n=="svg"||(n=="DIV"&&t.prop("class").indexOf("GooFlow_work")>-1)||n=="LABEL"){
  134. if(This.$lineOper.data("tid")){
  135. This.focusItem(This.$lineOper.data("tid"),false);
  136. //This.$mpFrom.removeData("p");
  137. }
  138. else{This.blurItem();}
  139. }
  140. return;
  141. }
  142. else if(type=="direct"||type=="group")return;
  143. var X,Y;
  144. var ev=mousePosition(e),t=getElCoordinate(this);
  145. X=ev.x-t.left+this.parentNode.scrollLeft-1;
  146. Y=ev.y-t.top+this.parentNode.scrollTop-1;
  147. This.addNode(This.$id+"_node_"+This.$max,{name:"node_"+This.$max,left:X,top:Y,type:This.$nowType});
  148. This.$max++;
  149. });
  150. //划线或改线时用的绑定
  151. this.$workArea.mousemove({inthis:this},function(e){
  152. if(e.data.inthis.$nowType!="direct"&&!e.data.inthis.$mpTo.data("p")) return;
  153. var lineStart=$(this).data("lineStart");
  154. var lineEnd=$(this).data("lineEnd");
  155. if(!lineStart&&!lineEnd)return;
  156. var ev=mousePosition(e),t=getElCoordinate(this);
  157. var X,Y;
  158. X=ev.x-t.left+this.parentNode.scrollLeft;
  159. Y=ev.y-t.top+this.parentNode.scrollTop;
  160. var line=document.getElementById("GooFlow_tmp_line");
  161. if(lineStart){
  162. if(GooFlow.prototype.useSVG!=""){
  163. line.childNodes[0].setAttribute("d","M "+lineStart.x+" "+lineStart.y+" L "+X+" "+Y);
  164. line.childNodes[1].setAttribute("d","M "+lineStart.x+" "+lineStart.y+" L "+X+" "+Y);
  165. if(line.childNodes[1].getAttribute("marker-end")=="url(\"#arrow2\")")
  166. line.childNodes[1].setAttribute("marker-end","url(#arrow3)");
  167. else line.childNodes[1].setAttribute("marker-end","url(#arrow2)");
  168. }
  169. else line.points.value=lineStart.x+","+lineStart.y+" "+X+","+Y;
  170. }else if(lineEnd){
  171. if(GooFlow.prototype.useSVG!=""){
  172. line.childNodes[0].setAttribute("d","M "+X+" "+Y+" L "+lineEnd.x+" "+lineEnd.y);
  173. line.childNodes[1].setAttribute("d","M "+X+" "+Y+" L "+lineEnd.x+" "+lineEnd.y);
  174. if(line.childNodes[1].getAttribute("marker-end")=="url(\"#arrow2\")")
  175. line.childNodes[1].setAttribute("marker-end","url(#arrow3)");
  176. else line.childNodes[1].setAttribute("marker-end","url(#arrow2)");
  177. }
  178. else line.points.value=X+","+Y+" "+lineEnd.x+","+lineEnd.y;
  179. }
  180. });
  181. this.$workArea.mouseup({inthis:this},function(e){
  182. var This=e.data.inthis;
  183. if(This.$nowType!="direct"&&!This.$mpTo.data("p")) return;
  184. var tmp=document.getElementById("GooFlow_tmp_line");
  185. if(tmp){
  186. $(this).css("cursor","auto").removeData("lineStart").removeData("lineEnd");
  187. This.$mpTo.hide().removeData("p");
  188. This.$mpFrom.hide().removeData("p");
  189. This.$draw.removeChild(tmp);
  190. This.focusItem(This.$focus,false);
  191. }else{
  192. This.$lineOper.removeData("tid");
  193. }
  194. });
  195. //为了结点而增加的一些集体delegate绑定
  196. this.initWorkForNode();
  197. //对结点进行移动或者RESIZE时用来显示的遮罩层
  198. this.$ghost=$("<div class='rs_ghost'></div>").attr({"unselectable":"on","onselectstart":'return false',"onselect":'document.selection.empty()'});
  199. this.$bgDiv.append(this.$ghost);
  200. this.$textArea=$("<textarea></textarea>");
  201. this.$bgDiv.append(this.$textArea);
  202. this.$lineMove=$("<div class='GooFlow_line_move' style='display:none'></div>");//操作折线时的移动框
  203. this.$workArea.append(this.$lineMove);
  204. this.$lineMove.on("mousedown",{inthis:this},function(e){
  205. if(e.button==2)return false;
  206. var lm=$(this);
  207. lm.css({"background-color":GooFlow.prototype.color.font||"#333"});
  208. var This=e.data.inthis;
  209. var ev=mousePosition(e),t=getElCoordinate(This.$workArea[0]);
  210. var X,Y;
  211. X=ev.x-t.left+This.$workArea[0].parentNode.scrollLeft;
  212. Y=ev.y-t.top+This.$workArea[0].parentNode.scrollTop;
  213. var p=This.$lineMove.position();
  214. var vX=X-p.left,vY=Y-p.top;
  215. var isMove=false;
  216. document.onmousemove=function(e){
  217. if(!e)e=window.event;
  218. var ev=mousePosition(e);
  219. var ps=This.$lineMove.position();
  220. X=ev.x-t.left+This.$workArea[0].parentNode.scrollLeft;
  221. Y=ev.y-t.top+This.$workArea[0].parentNode.scrollTop;
  222. if(This.$lineMove.data("type")=="lr"){
  223. X=X-vX;
  224. if(X<0) X=0;
  225. else if(X>This.$workArea.width())
  226. X=This.$workArea.width();
  227. This.$lineMove.css({left:X+"px"});
  228. }
  229. else if(This.$lineMove.data("type")=="tb"){
  230. Y=Y-vY;
  231. if(Y<0) Y=0;
  232. else if(Y>This.$workArea.height())
  233. Y=This.$workArea.height();
  234. This.$lineMove.css({top:Y+"px"});
  235. }
  236. isMove=true;
  237. }
  238. document.onmouseup=function(e){
  239. if(isMove){
  240. var p=This.$lineMove.position();
  241. if(This.$lineMove.data("type")=="lr")
  242. This.setLineM(This.$lineMove.data("tid"),p.left+3);
  243. else if(This.$lineMove.data("type")=="tb")
  244. This.setLineM(This.$lineMove.data("tid"),p.top+3);
  245. }
  246. This.$lineMove.css({"background-color":"transparent"});
  247. if(This.$focus==This.$lineMove.data("tid")){
  248. This.focusItem(This.$lineMove.data("tid"));
  249. }
  250. document.onmousemove=null;
  251. document.onmouseup=null;
  252. }
  253. });
  254. //选定一条转换线后出现的浮动操作栏,有改变线的样式和删除线等按钮。
  255. this.$lineOper=$("<div class='GooFlow_line_oper' style='display:none'><i class='b_l1'></i><i class='b_l2'></i><i class='b_l3'></i><i class='b_x'></i></div>");//选定线时显示的操作框
  256. this.$workArea.parent().append(this.$lineOper);
  257. this.$lineOper.on("click",{inthis:this},function(e){
  258. if(!e)e=window.event;
  259. if(e.target.tagName!="I") return;
  260. var This=e.data.inthis;
  261. var id=$(this).data("tid");
  262. switch($(e.target).attr("class")){
  263. case "b_x":
  264. This.delLine(id);
  265. this.style.display="none";break;
  266. case "b_l1":
  267. This.setLineType(id,"lr");break;
  268. case "b_l2":
  269. This.setLineType(id,"tb");break;
  270. case "b_l3":
  271. This.setLineType(id,"sl");break;
  272. }
  273. });
  274. //新增移动线两个端点至新的结点功能移动功能,这里要提供移动用的DOM
  275. this.$mpFrom=$("<div class='GooFlow_line_mp' style='display:none'></div>");
  276. this.$mpTo=$("<div class='GooFlow_line_mp' style='display:none'></div>");
  277. this.$workArea.append(this.$mpFrom).append(this.$mpTo);
  278. this.initLinePointsChg();
  279. //下面绑定当结点/线/分组块的一些操作事件,这些事件可直接通过this访问对象本身
  280. //当操作某个单元(结点/线/分组块)被添加时,触发的方法,返回FALSE可阻止添加事件的发生
  281. //格式function(id,type,json):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值,json即addNode,addLine或addArea方法的第二个传参json.
  282. this.onItemAdd=null;
  283. //当操作某个单元(结点/线/分组块)被删除时,触发的方法,返回FALSE可阻止删除事件的发生
  284. //格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值
  285. this.onItemDel=null;
  286. //当操作某个单元(结点/分组块)被移动时,触发的方法,返回FALSE可阻止移动事件的发生
  287. //格式function(id,type,left,top):id是单元的唯一标识ID,type是单元的种类,有"node","area"两种取值,线line不支持移动,left是新的左边距坐标,top是新的顶边距坐标
  288. this.onItemMove=null;
  289. //当操作某个单元(结点/线/分组块)被重命名时,触发的方法,返回FALSE可阻止重命名事件的发生
  290. //格式function(id,name,type):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值,name是新的名称
  291. this.onItemRename=null;
  292. //当操作某个单元(结点/线)被由不选中变成选中时,触发的方法,返回FALSE可阻止选中事件的发生
  293. //格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line"两种取值,"area"不支持被选中
  294. this.onItemFocus=null;
  295. //当操作某个单元(结点/线)被由选中变成不选中时,触发的方法,返回FALSE可阻止取消选中事件的发生
  296. //格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line"两种取值,"area"不支持被取消选中
  297. this.onItemBlur=null;
  298. //当操作某个单元(结点/分组块)被重定义大小或造型时,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
  299. //格式function(id,type,width,height):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值;width是新的宽度,height是新的高度
  300. this.onItemResize=null;
  301. //当移动某条折线中段的位置,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
  302. //格式function(id,M):id是单元的唯一标识ID,M是中段的新X(或Y)的坐标
  303. this.onLineMove=null;
  304. //当变换某条连接线的类型,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
  305. //格式function(id,type):id是单元的唯一标识ID,type是连接线的新类型,"sl":直线,"lr":中段可左右移动的折线,"tb":中段可上下移动的折线
  306. this.onLineSetType=null;
  307. //当变换某条连接线的端点变更连接的结点时,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
  308. //格式function(id,newStart,newEnd):id是连线单元的唯一标识ID,newStart,newEnd分别是起始结点的ID和到达结点的ID
  309. this.onLinePointMove=null;
  310. //当用重色标注某个结点/转换线时触发的方法,返回FALSE可阻止重定大小/造型事件的发生
  311. //格式function(id,type,mark):id是单元的唯一标识ID,type是单元类型("node"结点,"line"转换线),mark为布尔值,表示是要标注TRUE还是取消标注FALSE
  312. this.onItemMark=null;
  313. if(property.useOperStack&&this.$editable){//如果要使用堆栈记录操作并提供“撤销/重做”的功能,只在编辑状态下有效
  314. this.$undoStack=[];
  315. this.$redoStack=[];
  316. this.$isUndo=0;
  317. ///////////////以下是构造撤销操作/重做操作的方法
  318. //为了节省浏览器内存空间,undo/redo中的操作缓存栈,最多只可放40步操作;超过40步时,将自动删掉最旧的一个缓存
  319. this.pushOper=function(funcName,paras){
  320. var len=this.$undoStack.length;
  321. if(this.$isUndo==1){
  322. this.$redoStack.push([funcName,paras]);
  323. this.$isUndo=false;
  324. if(this.$redoStack.length>40) this.$redoStack.shift();
  325. }else{
  326. this.$undoStack.push([funcName,paras]);
  327. if(this.$undoStack.length>40) this.$undoStack.shift();
  328. if(this.$isUndo==0){
  329. this.$redoStack.splice(0,this.$redoStack.length);
  330. }
  331. this.$isUndo=0;
  332. }
  333. };
  334. //将外部的方法加入到GooFlow对象的事务操作堆栈中,在过后的undo/redo操作中可以进行控制,一般用于对流程图以外的附加信息进行编辑的事务撤销/重做控制;
  335. //传参func为要执行方法对象,jsonPara为外部方法仅有的一个面向字面的JSON传参,由JSON对象带入所有要传的信息;
  336. //提示:为了让外部方法能够被UNDO/REDO,需要在编写这些外部方法实现时,加入对该方法执行后效果回退的另一个执行方法的pushExternalOper
  337. this.pushExternalOper=function(func,jsonPara){
  338. this.pushOper("externalFunc",[func,jsonPara]);
  339. };
  340. //撤销上一步操作
  341. this.undo=function(){
  342. if(this.$undoStack.length==0) return;
  343. this.blurItem();
  344. var tmp=this.$undoStack.pop();
  345. this.$isUndo=1;
  346. if(tmp[0]=="externalFunc"){
  347. tmp[1][0](tmp[1][1]);
  348. }
  349. else{
  350. //传参的数量,最多支持6个.
  351. switch(tmp[1].length){
  352. case 0:this[tmp[0]]();break;
  353. case 1:this[tmp[0]](tmp[1][0]);break;
  354. case 2:this[tmp[0]](tmp[1][0],tmp[1][1]);break;
  355. case 3:this[tmp[0]](tmp[1][0],tmp[1][1],tmp[1][2]);break;
  356. case 4:this[tmp[0]](tmp[1][0],tmp[1][1],tmp[1][2],tmp[1][3]);break;
  357. case 5:this[tmp[0]](tmp[1][0],tmp[1][1],tmp[1][2],tmp[1][3],tmp[1][4]);break;
  358. case 6:this[tmp[0]](tmp[1][0],tmp[1][1],tmp[1][2],tmp[1][3],tmp[1][4],tmp[1][5]);break;
  359. }
  360. }
  361. };
  362. //重做最近一次被撤销的操作
  363. this.redo=function(){
  364. if(this.$redoStack.length==0) return;
  365. this.blurItem();
  366. var tmp=this.$redoStack.pop();
  367. this.$isUndo=2;
  368. if(tmp[0]=="externalFunc"){
  369. tmp[1][0](tmp[1][1]);
  370. }
  371. else{
  372. //传参的数量,最多支持6个.
  373. switch(tmp[1].length){
  374. case 0:this[tmp[0]]();break;
  375. case 1:this[tmp[0]](tmp[1][0]);break;
  376. case 2:this[tmp[0]](tmp[1][0],tmp[1][1]);break;
  377. case 3:this[tmp[0]](tmp[1][0],tmp[1][1],tmp[1][2]);break;
  378. case 4:this[tmp[0]](tmp[1][0],tmp[1][1],tmp[1][2],tmp[1][3]);break;
  379. case 5:this[tmp[0]](tmp[1][0],tmp[1][1],tmp[1][2],tmp[1][3],tmp[1][4]);break;
  380. case 6:this[tmp[0]](tmp[1][0],tmp[1][1],tmp[1][2],tmp[1][3],tmp[1][4],tmp[1][5]);break;
  381. }
  382. }
  383. };
  384. }
  385. $(document).keydown({inthis:this},function(e){
  386. //绑定键盘操作
  387. var This=e.data.inthis;
  388. if(This.$focus=="")return;
  389. switch(e.keyCode){
  390. case 46://删除
  391. This.delNode(This.$focus,true);
  392. This.delLine(This.$focus);
  393. break;
  394. }
  395. });
  396. }
  397. }
  398. GooFlow.prototype={
  399. useSVG:"",
  400. getSvgMarker:function(id,color){
  401. var m=document.createElementNS("http://www.w3.org/2000/svg","marker");
  402. m.setAttribute("id",id);
  403. m.setAttribute("viewBox","0 0 6 6");
  404. m.setAttribute("refX",5);
  405. m.setAttribute("refY",3);
  406. m.setAttribute("markerUnits","strokeWidth");
  407. m.setAttribute("markerWidth",6);
  408. m.setAttribute("markerHeight",6);
  409. m.setAttribute("orient","auto");
  410. var path=document.createElementNS("http://www.w3.org/2000/svg","path");
  411. path.setAttribute("d","M 0 0 L 6 3 L 0 6 z");
  412. path.setAttribute("fill",color);
  413. path.setAttribute("stroke-width",0);
  414. m.appendChild(path);
  415. return m;
  416. },
  417. initDraw:function(id,width,height){
  418. var elem;
  419. if(GooFlow.prototype.useSVG!=""){
  420. this.$draw=document.createElementNS("http://www.w3.org/2000/svg","svg");//可创建带有指定命名空间的元素节点
  421. this.$workArea.prepend(this.$draw);
  422. var defs=document.createElementNS("http://www.w3.org/2000/svg","defs");
  423. this.$draw.appendChild(defs);
  424. defs.appendChild(GooFlow.prototype.getSvgMarker("arrow1",GooFlow.prototype.color.line||"#3892D3"));
  425. defs.appendChild(GooFlow.prototype.getSvgMarker("arrow2",GooFlow.prototype.color.mark||"#ff3300"));
  426. defs.appendChild(GooFlow.prototype.getSvgMarker("arrow3",GooFlow.prototype.color.mark||"#ff3300"));
  427. }
  428. else{
  429. this.$draw = document.createElement("v:group");
  430. this.$draw.coordsize = width*3+","+height*3;
  431. this.$workArea.prepend("<div class='GooFlow_work_vml' style='position:relative;width:"+width*3+"px;height:"+height*3+"px'></div>");
  432. this.$workArea.children("div")[0].insertBefore(this.$draw,null);
  433. }
  434. this.$draw.id = id;
  435. this.$draw.style.width = width*3 + "px";
  436. this.$draw.style.height = +height*3 + "px";
  437. //绑定连线的点击选中以及双击编辑事件
  438. var tmpClk=null;
  439. if(GooFlow.prototype.useSVG!="") tmpClk="g";
  440. else tmpClk="PolyLine";
  441. if(!this.$editable) return;
  442. $(this.$draw).delegate(tmpClk,"click",{inthis:this},function(e){
  443. e.data.inthis.focusItem(this.id,true);
  444. });
  445. $(this.$draw).delegate(tmpClk,"dblclick",{inthis:this},function(e){
  446. var oldTxt,x,y,from,to;
  447. var This=e.data.inthis;
  448. if(GooFlow.prototype.useSVG!=""){
  449. oldTxt=this.childNodes[2].textContent;
  450. from=this.getAttribute("from").split(",");
  451. to=this.getAttribute("to").split(",");
  452. }else{
  453. oldTxt=this.childNodes[1].innerHTML;
  454. var n=this.getAttribute("fromTo").split(",");
  455. from=[n[0],n[1]];
  456. to=[n[2],n[3]];
  457. }
  458. if(This.$lineData[this.id].type=="lr"){
  459. from[0]=This.$lineData[this.id].M;
  460. to[0]=from[0];
  461. }
  462. else if(This.$lineData[this.id].type=="tb"){
  463. from[1]=This.$lineData[this.id].M;
  464. to[1]=from[1];
  465. }
  466. x=(parseInt(from[0],10)+parseInt(to[0],10))/2-60;
  467. y=(parseInt(from[1],10)+parseInt(to[1],10))/2-12;
  468. var t=getElCoordinate(This.$workArea[0]);
  469. This.$textArea.val(oldTxt).css({display:"block",width:120,height:14,
  470. left:t.left+x-This.$workArea[0].parentNode.scrollLeft,
  471. top:t.top+y-This.$workArea[0].parentNode.scrollTop}).data("id",This.$focus).focus();
  472. This.$workArea.parent().one("mousedown",function(e){
  473. if(e.button==2)return false;
  474. This.setName(This.$textArea.data("id"),This.$textArea.val(),"line");
  475. This.$textArea.val("").removeData("id").hide();
  476. });
  477. });
  478. },
  479. initGroup:function(width,height){
  480. this.$group=$("<div class='GooFlow_work_group' style='width:"+width*3+"px;height:"+height*3+"px'></div>");//存放背景区域的容器
  481. this.$workArea.prepend(this.$group);
  482. if(!this.$editable) return;
  483. //区域划分框操作区的事件绑定
  484. this.$group.on("mousedown",{inthis:this},function(e){//绑定RESIZE功能以及移动功能
  485. if(e.button==2)return false;
  486. var This=e.data.inthis;
  487. if(This.$nowType!="group") return;
  488. if(This.$textArea.css("display")=="block"){
  489. This.setName(This.$textArea.data("id"),This.$textArea.val(),"area");
  490. This.$textArea.val("").removeData("id").hide();
  491. return false;
  492. };
  493. if(!e)e=window.event;
  494. var cursor=$(e.target).css("cursor");
  495. var id=e.target.parentNode;
  496. switch(cursor){
  497. case "nw-resize":id=id.parentNode;break;
  498. case "w-resize":id=id.parentNode;break;
  499. case "n-resize":id=id.parentNode;break;
  500. case "move":break;
  501. default:return;
  502. }
  503. id=id.id;
  504. var hack=1;
  505. if(navigator.userAgent.indexOf("8.0")!=-1) hack=0;
  506. var ev=mousePosition(e),t=getElCoordinate(This.$workArea[0]);
  507. var X,Y;
  508. X=ev.x-t.left+This.$workArea[0].parentNode.scrollLeft;
  509. Y=ev.y-t.top+This.$workArea[0].parentNode.scrollTop;
  510. if(cursor!="move"){
  511. This.$ghost.css({display:"block",
  512. width:This.$areaData[id].width-2+"px", height:This.$areaData[id].height-2+"px",
  513. top:This.$areaData[id].top+t.top-This.$workArea[0].parentNode.scrollTop+hack+"px",
  514. left:This.$areaData[id].left+t.left-This.$workArea[0].parentNode.scrollLeft+hack+"px",cursor:cursor});
  515. var vX=(This.$areaData[id].left+This.$areaData[id].width)-X;
  516. var vY=(This.$areaData[id].top+This.$areaData[id].height)-Y;
  517. }
  518. else{
  519. var vX=X-This.$areaData[id].left;
  520. var vY=Y-This.$areaData[id].top;
  521. }
  522. var isMove=false;
  523. This.$ghost.css("cursor",cursor);
  524. document.onmousemove=function(e){
  525. if(!e)e=window.event;
  526. var ev=mousePosition(e);
  527. if(cursor!="move"){
  528. X=ev.x-t.left+This.$workArea[0].parentNode.scrollLeft-This.$areaData[id].left+vX;
  529. Y=ev.y-t.top+This.$workArea[0].parentNode.scrollTop-This.$areaData[id].top+vY;
  530. if(X<200) X=200;
  531. if(Y<100) Y=100;
  532. switch(cursor){
  533. case "nw-resize":This.$ghost.css({width:X-2+"px",height:Y-2+"px"});break;
  534. case "w-resize":This.$ghost.css({width:X-2+"px"});break;
  535. case "n-resize":This.$ghost.css({height:Y-2+"px"});break;
  536. }
  537. }
  538. else{
  539. if(This.$ghost.css("display")=="none"){
  540. This.$ghost.css({display:"block",
  541. width:This.$areaData[id].width-2+"px", height:This.$areaData[id].height-2+"px",
  542. top:This.$areaData[id].top+t.top-This.$workArea[0].parentNode.scrollTop+hack+"px",
  543. left:This.$areaData[id].left+t.left-This.$workArea[0].parentNode.scrollLeft+hack+"px",cursor:cursor});
  544. }
  545. X=ev.x-vX;Y=ev.y-vY;
  546. if(X<t.left-This.$workArea[0].parentNode.scrollLeft)
  547. X=t.left-This.$workArea[0].parentNode.scrollLeft;
  548. else if(X+This.$workArea[0].parentNode.scrollLeft+This.$areaData[id].width>t.left+This.$workArea.width())
  549. X=t.left+This.$workArea.width()-This.$workArea[0].parentNode.scrollLeft-This.$areaData[id].width;
  550. if(Y<t.top-This.$workArea[0].parentNode.scrollTop)
  551. Y=t.top-This.$workArea[0].parentNode.scrollTop;
  552. else if(Y+This.$workArea[0].parentNode.scrollTop+This.$areaData[id].height>t.top+This.$workArea.height())
  553. Y=t.top+This.$workArea.height()-This.$workArea[0].parentNode.scrollTop-This.$areaData[id].height;
  554. This.$ghost.css({left:X+hack+"px",top:Y+hack+"px"});
  555. }
  556. isMove=true;
  557. }
  558. document.onmouseup=function(e){
  559. This.$ghost.empty().hide();
  560. document.onmousemove=null;
  561. document.onmouseup=null;
  562. if(!isMove)return;
  563. if(cursor!="move")
  564. This.resizeArea(id,This.$ghost.outerWidth(),This.$ghost.outerHeight());
  565. else
  566. This.moveArea(id,X+This.$workArea[0].parentNode.scrollLeft-t.left,Y+This.$workArea[0].parentNode.scrollTop-t.top);
  567. return false;
  568. }
  569. });
  570. //绑定修改文字说明功能
  571. this.$group.on("dblclick",{inthis:this},function(e){
  572. var This=e.data.inthis;
  573. if(This.$nowType!="group") return;
  574. if(!e)e=window.event;
  575. if(e.target.tagName!="LABEL") return false;
  576. var oldTxt=e.target.innerHTML;
  577. var p=e.target.parentNode;
  578. var x=parseInt(p.style.left,10)+18,y=parseInt(p.style.top,10)+1;
  579. var t=getElCoordinate(This.$workArea[0]);
  580. This.$textArea.val(oldTxt).css({display:"block",width:100,height:14,
  581. left:t.left+x-This.$workArea[0].parentNode.scrollLeft,
  582. top:t.top+y-This.$workArea[0].parentNode.scrollTop}).data("id",p.id).focus();
  583. This.$workArea.parent().one("mousedown",function(e){
  584. if(e.button==2)return false;
  585. if(This.$textArea.css("display")=="block"){
  586. This.setName(This.$textArea.data("id"),This.$textArea.val(),"area");
  587. This.$textArea.val("").removeData("id").hide();
  588. }
  589. });
  590. return false;
  591. });
  592. //绑定点击事件
  593. this.$group.mouseup({inthis:this},function(e){
  594. var This=e.data.inthis;
  595. if(This.$nowType!="group") return;
  596. if(!e)e=window.event;
  597. switch($(e.target).attr("class")){
  598. case "rs_close": This.delArea(e.target.parentNode.parentNode.id);return false;//删除该分组区域
  599. case "bg": return;
  600. }
  601. switch(e.target.tagName){
  602. case "LABEL": return false;
  603. case "I"://绑定变色功能
  604. var id=e.target.parentNode.id;
  605. switch(This.$areaData[id].color){
  606. case "red": This.setAreaColor(id,"yellow");break;
  607. case "yellow": This.setAreaColor(id,"blue");break;
  608. case "blue": This.setAreaColor(id,"green");break;
  609. case "green": This.setAreaColor(id,"red");break;
  610. }
  611. return false;
  612. }
  613. if(e.data.inthis.$ghost.css("display")=="none"){
  614. var X,Y;
  615. var ev=mousePosition(e),t=getElCoordinate(this);
  616. X=ev.x-t.left+this.parentNode.parentNode.scrollLeft-1;
  617. Y=ev.y-t.top+this.parentNode.parentNode.scrollTop-1;
  618. var color=["red","yellow","blue","green"];
  619. 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});
  620. e.data.inthis.$max++;
  621. return false;
  622. }
  623. });
  624. },
  625. //初始化用来改变连线的连接端点的两个小方块的操作事件
  626. initLinePointsChg:function(){
  627. this.$mpFrom.on("mousedown",{inthis:this},function(e){
  628. var This=e.data.inthis;
  629. This.switchToolBtn("cursor");
  630. var ps=This.$mpFrom.data("p").split(",");
  631. var pe=This.$mpTo.data("p").split(",");
  632. $(this).hide();
  633. This.$workArea.data("lineEnd",{"x":pe[0],"y":pe[1],"id":This.$lineData[This.$lineOper.data("tid")].to}).css("cursor","crosshair");
  634. var line=GooFlow.prototype.drawLine("GooFlow_tmp_line",[ps[0],ps[1]],[pe[0],pe[1]],true,true);
  635. This.$draw.appendChild(line);
  636. return false;
  637. });
  638. this.$mpTo.on("mousedown",{inthis:this},function(e){
  639. var This=e.data.inthis;
  640. This.switchToolBtn("cursor");
  641. var ps=This.$mpFrom.data("p").split(",");
  642. var pe=This.$mpTo.data("p").split(",");
  643. $(this).hide();
  644. This.$workArea.data("lineStart",{"x":ps[0],"y":ps[1],"id":This.$lineData[This.$lineOper.data("tid")].from}).css("cursor","crosshair");
  645. var line=GooFlow.prototype.drawLine("GooFlow_tmp_line",[ps[0],ps[1]],[pe[0],pe[1]],true,true);
  646. This.$draw.appendChild(line);
  647. return false;
  648. });
  649. },
  650. //每一种类型结点及其按钮的说明文字
  651. setNodeRemarks:function(remark){
  652. if(this.$tool==null) return;
  653. this.$tool.children("a").each(function(){
  654. this.title=remark[$(this).attr("id").split("btn_")[1]];
  655. });
  656. this.$nodeRemark=remark;
  657. },
  658. //切换左边工具栏按钮,传参TYPE表示切换成哪种类型的按钮
  659. switchToolBtn:function(type){
  660. this.$tool.children("#"+this.$id+"_btn_"+this.$nowType.split(" ")[0]).attr("class","GooFlow_tool_btn");
  661. if(this.$nowType=="group"){
  662. this.$workArea.prepend(this.$group);
  663. for(var key in this.$areaDom) this.$areaDom[key].addClass("lock").children("div:eq(1)").css("display","none");
  664. }
  665. this.$nowType=type;
  666. this.$tool.children("#"+this.$id+"_btn_"+type.split(" ")[0]).attr("class","GooFlow_tool_btndown");
  667. if(this.$nowType=="group"){
  668. this.blurItem();
  669. this.$workArea.append(this.$group);
  670. for(var key in this.$areaDom) this.$areaDom[key].removeClass("lock").children("div:eq(1)").css("display","");
  671. }else if(this.$nowType=="direct"){
  672. this.blurItem();
  673. }
  674. if(this.$textArea.css("display")=="none") this.$textArea.removeData("id").val("").hide();
  675. },
  676. //增加一个流程结点,传参为一个JSON,有id,name,top,left,width,height,type(结点类型)等属性
  677. addNode:function(id,json){
  678. if(this.onItemAdd!=null&&!this.onItemAdd(id,"node",json))return;
  679. if(this.$undoStack&&this.$editable){
  680. this.pushOper("delNode",[id]);
  681. }
  682. var mark=json.marked? " item_mark":"";
  683. if(json.type.indexOf(" round")<0){
  684. if(!json.width||json.width<100)json.width=100;
  685. if(!json.height||json.height<24)json.height=24;
  686. if(!json.top||json.top<0)json.top=0;
  687. if(!json.left||json.left<0)json.left=0;
  688. var hack=0;
  689. if(navigator.userAgent.indexOf("8.0")!=-1) hack=2;
  690. this.$nodeDom[id]=$("<div class='GooFlow_item"+mark+"' id='"+id+"' style='top:"+json.top+"px;left:"+json.left+"px'><table cellspacing='1' style='width:"+(json.width-2)+"px;height:"+(json.height-2)+"px;'><tr><td class='ico'><i class='ico_"+json.type+"'></i></td><td>"+json.name+"</td></tr></table><div style='display:none'><div class='rs_bottom'></div><div class='rs_right'></div><div class='rs_rb'></div><div class='rs_close'></div></div></div>");
  691. }
  692. else{
  693. json.width=24;json.height=24;
  694. this.$nodeDom[id]=$("<div class='GooFlow_item item_round"+mark+"' id='"+id+"' style='top:"+json.top+"px;left:"+json.left+"px'><table cellspacing='0'><tr><td class='ico'><i class='ico_"+json.type+"'></i></td></tr></table><div style='display:none'><div class='rs_close'></div></div><div class='span'>"+json.name+"</div></div>");
  695. }
  696. if(GooFlow.prototype.color.node){
  697. if(json.type.indexOf(" mix")>-1){
  698. this.$nodeDom[id].css({"background-color":GooFlow.prototype.color.mix,"border-color":GooFlow.prototype.color.mix});
  699. }else{
  700. this.$nodeDom[id].css({"background-color":GooFlow.prototype.color.node,"border-color":GooFlow.prototype.color.node});
  701. }
  702. if(mark&&GooFlow.prototype.color.mark){
  703. this.$nodeDom[id].css({"border-color":GooFlow.prototype.color.mark});
  704. }
  705. }
  706. if(json.type.indexOf(" mix")>-1){
  707. this.$nodeDom[id].addClass("item_mix");
  708. }
  709. var ua=navigator.userAgent.toLowerCase();
  710. if(ua.indexOf('msie')!=-1 && ua.indexOf('8.0')!=-1)
  711. this.$nodeDom[id].css("filter","progid:DXImageTransform.Microsoft.Shadow(color=#94AAC2,direction=135,strength=2)");
  712. this.$workArea.append(this.$nodeDom[id]);
  713. this.$nodeData[id]=json;
  714. ++this.$nodeCount;
  715. if(this.$editable){
  716. this.$nodeData[id].alt=true;
  717. if(this.$deletedItem[id]) delete this.$deletedItem[id];//在回退删除操作时,去掉该元素的删除记录
  718. }
  719. },
  720. initWorkForNode:function(){
  721. //绑定点击事件
  722. this.$workArea.delegate(".GooFlow_item","click",{inthis:this},function(e){
  723. e.data.inthis.focusItem(this.id,true);
  724. $(this).removeClass("item_mark");
  725. });
  726. //绑定用鼠标移动事件
  727. this.$workArea.delegate(".ico","mousedown",{inthis:this},function(e){
  728. if(!e)e=window.event;
  729. if(e.button==2)return false;
  730. var This=e.data.inthis;
  731. if(This.$nowType=="direct") return;
  732. var Dom=$(this).parents(".GooFlow_item");
  733. var id=Dom.attr("id");
  734. This.focusItem(id,true);
  735. var hack=1;
  736. if(navigator.userAgent.indexOf("8.0")!=-1) hack=0;
  737. var ev=mousePosition(e),t=getElCoordinate(This.$workArea[0]);
  738. Dom.children("table").clone().prependTo(This.$ghost);
  739. var X,Y;
  740. X=ev.x-t.left+This.$workArea[0].parentNode.scrollLeft;
  741. Y=ev.y-t.top+This.$workArea[0].parentNode.scrollTop;
  742. var vX=X-This.$nodeData[id].left,vY=Y-This.$nodeData[id].top;
  743. var isMove=false;
  744. document.onmousemove=function(e){
  745. if(!e)e=window.event;
  746. var ev=mousePosition(e);
  747. if(X==ev.x-vX&&Y==ev.y-vY) return false;
  748. X=ev.x-vX;Y=ev.y-vY;
  749. if(isMove&&This.$ghost.css("display")=="none"){
  750. This.$ghost.css({display:"block",
  751. width:This.$nodeData[id].width-2+"px", height:This.$nodeData[id].height-2+"px",
  752. top:This.$nodeData[id].top+t.top-This.$workArea[0].parentNode.scrollTop+hack+"px",
  753. left:This.$nodeData[id].left+t.left-This.$workArea[0].parentNode.scrollLeft+hack+"px",cursor:"move"
  754. });
  755. }
  756. if(X<t.left-This.$workArea[0].parentNode.scrollLeft)
  757. X=t.left-This.$workArea[0].parentNode.scrollLeft;
  758. else if(X+This.$workArea[0].parentNode.scrollLeft+This.$nodeData[id].width>t.left+This.$workArea.width())
  759. X=t.left+This.$workArea.width()-This.$workArea[0].parentNode.scrollLeft-This.$nodeData[id].width;
  760. if(Y<t.top-This.$workArea[0].parentNode.scrollTop)
  761. Y=t.top-This.$workArea[0].parentNode.scrollTop;
  762. else if(Y+This.$workArea[0].parentNode.scrollTop+This.$nodeData[id].height>t.top+This.$workArea.height())
  763. Y=t.top+This.$workArea.height()-This.$workArea[0].parentNode.scrollTop-This.$nodeData[id].height;
  764. This.$ghost.css({left:X+hack+"px",top:Y+hack+"px"});
  765. isMove=true;
  766. }
  767. document.onmouseup=function(e){
  768. if(isMove)This.moveNode(id,X+This.$workArea[0].parentNode.scrollLeft-t.left,Y+This.$workArea[0].parentNode.scrollTop-t.top);
  769. This.$ghost.empty().hide();
  770. document.onmousemove=null;
  771. document.onmouseup=null;
  772. }
  773. });
  774. if(!this.$editable) return;
  775. //绑定鼠标覆盖/移出事件
  776. this.$workArea.delegate(".GooFlow_item","mouseenter",{inthis:this},function(e){
  777. if(e.data.inthis.$nowType!="direct"&&!document.getElementById("GooFlow_tmp_line")) return;
  778. $(this).addClass("item_mark").addClass("crosshair").css("border-color",GooFlow.prototype.color.mark||"#ff3300");
  779. });
  780. this.$workArea.delegate(".GooFlow_item","mouseleave",{inthis:this},function(e){
  781. if(e.data.inthis.$nowType!="direct"&&!document.getElementById("GooFlow_tmp_line")) return;
  782. $(this).removeClass("item_mark").removeClass("crosshair");
  783. if(this.id==e.data.inthis.$focus){
  784. $(this).css("border-color",GooFlow.prototype.color.line||"#3892D3");
  785. }else{
  786. $(this).css("border-color",GooFlow.prototype.color.node||"#A1DCEB");
  787. }
  788. });
  789. //绑定连线时确定初始点
  790. this.$workArea.delegate(".GooFlow_item","mousedown",{inthis:this},function(e){
  791. if(e.button==2)return false;
  792. var This=e.data.inthis;
  793. if(This.$nowType!="direct") return;
  794. var ev=mousePosition(e),t=getElCoordinate(This.$workArea[0]);
  795. var X,Y;
  796. X=ev.x-t.left+This.$workArea[0].parentNode.scrollLeft;
  797. Y=ev.y-t.top+This.$workArea[0].parentNode.scrollTop;
  798. This.$workArea.data("lineStart",{"x":X,"y":Y,"id":this.id}).css("cursor","crosshair");
  799. var line=GooFlow.prototype.drawLine("GooFlow_tmp_line",[X,Y],[X,Y],true,true);
  800. This.$draw.appendChild(line);
  801. });
  802. //绑定连线时确定结束点
  803. this.$workArea.delegate(".GooFlow_item","mouseup",{inthis:this},function(e){
  804. var This=e.data.inthis;
  805. if(This.$nowType!="direct"&&!This.$mpTo.data("p")) return;
  806. var lineStart=This.$workArea.data("lineStart");
  807. var lineEnd=This.$workArea.data("lineEnd");
  808. if(lineStart&&!This.$mpTo.data("p")){
  809. This.addLine(This.$id+"_line_"+This.$max,{from:lineStart.id,to:this.id,name:""});
  810. This.$max++;
  811. }
  812. else{
  813. if(lineStart){
  814. This.moveLinePoints(This.$focus,lineStart.id,this.id);
  815. }else if(lineEnd){
  816. This.moveLinePoints(This.$focus,this.id,lineEnd.id);
  817. }
  818. if(!This.$nodeData[this.id].marked){
  819. $(this).removeClass("item_mark");
  820. if(this.id!=This.$focus){
  821. $(this).css("border-color",GooFlow.prototype.color.node);
  822. }
  823. else{
  824. $(this).css("border-color",GooFlow.prototype.color.line);
  825. }
  826. }
  827. }
  828. });
  829. //绑定双击编辑事件
  830. this.$workArea.delegate(".GooFlow_item > .span","dblclick",{inthis:this},function(e){
  831. var oldTxt=this.innerHTML;
  832. var This=e.data.inthis;
  833. var id=this.parentNode.id;
  834. var t=getElCoordinate(This.$workArea[0]);
  835. This.$textArea.val(oldTxt).css({display:"block",height:$(this).height(),width:100,
  836. left:t.left+This.$nodeData[id].left-This.$workArea[0].parentNode.scrollLeft-24,
  837. top:t.top+This.$nodeData[id].top-This.$workArea[0].parentNode.scrollTop+26})
  838. .data("id",This.$focus).focus();
  839. This.$workArea.parent().one("mousedown",function(e){
  840. if(e.button==2)return false;
  841. This.setName(This.$textArea.data("id"),This.$textArea.val(),"node");
  842. This.$textArea.val("").removeData("id").hide();
  843. });
  844. });
  845. this.$workArea.delegate(".ico + td","dblclick",{inthis:this},function(e){
  846. var oldTxt=this.innerHTML;
  847. var This=e.data.inthis;
  848. var id=$(this).parents(".GooFlow_item").attr("id");
  849. var t=getElCoordinate(This.$workArea[0]);
  850. This.$textArea.val(oldTxt).css({display:"block",width:$(this).width()+24,height:$(this).height(),
  851. left:t.left+24+This.$nodeData[id].left-This.$workArea[0].parentNode.scrollLeft,
  852. top:t.top+2+This.$nodeData[id].top-This.$workArea[0].parentNode.scrollTop})
  853. .data("id",This.$focus).focus();
  854. This.$workArea.parent().one("mousedown",function(e){
  855. if(e.button==2)return false;
  856. This.setName(This.$textArea.data("id"),This.$textArea.val(),"node");
  857. This.$textArea.val("").removeData("id").hide();
  858. });
  859. });
  860. //绑定结点的删除功能
  861. this.$workArea.delegate(".rs_close","click",{inthis:this},function(e){
  862. if(!e)e=window.event;
  863. e.data.inthis.delNode(e.data.inthis.$focus);
  864. return false;
  865. });
  866. //绑定结点的RESIZE功能
  867. this.$workArea.delegate(".GooFlow_item > div > div[class!=rs_close]","mousedown",{inthis:this},function(e){
  868. if(!e)e=window.event;
  869. if(e.button==2)return false;
  870. var cursor=$(this).css("cursor");
  871. if(cursor=="pointer"){return;}
  872. var This=e.data.inthis;
  873. var id=This.$focus;
  874. This.switchToolBtn("cursor");
  875. e.cancelBubble = true;
  876. e.stopPropagation();
  877. var hack=1;
  878. if(navigator.userAgent.indexOf("8.0")!=-1) hack=0;
  879. var ev=mousePosition(e),t=getElCoordinate(This.$workArea[0]);
  880. This.$ghost.css({display:"block",
  881. width:This.$nodeData[id].width-2+"px", height:This.$nodeData[id].height-2+"px",
  882. top:This.$nodeData[id].top+t.top-This.$workArea[0].parentNode.scrollTop+hack+"px",
  883. left:This.$nodeData[id].left+t.left-This.$workArea[0].parentNode.scrollLeft+hack+"px",cursor:cursor
  884. });
  885. var X,Y;
  886. X=ev.x-t.left+This.$workArea[0].parentNode.scrollLeft;
  887. Y=ev.y-t.top+This.$workArea[0].parentNode.scrollTop;
  888. var vX=(This.$nodeData[id].left+This.$nodeData[id].width)-X;
  889. var vY=(This.$nodeData[id].top+This.$nodeData[id].height)-Y;
  890. var isMove=false;
  891. This.$ghost.css("cursor",cursor);
  892. document.onmousemove=function(e){
  893. if(!e)e=window.event;
  894. var ev=mousePosition(e);
  895. X=ev.x-t.left+This.$workArea[0].parentNode.scrollLeft-This.$nodeData[id].left+vX;
  896. Y=ev.y-t.top+This.$workArea[0].parentNode.scrollTop-This.$nodeData[id].top+vY;
  897. if(X<100) X=100;
  898. if(Y<24) Y=24;
  899. isMove=true;
  900. switch(cursor){
  901. case "nw-resize":This.$ghost.css({width:X-2+"px",height:Y-2+"px"});break;
  902. case "w-resize":This.$ghost.css({width:X-2+"px"});break;
  903. case "n-resize":This.$ghost.css({height:Y-2+"px"});break;
  904. }
  905. }
  906. document.onmouseup=function(e){
  907. This.$ghost.hide();
  908. if(!isMove)return;
  909. if(!e)e=window.event;
  910. This.resizeNode(id,This.$ghost.outerWidth(),This.$ghost.outerHeight());
  911. document.onmousemove=null;
  912. document.onmouseup=null;
  913. }
  914. });
  915. },
  916. //获取结点/连线/分组区域的详细信息
  917. getItemInfo:function(id,type){
  918. switch(type){
  919. case "node": return this.$nodeData[id]||null;
  920. case "line": return this.$lineData[id]||null;
  921. case "area": return this.$areaData[id]||null;
  922. }
  923. },
  924. //取消所有结点/连线被选定的状态
  925. blurItem:function(){
  926. if(this.$focus!=""){
  927. var jq=$("#"+this.$focus);
  928. if(jq.prop("tagName")=="DIV"){
  929. if(this.onItemBlur!=null&&!this.onItemBlur(this.$focus,"node")) return false;
  930. jq.removeClass("item_focus").children("div:eq(0)").css("display","none");
  931. if(GooFlow.prototype.color.line){
  932. if(this.$nodeData[this.$focus].marked){
  933. jq.css("border-color",GooFlow.prototype.color.mark||"#ff3300");
  934. }
  935. else{
  936. jq.css("border-color",GooFlow.prototype.color.node||"#A1DCEB");
  937. }
  938. }
  939. }
  940. else{
  941. if(this.onItemBlur!=null&&!this.onItemBlur(this.$focus,"line")) return false;
  942. if(GooFlow.prototype.useSVG!=""){
  943. if(!this.$lineData[this.$focus].marked){
  944. jq[0].childNodes[1].setAttribute("stroke",GooFlow.prototype.color.line||"#3892D3");
  945. jq[0].childNodes[1].setAttribute("marker-end","url(#arrow1)");
  946. }
  947. }
  948. else{
  949. if(!this.$lineData[this.$focus].marked) jq[0].strokeColor=GooFlow.prototype.color.line||"#3892D3";
  950. }
  951. this.$lineMove.hide().removeData("type").removeData("tid");
  952. if(this.$editable){
  953. this.$lineOper.hide().removeData("tid");
  954. this.$mpFrom.hide().removeData("p");
  955. this.$mpTo.hide().removeData("p");
  956. }
  957. }
  958. }
  959. this.$focus="";
  960. return true;
  961. },
  962. //选定某个结点/转换线 bool:TRUE决定了要触发选中事件,FALSE则不触发选中事件,多用在程序内部调用。
  963. focusItem:function(id,bool){
  964. var jq=$("#"+id);
  965. if(jq.length==0) return;
  966. if(!this.blurItem()) return;//先执行"取消选中",如果返回FLASE,则也会阻止选定事件继续进行.
  967. if(jq.prop("tagName")=="DIV"){
  968. if(bool&&this.onItemFocus!=null&&!this.onItemFocus(id,"node")) return;
  969. jq.addClass("item_focus");
  970. if(GooFlow.prototype.color.line){
  971. jq.css("border-color",GooFlow.prototype.color.line);
  972. }
  973. if(this.$editable)jq.children("div:eq(0)").css("display","block");
  974. this.$workArea.append(jq);
  975. }
  976. else{//如果是连接线
  977. if(this.onItemFocus!=null&&!this.onItemFocus(id,"line")) return;
  978. if(GooFlow.prototype.useSVG!=""){
  979. jq[0].childNodes[1].setAttribute("stroke",GooFlow.prototype.color.mark||"#ff3300");
  980. jq[0].childNodes[1].setAttribute("marker-end","url(#arrow2)");
  981. }
  982. else jq[0].strokeColor=GooFlow.prototype.color.mark||"#ff3300";
  983. if(!this.$editable) return;
  984. var x,y,from,to,n;
  985. if(GooFlow.prototype.useSVG!=""){
  986. from=jq.attr("from").split(",");
  987. to=jq.attr("to").split(",");
  988. n=[from[0],from[1],to[0],to[1]];
  989. }else{
  990. n=jq[0].getAttribute("fromTo").split(",");
  991. from=[n[0],n[1]];
  992. to=[n[2],n[3]];
  993. }
  994. from[0]=parseInt(from[0],10);
  995. from[1]=parseInt(from[1],10);
  996. to[0]=parseInt(to[0],10);
  997. to[1]=parseInt(to[1],10);
  998. //var t=getElCoordinate(this.$workArea[0]);
  999. if(this.$lineData[id].type=="lr"){
  1000. from[0]=this.$lineData[id].M;
  1001. to[0]=from[0];
  1002. this.$lineMove.css({
  1003. width:"5px",height:(to[1]-from[1])*(to[1]>from[1]? 1:-1)+"px",
  1004. left:from[0]-3+"px",
  1005. top:(to[1]>from[1]? from[1]:to[1])+1+"px",
  1006. cursor:"e-resize",display:"block"
  1007. }).data({"type":"lr","tid":id});
  1008. }
  1009. else if(this.$lineData[id].type=="tb"){
  1010. from[1]=this.$lineData[id].M;
  1011. to[1]=from[1];
  1012. this.$lineMove.css({
  1013. width:(to[0]-from[0])*(to[0]>from[0]? 1:-1)+"px",height:"5px",
  1014. left:(to[0]>from[0]? from[0]:to[0])+1+"px",
  1015. top:from[1]-3+"px",
  1016. cursor:"s-resize",display:"block"
  1017. }).data({"type":"tb","tid":id});
  1018. }
  1019. x=(from[0]+to[0])/2-35;
  1020. y=(from[1]+to[1])/2+6;
  1021. this.$lineOper.css({display:"block",left:x+"px",top:y+"px"}).data("tid",id);
  1022. if(this.$editable){
  1023. this.$mpFrom.css({display:"block",left:n[0]-4+"px",top:n[1]-4+"px"}).data("p",n[0]+","+n[1]);
  1024. this.$mpTo.css({display:"block",left:n[2]-4+"px",top:n[3]-4+"px"}).data("p",n[2]+","+n[3]);
  1025. }
  1026. this.$draw.appendChild(jq[0]);
  1027. }
  1028. this.$focus=id;
  1029. this.switchToolBtn("cursor");
  1030. },
  1031. //移动结点到一个新的位置
  1032. moveNode:function(id,left,top){
  1033. if(!this.$nodeData[id]) return;
  1034. if(this.onItemMove!=null&&!this.onItemMove(id,"node",left,top)) return;
  1035. if(this.$undoStack){
  1036. var paras=[id,this.$nodeData[id].left,this.$nodeData[id].top];
  1037. this.pushOper("moveNode",paras);
  1038. }
  1039. if(left<0) left=0;
  1040. if(top<0) top=0;
  1041. $("#"+id).css({left:left+"px",top:top+"px"});
  1042. this.$nodeData[id].left=left;
  1043. this.$nodeData[id].top=top;
  1044. //重画转换线
  1045. this.resetLines(id,this.$nodeData[id]);
  1046. if(this.$editable){
  1047. this.$nodeData[id].alt=true;
  1048. }
  1049. },
  1050. //设置结点/连线/分组区域的文字信息
  1051. setName:function(id,name,type){
  1052. var oldName;
  1053. if(type=="node"){//如果是结点
  1054. if(!this.$nodeData[id]) return;
  1055. if(this.$nodeData[id].name==name) return;
  1056. if(this.onItemRename!=null&&!this.onItemRename(id,name,"node")) return;
  1057. oldName=this.$nodeData[id].name;
  1058. this.$nodeData[id].name=name;
  1059. if(this.$nodeData[id].type.indexOf("round")>1){
  1060. this.$nodeDom[id].children(".span").text(name);
  1061. }
  1062. else{
  1063. this.$nodeDom[id].find("td:eq(1)").text(name);
  1064. var hack=0;
  1065. if(navigator.userAgent.indexOf("8.0")!=-1) hack=2;
  1066. var width=this.$nodeDom[id].outerWidth();
  1067. var height=this.$nodeDom[id].outerHeight();
  1068. this.$nodeDom[id].children("table").css({width:width-2+"px",height:height-2+"px"});
  1069. this.$nodeData[id].width=width;
  1070. this.$nodeData[id].height=height;
  1071. }
  1072. if(this.$editable){
  1073. this.$nodeData[id].alt=true;
  1074. }
  1075. //重画转换线
  1076. this.resetLines(id,this.$nodeData[id]);
  1077. }
  1078. else if(type=="line"){//如果是线
  1079. if(!this.$lineData[id]) return;
  1080. if(this.$lineData[id].name==name) return;
  1081. if(this.onItemRename!=null&&!this.onItemRename(id,name,"line")) return;
  1082. oldName=this.$lineData[id].name;
  1083. this.$lineData[id].name=name;
  1084. if(GooFlow.prototype.useSVG!=""){
  1085. this.$lineDom[id].childNodes[2].textContent=name;
  1086. }
  1087. else{
  1088. this.$lineDom[id].childNodes[1].innerHTML=name;
  1089. var n=this.$lineDom[id].getAttribute("fromTo").split(",");
  1090. var x;
  1091. if(this.$lineData[id].type!="lr"){
  1092. x=(n[2]-n[0])/2;
  1093. }
  1094. else{
  1095. var Min=n[2]>n[0]? n[0]:n[2];
  1096. if(Min>this.$lineData[id].M) Min=this.$lineData[id].M;
  1097. x=this.$lineData[id].M-Min;
  1098. }
  1099. if(x<0) x=x*-1;
  1100. this.$lineDom[id].childNodes[1].style.left=x-this.$lineDom[id].childNodes[1].offsetWidth/2+4+"px";
  1101. }
  1102. if(this.$editable){
  1103. this.$lineData[id].alt=true;
  1104. }
  1105. }
  1106. else if(type=="area"){//如果是分组区域
  1107. if(!this.$areaData[id]) return;
  1108. if(this.$areaData[id].name==name) return;
  1109. if(this.onItemRename!=null&&!this.onItemRename(id,name,"area")) return;
  1110. oldName=this.$areaData[id].name;
  1111. this.$areaData[id].name=name;
  1112. this.$areaDom[id].children("label").text(name);
  1113. if(this.$editable){
  1114. this.$areaData[id].alt=true;
  1115. }
  1116. }
  1117. if(this.$undoStack){
  1118. var paras=[id,oldName,type];
  1119. this.pushOper("setName",paras);
  1120. }
  1121. },
  1122. //设置结点的尺寸,仅支持非开始/结束结点
  1123. resizeNode:function(id,width,height){
  1124. if(!this.$nodeData[id]) return;
  1125. if(this.onItemResize!=null&&!this.onItemResize(id,"node",width,height)) return;
  1126. if(this.$nodeData[id].type=="start"||this.$nodeData[id].type=="end")return;
  1127. if(this.$undoStack){
  1128. var paras=[id,this.$nodeData[id].width,this.$nodeData[id].height];
  1129. this.pushOper("resizeNode",paras);
  1130. }
  1131. var hack=0;
  1132. if(navigator.userAgent.indexOf("8.0")!=-1) hack=2;
  1133. this.$nodeDom[id].children("table").css({width:width-2+"px",height:height-2+"px"});
  1134. width=this.$nodeDom[id].outerWidth()-hack;
  1135. height=this.$nodeDom[id].outerHeight()-hack;
  1136. this.$nodeDom[id].children("table").css({width:width-2+"px",height:height-2+"px"});
  1137. this.$nodeData[id].width=width;
  1138. this.$nodeData[id].height=height;
  1139. if(this.$editable){
  1140. this.$nodeData[id].alt=true;
  1141. }
  1142. //重画转换线
  1143. this.resetLines(id,this.$nodeData[id]);
  1144. },
  1145. //删除结点
  1146. delNode:function(id){
  1147. if(!this.$nodeData[id]) return;
  1148. if(this.onItemDel!=null&&!this.onItemDel(id,"node")) return;
  1149. //先删除可能的连线
  1150. for(var k in this.$lineData){
  1151. if(this.$lineData[k].from==id||this.$lineData[k].to==id){
  1152. //this.$draw.removeChild(this.$lineDom[k]);
  1153. //delete this.$lineData[k];
  1154. //delete this.$lineDom[k];
  1155. this.delLine(k);
  1156. }
  1157. }
  1158. //再删除结点本身
  1159. if(this.$undoStack){
  1160. var paras=[id,this.$nodeData[id]];
  1161. this.pushOper("addNode",paras);
  1162. }
  1163. delete this.$nodeData[id];
  1164. this.$nodeDom[id].remove();
  1165. delete this.$nodeDom[id];
  1166. --this.$nodeCount;
  1167. if(this.$focus==id) this.$focus="";
  1168. if(this.$editable){
  1169. //在回退新增操作时,如果节点ID以this.$id+"_node_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中
  1170. if(id.indexOf(this.$id+"_node_")<0)
  1171. this.$deletedItem[id]="node";
  1172. }
  1173. },
  1174. //设置流程图的名称
  1175. setTitle:function(text){
  1176. this.$title=text;
  1177. if(this.$head) this.$head.children("label").attr("title",text).text(text);
  1178. },
  1179. //载入一组数据
  1180. loadData:function(data){
  1181. var t=this.$editable;
  1182. this.$editable=false;
  1183. if(data.title) this.setTitle(data.title);
  1184. if(data.initNum) this.$max=data.initNum;
  1185. for(var i in data.nodes)
  1186. this.addNode(i,data.nodes[i]);
  1187. for(var j in data.lines)
  1188. this.addLine(j,data.lines[j]);
  1189. for(var k in data.areas)
  1190. this.addArea(k,data.areas[k]);
  1191. this.$editable=t;
  1192. this.$deletedItem={};
  1193. },
  1194. //用AJAX方式,远程读取一组数据
  1195. //参数para为JSON结构,与JQUERY中$.ajax()方法的传参一样
  1196. loadDataAjax:function(para){
  1197. var This=this;
  1198. $.ajax({
  1199. type:para.type,
  1200. url:para.url,
  1201. dataType:"json",
  1202. data:para.data,
  1203. success: function(msg){
  1204. if(para.dataFilter) para.dataFilter(msg,"json");
  1205. This.loadData(msg);
  1206. if(para.success) para.success(msg);
  1207. },
  1208. error: function(XMLHttpRequest, textStatus, errorThrown){
  1209. if(para.error) para.error(textStatus,errorThrown);
  1210. }
  1211. })
  1212. },
  1213. //把画好的整个流程图导出到一个变量中(其实也可以直接访问GooFlow对象的$nodeData,$lineData,$areaData这三个JSON属性)
  1214. exportData:function(){
  1215. var ret={title:this.$title,nodes:this.$nodeData,lines:this.$lineData,areas:this.$areaData,initNum:this.$max};
  1216. for(var k1 in ret.nodes){
  1217. if(!ret.nodes[k1].marked){
  1218. delete ret.nodes[k1]["marked"];
  1219. }
  1220. }
  1221. for(var k2 in ret.lines){
  1222. if(!ret.lines[k2].marked){
  1223. delete ret.lines[k2]["marked"];
  1224. }
  1225. }
  1226. return ret;
  1227. },
  1228. //只把本次编辑流程图中作了变更(包括增删改)的元素导出到一个变量中,以方便用户每次编辑载入的流程图后只获取变更过的数据
  1229. exportAlter:function(){
  1230. var ret={nodes:{},lines:{},areas:{}};
  1231. for(var k1 in this.$nodeData){
  1232. if(this.$nodeData[k1].alt){
  1233. ret.nodes[k1]=this.$nodeData[k1];
  1234. }
  1235. }
  1236. for(var k2 in this.$lineData){
  1237. if(this.$lineData[k2].alt){
  1238. ret.lines[k2]=this.$lineData[k2];
  1239. }
  1240. }
  1241. for(var k3 in this.$areaData){
  1242. if(this.$areaData[k3].alt){
  1243. ret.areas[k3]=this.$areaData[k3];
  1244. }
  1245. }
  1246. ret.deletedItem=this.$deletedItem;
  1247. return ret;
  1248. },
  1249. //变更元素的ID,一般用于快速保存后,将后台返回新元素的ID更新到页面中;type为元素类型(节点,连线,区块)
  1250. transNewId:function(oldId,newId,type){
  1251. var tmp;
  1252. switch(type){
  1253. case "node":
  1254. if(this.$nodeData[oldId]){
  1255. tmp=this.$nodeData[oldId];
  1256. delete this.$nodeData[oldId];
  1257. this.$nodeData[newId]=tmp;
  1258. tmp=this.$nodeDom[oldId].attr("id",newId);
  1259. delete this.$nodeDom[oldId];
  1260. this.$nodeDom[newId]=tmp;
  1261. }
  1262. break;
  1263. case "line":
  1264. if(this.$lineData[oldId]){
  1265. tmp=this.$lineData[oldId];
  1266. delete this.$lineData[oldId];
  1267. this.$lineData[newId]=tmp;
  1268. tmp=this.$lineDom[oldId].attr("id",newId);
  1269. delete this.$lineDom[oldId];
  1270. this.$lineDom[newId]=tmp;
  1271. }
  1272. break;
  1273. case "area":
  1274. if(this.$areaData[oldId]){
  1275. tmp=this.$areaData[oldId];
  1276. delete this.$areaData[oldId];
  1277. this.$areaData[newId]=tmp;
  1278. tmp=this.$areaDom[oldId].attr("id",newId);
  1279. delete this.$areaDom[oldId];
  1280. this.$areaDom[newId]=tmp;
  1281. }
  1282. break;
  1283. }
  1284. },
  1285. //清空工作区及已载入的数据
  1286. clearData:function(){
  1287. for(var key in this.$nodeData){
  1288. this.delNode(key);
  1289. }
  1290. for(var key in this.$lineData){
  1291. this.delLine(key);
  1292. }
  1293. for(var key in this.$areaData){
  1294. this.delArea(key);
  1295. }
  1296. this.$deletedItem={};
  1297. },
  1298. //销毁自己
  1299. destrory:function(){
  1300. this.$bgDiv.empty();
  1301. this.$lineData=null;
  1302. this.$nodeData=null;
  1303. this.$lineDom=null;
  1304. this.$nodeDom=null;
  1305. this.$areaDom=null;
  1306. this.$areaData=null;
  1307. this.$nodeCount=0;
  1308. this.$areaCount=0;
  1309. this.$areaCount=0;
  1310. this.$deletedItem={};
  1311. },
  1312. ///////////以下为有关画线的方法
  1313. //绘制一条箭头线,并返回线的DOM
  1314. drawLine:function(id,sp,ep,mark,dash){
  1315. var line;
  1316. if(GooFlow.prototype.useSVG!=""){
  1317. line=document.createElementNS("http://www.w3.org/2000/svg","g");
  1318. var hi=document.createElementNS("http://www.w3.org/2000/svg","path");
  1319. var path=document.createElementNS("http://www.w3.org/2000/svg","path");
  1320. if(id!="") line.setAttribute("id",id);
  1321. line.setAttribute("from",sp[0]+","+sp[1]);
  1322. line.setAttribute("to",ep[0]+","+ep[1]);
  1323. hi.setAttribute("visibility","hidden");
  1324. hi.setAttribute("stroke-width",9);
  1325. hi.setAttribute("fill","none");
  1326. hi.setAttribute("stroke","white");
  1327. hi.setAttribute("d","M "+sp[0]+" "+sp[1]+" L "+ep[0]+" "+ep[1]);
  1328. hi.setAttribute("pointer-events","stroke");
  1329. path.setAttribute("d","M "+sp[0]+" "+sp[1]+" L "+ep[0]+" "+ep[1]);
  1330. path.setAttribute("stroke-width",1.4);
  1331. path.setAttribute("stroke-linecap","round");
  1332. path.setAttribute("fill","none");
  1333. if(dash) path.setAttribute("style", "stroke-dasharray:6,5");
  1334. if(mark){
  1335. path.setAttribute("stroke",GooFlow.prototype.color.mark||"#ff3300");
  1336. path.setAttribute("marker-end","url(#arrow2)");
  1337. }
  1338. else{
  1339. path.setAttribute("stroke",GooFlow.prototype.color.line||"#3892D3");
  1340. path.setAttribute("marker-end","url(#arrow1)");
  1341. }
  1342. line.appendChild(hi);
  1343. line.appendChild(path);
  1344. line.style.cursor="crosshair";
  1345. if(id!=""&&id!="GooFlow_tmp_line"){
  1346. var text=document.createElementNS("http://www.w3.org/2000/svg","text");
  1347. text.setAttribute("fill",GooFlow.prototype.color.font||"#333");
  1348. line.appendChild(text);
  1349. var x=(ep[0]+sp[0])/2;
  1350. var y=(ep[1]+sp[1])/2;
  1351. text.setAttribute("text-anchor","middle");
  1352. text.setAttribute("x",x);
  1353. text.setAttribute("y",y);
  1354. line.style.cursor="pointer";
  1355. text.style.cursor="text";
  1356. }
  1357. }else{
  1358. line=document.createElement("v:polyline");
  1359. if(id!="") line.id=id;
  1360. //line.style.position="absolute";
  1361. line.points.value=sp[0]+","+sp[1]+" "+ep[0]+","+ep[1];
  1362. line.setAttribute("fromTo",sp[0]+","+sp[1]+","+ep[0]+","+ep[1]);
  1363. line.strokeWeight="1.2";
  1364. line.stroke.EndArrow="Block";
  1365. line.style.cursor="crosshair";
  1366. if(id!=""&&id!="GooFlow_tmp_line"){
  1367. var text=document.createElement("div");
  1368. //text.innerHTML=id;
  1369. line.appendChild(text);
  1370. var x=(ep[0]-sp[0])/2;
  1371. var y=(ep[1]-sp[1])/2;
  1372. if(x<0) x=x*-1;
  1373. if(y<0) y=y*-1;
  1374. text.style.left=x+"px";
  1375. text.style.top=y-6+"px";
  1376. line.style.cursor="pointer";
  1377. }
  1378. if(dash) line.stroke.dashstyle="Dash";
  1379. if(mark) line.strokeColor=GooFlow.prototype.color.mark||"#ff3300";
  1380. else line.strokeColor=GooFlow.prototype.color.line||"#3892D3";
  1381. line.fillColor=GooFlow.prototype.color.line||"#3892D3";
  1382. }
  1383. return line;
  1384. },
  1385. //画一条只有两个中点的折线
  1386. drawPoly:function(id,sp,m1,m2,ep,mark){
  1387. var poly,strPath;
  1388. if(GooFlow.prototype.useSVG!=""){
  1389. poly=document.createElementNS("http://www.w3.org/2000/svg","g");
  1390. var hi=document.createElementNS("http://www.w3.org/2000/svg","path");
  1391. var path=document.createElementNS("http://www.w3.org/2000/svg","path");
  1392. if(id!="") poly.setAttribute("id",id);
  1393. poly.setAttribute("from",sp[0]+","+sp[1]);
  1394. poly.setAttribute("to",ep[0]+","+ep[1]);
  1395. hi.setAttribute("visibility","hidden");
  1396. hi.setAttribute("stroke-width",9);
  1397. hi.setAttribute("fill","none");
  1398. hi.setAttribute("stroke","white");
  1399. strPath="M "+sp[0]+" "+sp[1];
  1400. if(m1[0]!=sp[0]||m1[1]!=sp[1])
  1401. strPath+=" L "+m1[0]+" "+m1[1];
  1402. if(m2[0]!=ep[0]||m2[1]!=ep[1])
  1403. strPath+=" L "+m2[0]+" "+m2[1];
  1404. strPath+=" L "+ep[0]+" "+ep[1];
  1405. hi.setAttribute("d",strPath);
  1406. hi.setAttribute("pointer-events","stroke");
  1407. path.setAttribute("d",strPath);
  1408. path.setAttribute("stroke-width",1.4);
  1409. path.setAttribute("stroke-linecap","round");
  1410. path.setAttribute("fill","none");
  1411. if(mark){
  1412. path.setAttribute("stroke",GooFlow.prototype.color.mark||"#ff3300");
  1413. path.setAttribute("marker-end","url(#arrow2)");
  1414. }
  1415. else{
  1416. path.setAttribute("stroke",GooFlow.prototype.color.line||"#3892D3");
  1417. path.setAttribute("marker-end","url(#arrow1)");
  1418. }
  1419. poly.appendChild(hi);
  1420. poly.appendChild(path);
  1421. var text=document.createElementNS("http://www.w3.org/2000/svg","text");
  1422. text.setAttribute("fill",GooFlow.prototype.color.font||"#333");
  1423. poly.appendChild(text);
  1424. var x=(m2[0]+m1[0])/2;
  1425. var y=(m2[1]+m1[1])/2;
  1426. text.setAttribute("text-anchor","middle");
  1427. text.setAttribute("x",x);
  1428. text.setAttribute("y",y);
  1429. text.style.cursor="text";
  1430. poly.style.cursor="pointer";
  1431. }
  1432. else{
  1433. poly=document.createElement("v:Polyline");
  1434. if(id!="") poly.id=id;
  1435. poly.filled="false";
  1436. strPath=sp[0]+","+sp[1];
  1437. if(m1[0]!=sp[0]||m1[1]!=sp[1])
  1438. strPath+=" "+m1[0]+","+m1[1];
  1439. if(m2[0]!=ep[0]||m2[1]!=ep[1])
  1440. strPath+=" "+m2[0]+","+m2[1];
  1441. strPath+=" "+ep[0]+","+ep[1];
  1442. poly.points.value=strPath;
  1443. poly.setAttribute("fromTo",sp[0]+","+sp[1]+","+ep[0]+","+ep[1]);
  1444. poly.strokeWeight="1.2";
  1445. poly.stroke.EndArrow="Block";
  1446. var text=document.createElement("div");
  1447. //text.innerHTML=id;
  1448. poly.appendChild(text);
  1449. var x=(m2[0]-m1[0])/2;
  1450. var y=(m2[1]-m1[1])/2;
  1451. if(x<0) x=x*-1;
  1452. if(y<0) y=y*-1;
  1453. text.style.left=x+"px";
  1454. text.style.top=y-4+"px";
  1455. poly.style.cursor="pointer";
  1456. if(mark) poly.strokeColor=GooFlow.prototype.color.mark||"#ff3300";
  1457. else poly.strokeColor=GooFlow.prototype.color.line||"#3892D3";
  1458. }
  1459. return poly;
  1460. },
  1461. //计算两个结点间要连直线的话,连线的开始坐标和结束坐标
  1462. calcStartEnd:function(n1,n2){
  1463. var X_1,Y_1,X_2,Y_2;
  1464. //X判断:
  1465. var x11=n1.left,x12=n1.left+n1.width,x21=n2.left,x22=n2.left+n2.width;
  1466. //结点2在结点1左边
  1467. if(x11>=x22){
  1468. X_1=x11;X_2=x22;
  1469. }
  1470. //结点2在结点1右边
  1471. else if(x12<=x21){
  1472. X_1=x12;X_2=x21;
  1473. }
  1474. //结点2在结点1水平部分重合
  1475. else if(x11<=x21&&x12>=x21&&x12<=x22){
  1476. X_1=(x12+x21)/2;X_2=X_1;
  1477. }
  1478. else if(x11>=x21&&x12<=x22){
  1479. X_1=(x11+x12)/2;X_2=X_1;
  1480. }
  1481. else if(x21>=x11&&x22<=x12){
  1482. X_1=(x21+x22)/2;X_2=X_1;
  1483. }
  1484. else if(x11<=x22&&x12>=x22){
  1485. X_1=(x11+x22)/2;X_2=X_1;
  1486. }
  1487. //Y判断:
  1488. var y11=n1.top,y12=n1.top+n1.height,y21=n2.top,y22=n2.top+n2.height;
  1489. //结点2在结点1上边
  1490. if(y11>=y22){
  1491. Y_1=y11;Y_2=y22;
  1492. }
  1493. //结点2在结点1下边
  1494. else if(y12<=y21){
  1495. Y_1=y12;Y_2=y21;
  1496. }
  1497. //结点2在结点1垂直部分重合
  1498. else if(y11<=y21&&y12>=y21&&y12<=y22){
  1499. Y_1=(y12+y21)/2;Y_2=Y_1;
  1500. }
  1501. else if(y11>=y21&&y12<=y22){
  1502. Y_1=(y11+y12)/2;Y_2=Y_1;
  1503. }
  1504. else if(y21>=y11&&y22<=y12){
  1505. Y_1=(y21+y22)/2;Y_2=Y_1;
  1506. }
  1507. else if(y11<=y22&&y12>=y22){
  1508. Y_1=(y11+y22)/2;Y_2=Y_1;
  1509. }
  1510. return {"start":[X_1,Y_1],"end":[X_2,Y_2]};
  1511. },
  1512. //计算两个结点间要连折线的话,连线的所有坐标
  1513. calcPolyPoints:function(n1,n2,type,M){
  1514. //开始/结束两个结点的中心
  1515. var SP={x:n1.left+n1.width/2,y:n1.top+n1.height/2};
  1516. var EP={x:n2.left+n2.width/2,y:n2.top+n2.height/2};
  1517. var sp=[],m1=[],m2=[],ep=[];
  1518. //如果是允许中段可左右移动的折线,则参数M为可移动中段线的X坐标
  1519. //粗略计算起始点
  1520. sp=[SP.x,SP.y];
  1521. ep=[EP.x,EP.y];
  1522. if(type=="lr"){
  1523. //粗略计算2个中点
  1524. m1=[M,SP.y];
  1525. m2=[M,EP.y];
  1526. //再具体分析修改开始点和中点1
  1527. if(m1[0]>n1.left&&m1[0]<n1.left+n1.width){
  1528. m1[1]=(SP.y>EP.y? n1.top:n1.top+n1.height);
  1529. sp[0]=m1[0];sp[1]=m1[1];
  1530. }
  1531. else{
  1532. sp[0]=(m1[0]<n1.left? n1.left:n1.left+n1.width)
  1533. }
  1534. //再具体分析中点2和结束点
  1535. if(m2[0]>n2.left&&m2[0]<n2.left+n2.width){
  1536. m2[1]=(SP.y>EP.y? n2.top+n2.height:n2.top);
  1537. ep[0]=m2[0];ep[1]=m2[1];
  1538. }
  1539. else{
  1540. ep[0]=(m2[0]<n2.left? n2.left:n2.left+n2.width)
  1541. }
  1542. }
  1543. //如果是允许中段可上下移动的折线,则参数M为可移动中段线的Y坐标
  1544. else if(type=="tb"){
  1545. //粗略计算2个中点
  1546. m1=[SP.x,M];
  1547. m2=[EP.x,M];
  1548. //再具体分析修改开始点和中点1
  1549. if(m1[1]>n1.top&&m1[1]<n1.top+n1.height){
  1550. m1[0]=(SP.x>EP.x? n1.left:n1.left+n1.width);
  1551. sp[0]=m1[0];sp[1]=m1[1];
  1552. }
  1553. else{
  1554. sp[1]=(m1[1]<n1.top? n1.top:n1.top+n1.height)
  1555. }
  1556. //再具体分析中点2和结束点
  1557. if(m2[1]>n2.top&&m2[1]<n2.top+n2.height){
  1558. m2[0]=(SP.x>EP.x? n2.left+n2.width:n2.left);
  1559. ep[0]=m2[0];ep[1]=m2[1];
  1560. }
  1561. else{
  1562. ep[1]=(m2[1]<n2.top? n2.top:n2.top+n2.height);
  1563. }
  1564. }
  1565. return {start:sp,m1:m1,m2:m2,end:ep};
  1566. },
  1567. //初始化折线中段的X/Y坐标,mType='rb'时为X坐标,mType='tb'时为Y坐标
  1568. getMValue:function(n1,n2,mType){
  1569. if(mType=="lr"){
  1570. return (n1.left+n1.width/2+n2.left+n2.width/2)/2;
  1571. }
  1572. else if(mType=="tb"){
  1573. return (n1.top+n1.height/2+n2.top+n2.height/2)/2;
  1574. }
  1575. },
  1576. //原lineData已经设定好的情况下,只在绘图工作区画一条线的页面元素
  1577. addLineDom:function(id,lineData){
  1578. var n1=this.$nodeData[lineData.from],n2=this.$nodeData[lineData.to];//获取开始/结束结点的数据
  1579. if(!n1||!n2) return;
  1580. //开始计算线端点坐标
  1581. var res;
  1582. if(lineData.type&&lineData.type!="sl")
  1583. res=GooFlow.prototype.calcPolyPoints(n1,n2,lineData.type,lineData.M);
  1584. else
  1585. res=GooFlow.prototype.calcStartEnd(n1,n2);
  1586. if(!res) return;
  1587. if(lineData.type=="sl")
  1588. this.$lineDom[id]=GooFlow.prototype.drawLine(id,res.start,res.end,lineData.marked);
  1589. else
  1590. this.$lineDom[id]=GooFlow.prototype.drawPoly(id,res.start,res.m1,res.m2,res.end,lineData.marked);
  1591. this.$draw.appendChild(this.$lineDom[id]);
  1592. if(GooFlow.prototype.useSVG==""){
  1593. this.$lineDom[id].childNodes[1].innerHTML=lineData.name;
  1594. if(lineData.type!="sl"){
  1595. var Min=(res.start[0]>res.end[0]? res.end[0]:res.start[0]);
  1596. if(Min>res.m2[0]) Min=res.m2[0];
  1597. if(Min>res.m1[0]) Min=res.m1[0];
  1598. this.$lineDom[id].childNodes[1].style.left = (res.m2[0]+res.m1[0])/2-Min-this.$lineDom[id].childNodes[1].offsetWidth/2+4;
  1599. Min=(res.start[1]>res.end[1]? res.end[1]:res.start[1]);
  1600. if(Min>res.m2[1]) Min=res.m2[1];
  1601. if(Min>res.m1[1]) Min=res.m1[1];
  1602. this.$lineDom[id].childNodes[1].style.top = (res.m2[1]+res.m1[1])/2-Min-this.$lineDom[id].childNodes[1].offsetHeight/2;
  1603. }else
  1604. this.$lineDom[id].childNodes[1].style.left=
  1605. ((res.end[0]-res.start[0])*(res.end[0]>res.start[0]? 1:-1)-this.$lineDom[id].childNodes[1].offsetWidth)/2+4;
  1606. }
  1607. else this.$lineDom[id].childNodes[2].textContent=lineData.name;
  1608. },
  1609. //增加一条线
  1610. addLine:function(id,json){
  1611. if(this.onItemAdd!=null&&!this.onItemAdd(id,"line",json))return;
  1612. if(this.$undoStack&&this.$editable){
  1613. this.pushOper("delLine",[id]);
  1614. }
  1615. if(json.from==json.to) return;
  1616. var n1=this.$nodeData[json.from],n2=this.$nodeData[json.to];//获取开始/结束结点的数据
  1617. if(!n1||!n2) return;
  1618. //避免两个节点间不能有一条以上同向接连线
  1619. for(var k in this.$lineData){
  1620. if((json.from==this.$lineData[k].from&&json.to==this.$lineData[k].to))
  1621. return;
  1622. }
  1623. //设置$lineData[id]
  1624. this.$lineData[id]={};
  1625. if(json.type){
  1626. this.$lineData[id].type=json.type;
  1627. this.$lineData[id].M=json.M;
  1628. }
  1629. else this.$lineData[id].type="sl";//默认为直线
  1630. this.$lineData[id].from=json.from;
  1631. this.$lineData[id].to=json.to;
  1632. this.$lineData[id].name=json.name;
  1633. if(json.marked) this.$lineData[id].marked=json.marked;
  1634. else this.$lineData[id].marked=false;
  1635. //设置$lineData[id]完毕
  1636. this.addLineDom(id,this.$lineData[id]);
  1637. ++this.$lineCount;
  1638. if(this.$editable){
  1639. this.$lineData[id].alt=true;
  1640. if(this.$deletedItem[id]) delete this.$deletedItem[id];//在回退删除操作时,去掉该元素的删除记录
  1641. }
  1642. },
  1643. //重构所有连向某个结点的线的显示,传参结构为$nodeData数组的一个单元结构
  1644. resetLines:function(id,node){
  1645. for(var i in this.$lineData){
  1646. var other=null;//获取结束/开始结点的数据
  1647. var res;
  1648. if(this.$lineData[i].from==id){//找结束点
  1649. other=this.$nodeData[this.$lineData[i].to]||null;
  1650. if(other==null) continue;
  1651. if(this.$lineData[i].type=="sl")
  1652. res=GooFlow.prototype.calcStartEnd(node,other);
  1653. else
  1654. res=GooFlow.prototype.calcPolyPoints(node,other,this.$lineData[i].type,this.$lineData[i].M)
  1655. if(!res) break;
  1656. }
  1657. else if(this.$lineData[i].to==id){//找开始点
  1658. other=this.$nodeData[this.$lineData[i].from]||null;
  1659. if(other==null) continue;
  1660. if(this.$lineData[i].type=="sl")
  1661. res=GooFlow.prototype.calcStartEnd(other,node);
  1662. else
  1663. res=GooFlow.prototype.calcPolyPoints(other,node,this.$lineData[i].type,this.$lineData[i].M);
  1664. if(!res) break;
  1665. }
  1666. if(other==null) continue;
  1667. this.$draw.removeChild(this.$lineDom[i]);
  1668. if(this.$lineData[i].type=="sl"){
  1669. this.$lineDom[i]=GooFlow.prototype.drawLine(i,res.start,res.end,this.$lineData[i].marked);
  1670. }
  1671. else{
  1672. this.$lineDom[i]=GooFlow.prototype.drawPoly(i,res.start,res.m1,res.m2,res.end,this.$lineData[i].marked);
  1673. }
  1674. this.$draw.appendChild(this.$lineDom[i]);
  1675. if(GooFlow.prototype.useSVG==""){
  1676. this.$lineDom[i].childNodes[1].innerHTML=this.$lineData[i].name;
  1677. if(this.$lineData[i].type!="sl"){
  1678. var Min=(res.start[0]>res.end[0]? res.end[0]:res.start[0]);
  1679. if(Min>res.m2[0]) Min=res.m2[0];
  1680. if(Min>res.m1[0]) Min=res.m1[0];
  1681. this.$lineDom[i].childNodes[1].style.left = (res.m2[0]+res.m1[0])/2-Min-this.$lineDom[i].childNodes[1].offsetWidth/2+4;
  1682. Min=(res.start[1]>res.end[1]? res.end[1]:res.start[1]);
  1683. if(Min>res.m2[1]) Min=res.m2[1];
  1684. if(Min>res.m1[1]) Min=res.m1[1];
  1685. this.$lineDom[i].childNodes[1].style.top = (res.m2[1]+res.m1[1])/2-Min-this.$lineDom[i].childNodes[1].offsetHeight/2-4;
  1686. }else
  1687. this.$lineDom[i].childNodes[1].style.left=
  1688. ((res.end[0]-res.start[0])*(res.end[0]>res.start[0]? 1:-1)-this.$lineDom[i].childNodes[1].offsetWidth)/2+4;
  1689. }
  1690. else this.$lineDom[i].childNodes[2].textContent=this.$lineData[i].name;
  1691. }
  1692. },
  1693. //重新设置连线的样式 newType= "sl":直线, "lr":中段可左右移动型折线, "tb":中段可上下移动型折线
  1694. setLineType:function(id,newType,M){
  1695. if(!newType||newType==null||newType==""||newType==this.$lineData[id].type) return false;
  1696. if(this.onLineSetType!=null&&!this.onLineSetType(id,newType)) return;
  1697. if(this.$undoStack){
  1698. var paras=[id,this.$lineData[id].type,this.$lineData[id].M];
  1699. this.pushOper("setLineType",paras);
  1700. }
  1701. var from=this.$lineData[id].from;
  1702. var to=this.$lineData[id].to;
  1703. this.$lineData[id].type=newType;
  1704. var res;
  1705. //如果是变成折线
  1706. if(newType!="sl"){
  1707. var res=GooFlow.prototype.calcPolyPoints(this.$nodeData[from],this.$nodeData[to],this.$lineData[id].type,this.$lineData[id].M);
  1708. if(M){
  1709. this.setLineM(id,M,true);
  1710. }else{
  1711. this.setLineM(id,this.getMValue(this.$nodeData[from],this.$nodeData[to],newType),true);
  1712. }
  1713. }
  1714. //如果是变回直线
  1715. else{
  1716. delete this.$lineData[id].M;
  1717. this.$lineMove.hide().removeData("type").removeData("tid");
  1718. res=GooFlow.prototype.calcStartEnd(this.$nodeData[from],this.$nodeData[to]);
  1719. if(!res) return;
  1720. this.$draw.removeChild(this.$lineDom[id]);
  1721. this.$lineDom[id]=GooFlow.prototype.drawLine(id,res.start,res.end,this.$lineData[id].marked||this.$focus==id);
  1722. this.$draw.appendChild(this.$lineDom[id]);
  1723. if(GooFlow.prototype.useSVG==""){
  1724. this.$lineDom[id].childNodes[1].innerHTML=this.$lineData[id].name;
  1725. this.$lineDom[id].childNodes[1].style.left=
  1726. ((res.end[0]-res.start[0])*(res.end[0]>res.start[0]? 1:-1)-this.$lineDom[id].childNodes[1].offsetWidth)/2+4;
  1727. }
  1728. else
  1729. this.$lineDom[id].childNodes[2].textContent=this.$lineData[id].name;
  1730. }
  1731. if(this.$focus==id){
  1732. this.focusItem(id);
  1733. }
  1734. if(this.$editable){
  1735. this.$lineData[id].alt=true;
  1736. }
  1737. },
  1738. //设置折线中段的X坐标值(可左右移动时)或Y坐标值(可上下移动时)
  1739. setLineM:function(id,M,noStack){
  1740. if(!this.$lineData[id]||M<0||!this.$lineData[id].type||this.$lineData[id].type=="sl") return false;
  1741. if(this.onLineMove!=null&&!this.onLineMove(id,M)) return false;
  1742. if(this.$undoStack&&!noStack){
  1743. var paras=[id,this.$lineData[id].M];
  1744. this.pushOper("setLineM",paras);
  1745. }
  1746. var from=this.$lineData[id].from;
  1747. var to=this.$lineData[id].to;
  1748. this.$lineData[id].M=M;
  1749. var ps=GooFlow.prototype.calcPolyPoints(this.$nodeData[from],this.$nodeData[to],this.$lineData[id].type,this.$lineData[id].M);
  1750. this.$draw.removeChild(this.$lineDom[id]);
  1751. this.$lineDom[id]=GooFlow.prototype.drawPoly(id,ps.start,ps.m1,ps.m2,ps.end,this.$lineData[id].marked||this.$focus==id);
  1752. this.$draw.appendChild(this.$lineDom[id]);
  1753. if(GooFlow.prototype.useSVG==""){
  1754. this.$lineDom[id].childNodes[1].innerHTML=this.$lineData[id].name;
  1755. var Min=(ps.start[0]>ps.end[0]? ps.end[0]:ps.start[0]);
  1756. if(Min>ps.m2[0]) Min=ps.m2[0];
  1757. if(Min>ps.m1[0]) Min=ps.m1[0];
  1758. this.$lineDom[id].childNodes[1].style.left = (ps.m2[0]+ps.m1[0])/2-Min-this.$lineDom[id].childNodes[1].offsetWidth/2+4;
  1759. Min=(ps.start[1]>ps.end[1]? ps.end[1]:ps.start[1]);
  1760. if(Min>ps.m2[1]) Min=ps.m2[1];
  1761. if(Min>ps.m1[1]) Min=ps.m1[1];
  1762. this.$lineDom[id].childNodes[1].style.top = (ps.m2[1]+ps.m1[1])/2-Min-this.$lineDom[id].childNodes[1].offsetHeight/2-4;
  1763. }
  1764. else this.$lineDom[id].childNodes[2].textContent=this.$lineData[id].name;
  1765. if(this.$editable){
  1766. this.$lineData[id].alt=true;
  1767. }
  1768. },
  1769. //删除转换线
  1770. delLine:function(id){
  1771. if(!this.$lineData[id]) return;
  1772. if(this.onItemDel!=null&&!this.onItemDel(id,"node")) return;
  1773. if(this.$undoStack){
  1774. var paras=[id,this.$lineData[id]];
  1775. this.pushOper("addLine",paras);
  1776. }
  1777. this.$draw.removeChild(this.$lineDom[id]);
  1778. delete this.$lineData[id];
  1779. delete this.$lineDom[id];
  1780. if(this.$focus==id) this.$focus="";
  1781. --this.$lineCount;
  1782. if(this.$editable){
  1783. //在回退新增操作时,如果节点ID以this.$id+"_line_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中
  1784. if(id.indexOf(this.$id+"_line_")<0)
  1785. this.$deletedItem[id]="line";
  1786. this.$mpFrom.hide().removeData("p");
  1787. this.$mpTo.hide().removeData("p");
  1788. }
  1789. this.$lineOper.hide().removeData("tid");
  1790. },
  1791. //变更连线两个端点所连的结点
  1792. //参数:要变更端点的连线ID,新的开始结点ID、新的结束结点ID;如果开始/结束结点ID是传入null或者"",则表示原端点不变
  1793. moveLinePoints:function(lineId, newStart, newEnd, noStack){
  1794. if(newStart==newEnd) return;
  1795. if(!lineId||!this.$lineData[lineId]) return;
  1796. if(newStart==null||newStart=="")
  1797. newStart=this.$lineData[lineId].from;
  1798. if(newEnd==null||newEnd=="")
  1799. newEnd=this.$lineData[lineId].to;
  1800. //避免两个节点间不能有一条以上同向接连线
  1801. for(var k in this.$lineData){
  1802. if((newStart==this.$lineData[k].from&&newEnd==this.$lineData[k].to))
  1803. return;
  1804. }
  1805. if(this.onLinePointMove!=null&&!this.onLinePointMove(id,newStart,newEnd)) return;
  1806. if(this.$undoStack&&!noStack){
  1807. var paras=[lineId,this.$lineData[lineId].from,this.$lineData[lineId].to];
  1808. this.pushOper("moveLinePoints",paras);
  1809. }
  1810. if(newStart!=null&&newStart!=""){
  1811. this.$lineData[lineId].from=newStart;
  1812. }
  1813. if(newEnd!=null&&newEnd!=""){
  1814. this.$lineData[lineId].to=newEnd;
  1815. }
  1816. //重建转换线
  1817. this.$draw.removeChild(this.$lineDom[lineId]);
  1818. this.addLineDom(lineId,this.$lineData[lineId]);
  1819. if(this.$editable){
  1820. this.$lineData[lineId].alt=true;
  1821. }
  1822. },
  1823. //用颜色标注/取消标注一个结点或转换线,常用于显示重点或流程的进度。
  1824. //这是一个在编辑模式中无用,但是在纯浏览模式中非常有用的方法,实际运用中可用于跟踪流程的进度。
  1825. markItem:function(id,type,mark){
  1826. if(type=="node"){
  1827. if(!this.$nodeData[id]) return;
  1828. if(this.onItemMark!=null&&!this.onItemMark(id,"node",mark)) return;
  1829. this.$nodeData[id].marked=mark||false;
  1830. if(mark){
  1831. this.$nodeDom[id].addClass("item_mark");
  1832. jq.css("border-color",GooFlow.prototype.color.mark);
  1833. }
  1834. else{
  1835. this.$nodeDom[id].removeClass("item_mark");
  1836. if(id!=this.$focus) jq.css("border-color","transparent");
  1837. }
  1838. }else if(type=="line"){
  1839. if(!this.$lineData[id]) return;
  1840. if(this.onItemMark!=null&&!this.onItemMark(id,"line",mark)) return;
  1841. this.$lineData[id].marked=mark||false;
  1842. if(GooFlow.prototype.useSVG!=""){
  1843. if(mark){
  1844. this.$nodeDom[id].childNodes[1].setAttribute("stroke",GooFlow.prototype.color.mark||"#ff3300");
  1845. this.$nodeDom[id].childNodes[1].setAttribute("marker-end","url(#arrow2)");
  1846. }else{
  1847. this.$nodeDom[id].childNodes[1].setAttribute("stroke",GooFlow.prototype.color.line||"#3892D3");
  1848. this.$nodeDom[id].childNodes[1].setAttribute("marker-end","url(#arrow1)");
  1849. }
  1850. }else{
  1851. if(mark) this.$nodeDom[id].strokeColor=GooFlow.prototype.color.mark||"#ff3300";
  1852. else this.$nodeDom[id].strokeColor=GooFlow.prototype.color.line||"#3892D3"
  1853. }
  1854. }
  1855. if(this.$undoStatck){
  1856. var paras=[id,type,!mark];
  1857. this.pushOper("markItem",paras);
  1858. }
  1859. },
  1860. ////////////////////////以下为区域分组块操作
  1861. moveArea:function(id,left,top){
  1862. if(!this.$areaData[id]) return;
  1863. if(this.onItemMove!=null&&!this.onItemMove(id,"area",left,top)) return;
  1864. if(this.$undoStack){
  1865. var paras=[id,this.$areaData[id].left,this.$areaData[id].top];
  1866. this.pushOper("moveNode",paras);
  1867. }
  1868. if(left<0) left=0;
  1869. if(top<0) top=0;
  1870. $("#"+id).css({left:left+"px",top:top+"px"});
  1871. this.$areaData[id].left=left;
  1872. this.$areaData[id].top=top;
  1873. if(this.$editable){
  1874. this.$areaData[id].alt=true;
  1875. }
  1876. },
  1877. //删除区域分组
  1878. delArea:function(id){
  1879. if(!this.$areaData[id]) return;
  1880. if(this.$undoStack){
  1881. var paras=[id,this.$areaData[id]];
  1882. this.pushOper("addArea",paras);
  1883. }
  1884. if(this.onItemDel!=null&&!this.onItemDel(id,"node")) return;
  1885. delete this.$areaData[id];
  1886. this.$areaDom[id].remove();
  1887. delete this.$areaDom[id];
  1888. --this.$areaCount;
  1889. if(this.$editable){
  1890. //在回退新增操作时,如果节点ID以this.$id+"_area_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中
  1891. if(id.indexOf(this.$id+"_area_")<0)
  1892. this.$deletedItem[id]="area";
  1893. }
  1894. },
  1895. //设置区域分组的颜色
  1896. setAreaColor:function(id,color){
  1897. if(!this.$areaData[id]) return;
  1898. if(this.$undoStack){
  1899. var paras=[id,this.$areaData[id].color];
  1900. this.pushOper("setAreaColor",paras);
  1901. }
  1902. if(color=="red"||color=="yellow"||color=="blue"||color=="green"){
  1903. this.$areaDom[id].removeClass("area_"+this.$areaData[id].color).addClass("area_"+color);
  1904. this.$areaData[id].color=color;
  1905. }
  1906. if(this.$editable){
  1907. this.$areaData[id].alt=true;
  1908. }
  1909. },
  1910. //设置区域分块的尺寸
  1911. resizeArea:function(id,width,height){
  1912. if(!this.$areaData[id]) return;
  1913. if(this.onItemResize!=null&&!this.onItemResize(id,"area",width,height)) return;
  1914. if(this.$undoStack){
  1915. var paras=[id,this.$areaData[id].width,this.$areaData[id].height];
  1916. this.pushOper("resizeArea",paras);
  1917. }
  1918. var hack=0;
  1919. if(navigator.userAgent.indexOf("8.0")!=-1) hack=2;
  1920. this.$areaDom[id].children(".bg").css({width:width-2+"px",height:height-2+"px"});
  1921. width=this.$areaDom[id].outerWidth();
  1922. height=this.$areaDom[id].outerHeight();
  1923. this.$areaDom[id].children("bg").css({width:width-2+"px",height:height-2+"px"});
  1924. this.$areaData[id].width=width;
  1925. this.$areaData[id].height=height;
  1926. if(this.$editable){
  1927. this.$areaData[id].alt=true;
  1928. }
  1929. },
  1930. addArea:function(id,json){
  1931. if(this.onItemAdd!=null&&!this.onItemAdd(id,"area",json))return;
  1932. if(this.$undoStack&&this.$editable){
  1933. this.pushOper("delArea",[id]);
  1934. }
  1935. this.$areaDom[id]=$("<div id='"+id+"' class='GooFlow_area area_"+json.color+"' style='top:"+json.top+"px;left:"+json.left+"px'><div class='bg' style='width:"+(json.width-2)+"px;height:"+(json.height-2)+"px'></div>"
  1936. +"<label>"+json.name+"</label><i></i><div><div class='rs_bottom'></div><div class='rs_right'></div><div class='rs_rb'></div><div class='rs_close'></div></div></div>");
  1937. this.$areaData[id]=json;
  1938. this.$group.append(this.$areaDom[id]);
  1939. if(this.$nowType!="group") this.$areaDom[id].children("div:eq(1)").css("display","none");
  1940. ++this.$areaCount;
  1941. if(this.$editable){
  1942. this.$areaData[id].alt=true;
  1943. if(this.$deletedItem[id]) delete this.$deletedItem[id];//在回退删除操作时,去掉该元素的删除记录
  1944. }
  1945. },
  1946. //重构整个流程图设计器的宽高
  1947. reinitSize:function(width,height){
  1948. var w=(width||800)-2;
  1949. var h=(height||500)-2;
  1950. this.$bgDiv.css({height:h+"px",width:w+"px"});
  1951. var headHeight=0,hack=10;
  1952. if(this.$head!=null){
  1953. headHeight=24;
  1954. hack=7;
  1955. }
  1956. if(this.$tool!=null){
  1957. this.$tool.css({height:h-headHeight-hack+"px"});
  1958. }
  1959. w-=39;
  1960. h=h-headHeight-(this.$head!=null? 5:8);
  1961. this.$workArea.parent().css({height:h+"px",width:w+"px"});
  1962. this.$workArea.css({height:h*3+"px",width:w*3+"px"});
  1963. if(GooFlow.prototype.useSVG==""){
  1964. this.$draw.coordsize = w*3+","+h*3;
  1965. }
  1966. this.$draw.style.width = w*3 + "px";
  1967. this.$draw.style.height = +h*3 + "px";
  1968. if(this.$group==null){
  1969. this.$group.css({height:h*3+"px",width:w*3+"px"});
  1970. }
  1971. }
  1972. }
  1973. GooFlow.prototype.color={};
  1974. //将此类的构造函数加入至JQUERY对象中
  1975. jQuery.extend({
  1976. createGooFlow:function(bgDiv,property){
  1977. return new GooFlow(bgDiv,property);
  1978. }
  1979. });