jquery.flowchart
Version:
JQuery UI plugin that allows you to draw a flow chart.
1 lines • 18.6 kB
JavaScript
$(function(){$.widget("flowchart.flowchart",{options:{canUserEditLinks:!0,canUserMoveOperators:!0,data:{},distanceFromArrow:3,defaultOperatorClass:"flowchart-default-operator",defaultLinkColor:"#3366ff",defaultSelectedLinkColor:"black",linkWidth:10,grid:20,multipleLinksOnOutput:!1,multipleLinksOnInput:!1,linkVerticalDecal:0,onOperatorSelect:function(t){return!0},onOperatorUnselect:function(){return!0},onLinkSelect:function(t){return!0},onLinkUnselect:function(){return!0},onOperatorCreate:function(t,e,o){return!0},onLinkCreate:function(t,e){return!0},onOperatorDelete:function(t){return!0},onLinkDelete:function(t,e){return!0},onOperatorMoved:function(t,e){},onAfterChange:function(t){}},data:null,objs:null,maskNum:0,linkNum:0,operatorNum:0,lastOutputConnectorClicked:null,selectedOperatorId:null,selectedLinkId:null,positionRatio:1,globalId:null,_create:function(){"undefined"==typeof document.__flowchartNumber?document.__flowchartNumber=0:document.__flowchartNumber++,this.globalId=document.__flowchartNumber,this._unitVariables(),this.element.addClass("flowchart-container"),this.objs.layers.links=$('<svg class="flowchart-links-layer"></svg>'),this.objs.layers.links.appendTo(this.element),this.objs.layers.operators=$('<div class="flowchart-operators-layer unselectable"></div>'),this.objs.layers.operators.appendTo(this.element),this.objs.layers.temporaryLink=$('<svg class="flowchart-temporary-link-layer"></svg>'),this.objs.layers.temporaryLink.appendTo(this.element);var t=document.createElementNS("http://www.w3.org/2000/svg","line");t.setAttribute("x1","0"),t.setAttribute("y1","0"),t.setAttribute("x2","0"),t.setAttribute("y2","0"),t.setAttribute("stroke-dasharray","6,6"),t.setAttribute("stroke-width","4"),t.setAttribute("stroke","black"),t.setAttribute("fill","none"),this.objs.layers.temporaryLink[0].appendChild(t),this.objs.temporaryLink=t,this._initEvents(),"undefined"!=typeof this.options.data&&this.setData(this.options.data)},_unitVariables:function(){this.data={operators:{},links:{}},this.objs={layers:{operators:null,temporaryLink:null,links:null},linksContext:null,temporaryLink:null}},_initEvents:function(){var t=this;this.element.mousemove(function(e){var o=$(this),r=o.offset();t._mousemove((e.pageX-r.left)/t.positionRatio,(e.pageY-r.top)/t.positionRatio,e)}),this.element.click(function(e){var o=$(this),r=o.offset();t._click((e.pageX-r.left)/t.positionRatio,(e.pageY-r.top)/t.positionRatio,e)}),this.objs.layers.operators.on("mousedown touchstart",".flowchart-operator",function(t){t.stopImmediatePropagation()}),this.objs.layers.operators.on("click",".flowchart-operator",function(e){0==$(e.target).closest(".flowchart-operator-connector").length&&t.selectOperator($(this).data("operator_id"))}),this.objs.layers.operators.on("click",".flowchart-operator-connector",function(){var e=$(this);t.options.canUserEditLinks&&t._connectorClicked(e.closest(".flowchart-operator").data("operator_id"),e.data("connector"),e.data("sub_connector"),e.closest(".flowchart-operator-connector-set").data("connector_type"))}),this.objs.layers.links.on("mousedown touchstart",".flowchart-link",function(t){t.stopImmediatePropagation()}),this.objs.layers.links.on("mouseover",".flowchart-link",function(){t._connecterMouseOver($(this).data("link_id"))}),this.objs.layers.links.on("mouseout",".flowchart-link",function(){t._connecterMouseOut($(this).data("link_id"))}),this.objs.layers.links.on("click",".flowchart-link",function(){t.selectLink($(this).data("link_id"))})},setData:function(t){this._clearOperatorsLayer(),this.data.operatorTypes={},"undefined"!=typeof t.operatorTypes&&(this.data.operatorTypes=t.operatorTypes),this.data.operators={};for(var e in t.operators)this.createOperator(e,t.operators[e]);for(var o in t.links)this.createLink(o,t.links[o]);this.redrawLinksLayer()},addLink:function(t){for(;"undefined"!=typeof this.data.links[this.linkNum];)this.linkNum++;return this.createLink(this.linkNum,t),this.linkNum},createLink:function(t,e){var o=$.extend(!0,{},e);if(this.options.onLinkCreate(t,o)){var r=this._getSubConnectors(o),n=r[0],i=r[1],a=this.options.multipleLinksOnOutput,s=this.options.multipleLinksOnInput;if(!a||!s)for(var l in this.data.links){var p=this.data.links[l],c=this._getSubConnectors(p),u=c[0],h=c[1];a||p.fromOperator!=o.fromOperator||p.fromConnector!=o.fromConnector||u!=n?s||p.toOperator!=o.toOperator||p.toConnector!=o.toConnector||h!=i||this.deleteLink(l):this.deleteLink(l)}this._autoCreateSubConnector(o.fromOperator,o.fromConnector,"outputs",n),this._autoCreateSubConnector(o.toOperator,o.toConnector,"inputs",i),this.data.links[t]=o,this._drawLink(t),this.options.onAfterChange("link_create")}},_autoCreateSubConnector:function(t,e,o,r){var n=this.data.operators[t].properties[o][e];if(n.multiple)for(var i=this.data.operators[t].internal.els,a=this.data.operators[t].internal.els.connectors[e].length,s=a;r+2>s;s++)this._createSubConnector(e,n,i)},redrawLinksLayer:function(){this._clearLinksLayer();for(var t in this.data.links)this._drawLink(t)},_clearLinksLayer:function(){this.objs.layers.links.empty(),this.objs.layers.operators.find(".flowchart-operator-connector-small-arrow").css("border-left-color","transparent")},_clearOperatorsLayer:function(){this.objs.layers.operators.empty()},getConnectorPosition:function(t,e,o){var r=this.data.operators[t],n=r.internal.els.connectorArrows[e][o],i=n.offset(),a=this.element.offset(),s=(i.left-a.left)/this.positionRatio,l=parseInt(n.css("border-top-width")),p=(i.top-a.top-1)/this.positionRatio+parseInt(n.css("border-left-width"));return{x:s,width:l,y:p}},getLinkMainColor:function(t){var e=this.options.defaultLinkColor,o=this.data.links[t];return"undefined"!=typeof o.color&&(e=o.color),e},setLinkMainColor:function(t,e){this.data.links[t].color=e,this.options.onAfterChange("link_change_main_color")},_drawLink:function(t){var e=this.data.links[t];"undefined"==typeof e.internal&&(e.internal={}),e.internal.els={};var o=e.fromOperator,r=e.fromConnector,n=e.toOperator,i=e.toConnector,a=this._getSubConnectors(e),s=a[0],l=a[1],c=(this.getLinkMainColor(t),this.data.operators[o]),u=this.data.operators[n],h=c.internal.els.connectorSmallArrows[r][s],d=u.internal.els.connectorSmallArrows[i][l];e.internal.els.fromSmallConnector=h,e.internal.els.toSmallConnector=d;var f=document.createElementNS("http://www.w3.org/2000/svg","g");this.objs.layers.links[0].appendChild(f),e.internal.els.overallGroup=f;var v=document.createElementNS("http://www.w3.org/2000/svg","mask"),k="fc_mask_"+this.globalId+"_"+this.maskNum;this.maskNum++,v.setAttribute("id",k),f.appendChild(v);var m=document.createElementNS("http://www.w3.org/2000/svg","rect");m.setAttribute("x","0"),m.setAttribute("y","0"),m.setAttribute("width","100%"),m.setAttribute("height","100%"),m.setAttribute("stroke","none"),m.setAttribute("fill","white"),v.appendChild(m);var m=document.createElementNS("http://www.w3.org/2000/svg","polygon");m.setAttribute("stroke","none"),m.setAttribute("fill","black"),v.appendChild(m),e.internal.els.mask=m;var C=document.createElementNS("http://www.w3.org/2000/svg","g");C.setAttribute("class","flowchart-link"),C.setAttribute("data-link_id",t),f.appendChild(C);var m=document.createElementNS("http://www.w3.org/2000/svg","path");m.setAttribute("stroke-width",this.options.linkWidth),m.setAttribute("fill","none"),C.appendChild(m),e.internal.els.path=m;var m=document.createElementNS("http://www.w3.org/2000/svg","rect");m.setAttribute("stroke","none"),m.setAttribute("mask","url(#"+k+")"),C.appendChild(m),e.internal.els.rect=m,this._refreshLinkPositions(t),this.uncolorizeLink(t)},_getSubConnectors:function(t){var e=0;"undefined"!=typeof t.fromSubConnector&&(e=t.fromSubConnector);var o=0;return"undefined"!=typeof t.toSubConnector&&(o=t.toSubConnector),[e,o]},_refreshLinkPositions:function(t){var e=this.data.links[t],o=this._getSubConnectors(e),r=o[0],n=o[1],i=this.getConnectorPosition(e.fromOperator,e.fromConnector,r),a=this.getConnectorPosition(e.toOperator,e.toConnector,n),s=i.x,l=i.width,p=i.y,c=a.x,u=a.y;p+=this.options.linkVerticalDecal,u+=this.options.linkVerticalDecal;var h=this.options.distanceFromArrow;e.internal.els.mask.setAttribute("points",s+","+(p-l-h)+" "+(s+l+h)+","+p+" "+s+","+(p+l+h));var d=s+l+h,f=c+1,v=Math.min(100,Math.max(Math.abs(d-f)/2,Math.abs(p-u)));e.internal.els.path.setAttribute("d","M"+d+","+p+" C"+(s+l+h+v)+","+p+" "+(c-v)+","+u+" "+f+","+u),e.internal.els.rect.setAttribute("x",s),e.internal.els.rect.setAttribute("y",p-this.options.linkWidth/2),e.internal.els.rect.setAttribute("width",l+h+1),e.internal.els.rect.setAttribute("height",this.options.linkWidth)},getOperatorCompleteData:function(t){"undefined"==typeof t.internal&&(t.internal={}),this._refreshInternalProperties(t),infos=$.extend(!0,{},t.internal.properties);for(var e in infos.inputs)null==infos.inputs[e]&&delete infos.inputs[e];for(var e in infos.outputs)null==infos.outputs[e]&&delete infos.outputs[e];return"undefined"==typeof infos["class"]&&(infos["class"]=this.options.defaultOperatorClass),infos},_getOperatorFullElement:function(t){function d(t,e,o,r){var n=$('<div class="flowchart-operator-connector-set"></div>');n.data("connector_type",r),n.appendTo(o),l[t]=[],p[t]=[],u[t]=[],c[t]=n,s._createSubConnector(t,e,h)}var e=this.getOperatorCompleteData(t),o=$('<div class="flowchart-operator"></div>');o.addClass(e["class"]);var r=$('<div class="flowchart-operator-title"></div>');r.text(e.title),r.appendTo(o);var n=$('<div class="flowchart-operator-inputs-outputs"></div>');n.appendTo(o);var i=$('<div class="flowchart-operator-inputs"></div>');i.appendTo(n);var a=$('<div class="flowchart-operator-outputs"></div>');a.appendTo(n);var s=this,l={},p={},c={},u={},h={operator:o,title:r,connectorSets:c,connectors:u,connectorArrows:l,connectorSmallArrows:p};for(var f in e.inputs)d(f,e.inputs[f],i,"inputs");for(var f in e.outputs)d(f,e.outputs[f],a,"outputs");return h},_createSubConnector:function(t,e,o){var r=o.connectorSets[t],n=o.connectors[t].length,i=$('<div class="flowchart-operator-connector"></div>');i.appendTo(r),i.data("connector",t),i.data("sub_connector",n);var a=$('<div class="flowchart-operator-connector-label"></div>');a.text(e.label.replace("(:i)",n+1)),a.appendTo(i);var s=$('<div class="flowchart-operator-connector-arrow"></div>');s.appendTo(i);var l=$('<div class="flowchart-operator-connector-small-arrow"></div>');l.appendTo(i),o.connectors[t].push(i),o.connectorArrows[t].push(s),o.connectorSmallArrows[t].push(l)},getOperatorElement:function(t){var e=this._getOperatorFullElement(t);return e.operator},addOperator:function(t){for(;"undefined"!=typeof this.data.operators[this.operatorNum];)this.operatorNum++;return this.createOperator(this.operatorNum,t),this.operatorNum},createOperator:function(t,e){function i(t,o){e.top=o.top,e.left=o.left;for(var r in n.data.links){var i=n.data.links[r];(i.fromOperator==t||i.toOperator==t)&&n._refreshLinkPositions(r)}}e.internal={},this._refreshInternalProperties(e);var o=this._getOperatorFullElement(e);if(!this.options.onOperatorCreate(t,e,o))return!1;var r=this.options.grid;e.top=Math.round(e.top/r)*r,e.left=Math.round(e.left/r)*r,o.operator.appendTo(this.objs.layers.operators),o.operator.css({top:e.top,left:e.left}),o.operator.data("operator_id",t),this.data.operators[t]=e,this.data.operators[t].internal.els=o,t==this.selectedOperatorId&&this._addSelectedClass(t);var e=this.data.operators[t],n=this;if(this.options.canUserMoveOperators){var a,s;o.operator.draggable({handle:".flowchart-operator-title",start:function(t,e){if(null!=n.lastOutputConnectorClicked)return void t.preventDefault();var o=n.element.offset();a=(t.pageX-o.left)/n.positionRatio-parseInt($(t.target).css("left")),s=(t.pageY-o.top)/n.positionRatio-parseInt($(t.target).css("top"))},drag:function(t,e){var r=n.options.grid,l=n.element.offset();e.position.left=Math.round(((t.pageX-l.left)/n.positionRatio-a)/r)*r,e.position.top=Math.round(((t.pageY-l.top)/n.positionRatio-s)/r)*r,e.offset.left=Math.round(e.position.left+l.left),e.offset.top=Math.round(e.position.top+l.top),o.operator.css({left:e.position.left,top:e.position.top}),i($(this).data("operator_id"),e.position)},stop:function(t,e){n._unsetTemporaryLink();var r=$(this).data("operator_id");i(r,e.position),o.operator.css({height:"auto"}),n.options.onOperatorMoved(r,e.position),n.options.onAfterChange("operator_moved")}})}this.options.onAfterChange("operator_create")},_connectorClicked:function(t,e,o,r){if("outputs"==r){var n=new Date;n.getTime();this.lastOutputConnectorClicked={operator:t,connector:e,subConnector:o},this.objs.layers.temporaryLink.show();var a=this.getConnectorPosition(t,e,o),s=a.x+a.width,l=a.y;this.objs.temporaryLink.setAttribute("x1",s),this.objs.temporaryLink.setAttribute("y1",l),this._mousemove(s,l)}if("inputs"==r&&null!=this.lastOutputConnectorClicked){var p={fromOperator:this.lastOutputConnectorClicked.operator,fromConnector:this.lastOutputConnectorClicked.connector,fromSubConnector:this.lastOutputConnectorClicked.subConnector,toOperator:t,toConnector:e,toSubConnector:o};this.addLink(p),this._unsetTemporaryLink()}},_unsetTemporaryLink:function(){this.lastOutputConnectorClicked=null,this.objs.layers.temporaryLink.hide()},_mousemove:function(t,e,o){null!=this.lastOutputConnectorClicked&&(this.objs.temporaryLink.setAttribute("x2",t),this.objs.temporaryLink.setAttribute("y2",e))},_click:function(t,e,o){var r=$(o.target);0==r.closest(".flowchart-operator-connector").length&&this._unsetTemporaryLink(),0==r.closest(".flowchart-operator").length&&this.unselectOperator(),0==r.closest(".flowchart-link").length&&this.unselectLink()},_removeSelectedClassOperators:function(){this.objs.layers.operators.find(".flowchart-operator").removeClass("selected")},unselectOperator:function(){if(null!=this.selectedOperatorId){if(!this.options.onOperatorUnselect())return;this._removeSelectedClassOperators(),this.selectedOperatorId=null}},_addSelectedClass:function(t){this.data.operators[t].internal.els.operator.addClass("selected")},selectOperator:function(t){this.options.onOperatorSelect(t)&&(this.unselectLink(),this._removeSelectedClassOperators(),this._addSelectedClass(t),this.selectedOperatorId=t)},getSelectedOperatorId:function(){return this.selectedOperatorId},getSelectedLinkId:function(){return this.selectedLinkId},_shadeColor:function(t,e){var o=parseInt(t.slice(1),16),r=0>e?0:255,n=0>e?-1*e:e,i=o>>16,a=o>>8&255,s=255&o;return"#"+(16777216+65536*(Math.round((r-i)*n)+i)+256*(Math.round((r-a)*n)+a)+(Math.round((r-s)*n)+s)).toString(16).slice(1)},colorizeLink:function(t,e){var o=this.data.links[t];o.internal.els.path.setAttribute("stroke",e),o.internal.els.rect.setAttribute("fill",e),o.internal.els.fromSmallConnector.css("border-left-color",e),o.internal.els.toSmallConnector.css("border-left-color",e)},uncolorizeLink:function(t){this.colorizeLink(t,this.getLinkMainColor(t))},_connecterMouseOver:function(t){this.selectedLinkId!=t&&this.colorizeLink(t,this._shadeColor(this.getLinkMainColor(t),-.4))},_connecterMouseOut:function(t){this.selectedLinkId!=t&&this.uncolorizeLink(t)},unselectLink:function(){if(null!=this.selectedLinkId){if(!this.options.onLinkUnselect())return;this.uncolorizeLink(this.selectedLinkId,this.options.defaultSelectedLinkColor),this.selectedLinkId=null}},selectLink:function(t){this.unselectLink(),this.options.onLinkSelect(t)&&(this.unselectOperator(),this.selectedLinkId=t,this.colorizeLink(t,this.options.defaultSelectedLinkColor))},deleteOperator:function(t){this._deleteOperator(t,!1)},_deleteOperator:function(t,e){if(!this.options.onOperatorDelete(t,e))return!1;if(!e)for(var o in this.data.links){var r=this.data.links[o];(r.fromOperator==t||r.toOperator==t)&&this._deleteLink(o,!0)}e||t!=this.selectedOperatorId||this.unselectOperator(),this.data.operators[t].internal.els.operator.remove(),delete this.data.operators[t],this.options.onAfterChange("operator_delete")},deleteLink:function(t){this._deleteLink(t,!1)},_deleteLink:function(t,e){if(this.selectedLinkId==t&&this.unselectLink(),this.options.onLinkDelete(t,e)||e){this.colorizeLink(t,"transparent");var o=this.data.links[t],r=o.fromOperator,n=o.fromConnector,i=o.toOperator,a=o.toConnector;o.internal.els.overallGroup.remove(),delete this.data.links[t],this._cleanMultipleConnectors(r,n,"from"),this._cleanMultipleConnectors(i,a,"to"),this.options.onAfterChange("link_delete")}},_cleanMultipleConnectors:function(t,e,o){if(this.data.operators[t].properties["from"==o?"outputs":"inputs"][e].multiple){var r=-1,n=o+"Operator",i=o+"Connector",a=o+"SubConnector",s=this.data.operators[t].internal.els,l=s.connectors[e],p=l.length;for(var c in this.data.links){var u=this.data.links[c];u[n]==t&&u[i]==e&&r<u[a]&&(r=u[a])}for(var h=Math.min(p-r-2,p-1),d=0;h>d;d++)l[l.length-1].remove(),l.pop(),s.connectorArrows[e].pop(),s.connectorSmallArrows[e].pop()}},deleteSelected:function(){null!=this.selectedLinkId&&this.deleteLink(this.selectedLinkId),null!=this.selectedOperatorId&&this.deleteOperator(this.selectedOperatorId)},setPositionRatio:function(t){this.positionRatio=t},getPositionRatio:function(){return this.positionRatio},getData:function(){var t=["operators","links"],e={};e.operators=$.extend(!0,{},this.data.operators),e.links=$.extend(!0,{},this.data.links);for(var o in t){var r=t[o];for(var n in e[r])delete e[r][n].internal}return e.operatorTypes=this.data.operatorTypes,e},setOperatorTitle:function(t,e){this.data.operators[t].internal.els.title.text(e),"undefined"==typeof this.data.operators[t].properties&&(this.data.operators[t].properties={}),this.data.operators[t].properties.title=e,this._refreshInternalProperties(this.data.operators[t]),this.options.onAfterChange("operator_title_change")},getOperatorTitle:function(t){return this.data.operators[t].internal.properties.title},setOperatorData:function(t,e){var o=this.getOperatorCompleteData(e);for(var r in this.data.links){var n=this.data.links[r];(n.fromOperator==t&&"undefined"==typeof o.outputs[n.fromConnector]||n.toOperator==t&&"undefined"==typeof o.inputs[n.toConnector])&&this._deleteLink(r,!0)}this._deleteOperator(t,!0),this.createOperator(t,e),this.redrawLinksLayer(),this.options.onAfterChange("operator_data_change")},getOperatorData:function(t){var e=$.extend(!0,{},this.data.operators[t]);return delete e.internal,e},getOperatorFullProperties:function(t){if("undefined"!=typeof t.type){var e=this.data.operatorTypes[t.type],o={};return"undefined"!=typeof t.properties&&(o=t.properties),$.extend({},e,o)}return t.properties},_refreshInternalProperties:function(t){t.internal.properties=this.getOperatorFullProperties(t)}})});